From 6683976ce041a07824b7c7b1eb660057543921a1 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 17 Apr 2024 04:45:16 -0400 Subject: [PATCH 001/355] use explicit imports instead of wildcards --- python/lammps/core.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/python/lammps/core.py b/python/lammps/core.py index 3498041454..626ece0770 100644 --- a/python/lammps/core.py +++ b/python/lammps/core.py @@ -18,12 +18,21 @@ from __future__ import print_function import os import sys -from ctypes import * # lgtm [py/polluting-import] -from os.path import dirname,abspath,join +from ctypes import CDLL, POINTER, RTLD_GLOBAL, CFUNCTYPE, py_object, byref, cast, sizeof, \ + create_string_buffer, c_int, c_int32, c_int64, c_double, c_void_p, c_char_p, pythonapi +from os.path import dirname, abspath, join from inspect import getsourcefile -from .constants import * # lgtm [py/polluting-import] -from .data import * # lgtm [py/polluting-import] +from .constants import LAMMPS_AUTODETECT, LAMMPS_STRING, \ + LAMMPS_INT, LAMMPS_INT_2D, LAMMPS_DOUBLE, LAMMPS_DOUBLE_2D, LAMMPS_INT64, LAMMPS_INT64_2D, \ + LMP_STYLE_GLOBAL, LMP_STYLE_ATOM, LMP_STYLE_LOCAL, \ + LMP_TYPE_SCALAR, LMP_TYPE_VECTOR, LMP_TYPE_ARRAY, \ + LMP_SIZE_VECTOR, LMP_SIZE_ROWS, LMP_SIZE_COLS, \ + LMP_ERROR_WARNING, LMP_ERROR_ONE, LMP_ERROR_ALL, LMP_ERROR_WORLD, LMP_ERROR_UNIVERSE, \ + LMP_VAR_EQUAL, LMP_VAR_ATOM, LMP_VAR_VECTOR, LMP_VAR_STRING, \ + get_ctypes_int + +from .data import NeighList # ------------------------------------------------------------------------- From ebd77afd58e387a458c9977be77e97e1fe7737ca Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 18 Apr 2024 00:09:07 -0400 Subject: [PATCH 002/355] initialize in the constructor for the class that defines variable --- src/ML-IAP/mliap_descriptor.cpp | 1 + src/ML-IAP/mliap_descriptor_ace.cpp | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ML-IAP/mliap_descriptor.cpp b/src/ML-IAP/mliap_descriptor.cpp index 34f81c3902..b8acd6329e 100644 --- a/src/ML-IAP/mliap_descriptor.cpp +++ b/src/ML-IAP/mliap_descriptor.cpp @@ -28,6 +28,7 @@ MLIAPDescriptor::MLIAPDescriptor(LAMMPS *lmp) : cutghost(nullptr), radelem(nullptr), wjelem(nullptr) { cutmax = 0.0; + allocated_elements = 0; } /* ---------------------------------------------------------------------- */ diff --git a/src/ML-IAP/mliap_descriptor_ace.cpp b/src/ML-IAP/mliap_descriptor_ace.cpp index aa30ac3f81..d815fb744c 100644 --- a/src/ML-IAP/mliap_descriptor_ace.cpp +++ b/src/ML-IAP/mliap_descriptor_ace.cpp @@ -57,7 +57,6 @@ MLIAPDescriptorACE::MLIAPDescriptorACE(LAMMPS *_lmp, char *yacefilename) : { acemlimpl = new ACE_ML_impl; - allocated_elements = 0; //read in file with CG coefficients or c_tilde coefficients ctilde_file = yacefilename; delete acemlimpl->basis_set; From d16d3ee02b7726c634084d453eab97e5b76187bc Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 18 Apr 2024 00:19:42 -0400 Subject: [PATCH 003/355] replace wildcard imports with explicit ones --- python/lammps/numpy_wrapper.py | 8 ++++++-- python/lammps/pylammps.py | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/python/lammps/numpy_wrapper.py b/python/lammps/numpy_wrapper.py index 91042c43c8..4e33913a69 100644 --- a/python/lammps/numpy_wrapper.py +++ b/python/lammps/numpy_wrapper.py @@ -19,11 +19,15 @@ import warnings from ctypes import POINTER, c_void_p, c_char_p, c_double, c_int, c_int32, c_int64, cast +from .constants import LAMMPS_AUTODETECT, LAMMPS_INT, LAMMPS_INT_2D, \ + LAMMPS_DOUBLE, LAMMPS_DOUBLE_2D, LAMMPS_INT64, LAMMPS_INT64_2D, \ + LMP_STYLE_GLOBAL, LMP_STYLE_ATOM, LMP_STYLE_LOCAL, \ + LMP_TYPE_SCALAR, LMP_TYPE_VECTOR, LMP_TYPE_ARRAY, \ + LMP_SIZE_VECTOR, LMP_SIZE_ROWS, LMP_SIZE_COLS, \ + LMP_VAR_EQUAL, LMP_VAR_ATOM, LMP_VAR_VECTOR, LMP_VAR_STRING -from .constants import * # lgtm [py/polluting-import] from .data import NeighList - class numpy_wrapper: """lammps API NumPy Wrapper diff --git a/python/lammps/pylammps.py b/python/lammps/pylammps.py index 7dd3823bbf..4b740f35ff 100644 --- a/python/lammps/pylammps.py +++ b/python/lammps/pylammps.py @@ -28,7 +28,7 @@ import tempfile from collections import namedtuple from .core import lammps -from .constants import * # lgtm [py/polluting-import] +from .constants import LMP_VAR_EQUAL, LMP_VAR_ATOM, LMP_VAR_VECTOR, LMP_VAR_STRING # ------------------------------------------------------------------------- From 38874c5aaaf52fccaa8760929416d12bcacbd954 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 18 Apr 2024 00:27:38 -0400 Subject: [PATCH 004/355] make more consistent --- python/lammps/core.py | 6 ++---- python/lammps/pylammps.py | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/python/lammps/core.py b/python/lammps/core.py index 626ece0770..ae488dc241 100644 --- a/python/lammps/core.py +++ b/python/lammps/core.py @@ -23,16 +23,15 @@ from ctypes import CDLL, POINTER, RTLD_GLOBAL, CFUNCTYPE, py_object, byref, cast from os.path import dirname, abspath, join from inspect import getsourcefile -from .constants import LAMMPS_AUTODETECT, LAMMPS_STRING, \ +from lammps.constants import LAMMPS_AUTODETECT, LAMMPS_STRING, \ LAMMPS_INT, LAMMPS_INT_2D, LAMMPS_DOUBLE, LAMMPS_DOUBLE_2D, LAMMPS_INT64, LAMMPS_INT64_2D, \ LMP_STYLE_GLOBAL, LMP_STYLE_ATOM, LMP_STYLE_LOCAL, \ LMP_TYPE_SCALAR, LMP_TYPE_VECTOR, LMP_TYPE_ARRAY, \ LMP_SIZE_VECTOR, LMP_SIZE_ROWS, LMP_SIZE_COLS, \ - LMP_ERROR_WARNING, LMP_ERROR_ONE, LMP_ERROR_ALL, LMP_ERROR_WORLD, LMP_ERROR_UNIVERSE, \ LMP_VAR_EQUAL, LMP_VAR_ATOM, LMP_VAR_VECTOR, LMP_VAR_STRING, \ get_ctypes_int -from .data import NeighList +from lammps.data import NeighList # ------------------------------------------------------------------------- @@ -2081,7 +2080,6 @@ class lammps(object): :param caller: reference to some object passed to the callback function :type: object, optional """ - import numpy as np def callback_wrapper(caller, ntimestep, nlocal, tag_ptr, x_ptr, fext_ptr): tag = self.numpy.iarray(self.c_tagint, tag_ptr, nlocal, 1) diff --git a/python/lammps/pylammps.py b/python/lammps/pylammps.py index 4b740f35ff..b5eabd6436 100644 --- a/python/lammps/pylammps.py +++ b/python/lammps/pylammps.py @@ -27,8 +27,8 @@ import sys import tempfile from collections import namedtuple -from .core import lammps -from .constants import LMP_VAR_EQUAL, LMP_VAR_ATOM, LMP_VAR_VECTOR, LMP_VAR_STRING +from lammps.core import lammps +from lammps.constants import LMP_VAR_EQUAL, LMP_VAR_ATOM, LMP_VAR_VECTOR, LMP_VAR_STRING # ------------------------------------------------------------------------- From 3116250da98be7e4de86d89e50a49f29bccbe2dc Mon Sep 17 00:00:00 2001 From: Nick Hagerty Date: Fri, 19 Apr 2024 16:00:58 -0400 Subject: [PATCH 005/355] Initial port of kspace_modify collective yes to KOKKOS package --- src/KOKKOS/remap_kokkos.cpp | 359 ++++++++++++++++++++++++++++++------ src/KOKKOS/remap_kokkos.h | 8 +- 2 files changed, 309 insertions(+), 58 deletions(-) diff --git a/src/KOKKOS/remap_kokkos.cpp b/src/KOKKOS/remap_kokkos.cpp index 0d539ada83..7fbfdd1130 100644 --- a/src/KOKKOS/remap_kokkos.cpp +++ b/src/KOKKOS/remap_kokkos.cpp @@ -103,11 +103,7 @@ template void RemapKokkos::remap_3d_kokkos(typename FFT_AT::t_FFT_SCALAR_1d d_in, typename FFT_AT::t_FFT_SCALAR_1d d_out, typename FFT_AT::t_FFT_SCALAR_1d d_buf, struct remap_plan_3d_kokkos *plan) { - // collective flag not yet supported - // use point-to-point communication - - int i,isend,irecv; typename FFT_AT::t_FFT_SCALAR_1d d_scratch; if (plan->memory == 0) @@ -116,70 +112,119 @@ void RemapKokkos::remap_3d_kokkos(typename FFT_AT::t_FFT_SCALAR_1d d d_scratch = plan->d_scratch; // post all recvs into scratch space + // If not using GPU-aware MPI, mirror data to host FFT_SCALAR* v_scratch = d_scratch.data(); - if (!plan->usegpu_aware) { - plan->h_scratch = Kokkos::create_mirror_view(d_scratch); - v_scratch = plan->h_scratch.data(); - } - - for (irecv = 0; irecv < plan->nrecv; irecv++) { - FFT_SCALAR* scratch = v_scratch + plan->recv_bufloc[irecv]; - MPI_Irecv(scratch,plan->recv_size[irecv], - MPI_FFT_SCALAR,plan->recv_proc[irecv],0, - plan->comm,&plan->request[irecv]); - } - FFT_SCALAR* v_sendbuf = plan->d_sendbuf.data(); if (!plan->usegpu_aware) { + plan->h_scratch = Kokkos::create_mirror_view(d_scratch); plan->h_sendbuf = Kokkos::create_mirror_view(plan->d_sendbuf); + v_scratch = plan->h_scratch.data(); v_sendbuf = plan->h_sendbuf.data(); } - // send all messages to other procs + // use point-to-point communication - for (isend = 0; isend < plan->nsend; isend++) { - int in_offset = plan->send_offset[isend]; - plan->pack(d_in,in_offset, - plan->d_sendbuf,0,&plan->packplan[isend]); + if (!plan->usecollective) { + int i,isend,irecv; - if (!plan->usegpu_aware) - Kokkos::deep_copy(plan->h_sendbuf,plan->d_sendbuf); - MPI_Send(v_sendbuf,plan->send_size[isend],MPI_FFT_SCALAR, - plan->send_proc[isend],0,plan->comm); - } + for (irecv = 0; irecv < plan->nrecv; irecv++) { + FFT_SCALAR* scratch = v_scratch + plan->recv_bufloc[irecv]; + MPI_Irecv(scratch,plan->recv_size[irecv], + MPI_FFT_SCALAR,plan->recv_proc[irecv],0, + plan->comm,&plan->request[irecv]); + } - // copy in -> scratch -> out for self data + // send all messages to other procs - if (plan->self) { - isend = plan->nsend; - irecv = plan->nrecv; + for (isend = 0; isend < plan->nsend; isend++) { + int in_offset = plan->send_offset[isend]; + plan->pack(d_in,in_offset, + plan->d_sendbuf,0,&plan->packplan[isend]); - int in_offset = plan->send_offset[isend]; - int scratch_offset = plan->recv_bufloc[irecv]; - int out_offset = plan->recv_offset[irecv]; + if (!plan->usegpu_aware) + Kokkos::deep_copy(plan->h_sendbuf,plan->d_sendbuf); - plan->pack(d_in,in_offset, - d_scratch,scratch_offset, - &plan->packplan[isend]); - plan->unpack(d_scratch,scratch_offset, - d_out,out_offset,&plan->unpackplan[irecv]); - } + MPI_Send(v_sendbuf,plan->send_size[isend],MPI_FFT_SCALAR, + plan->send_proc[isend],0,plan->comm); + } - // unpack all messages from scratch -> out + // copy in -> scratch -> out for self data - for (i = 0; i < plan->nrecv; i++) { - MPI_Waitany(plan->nrecv,plan->request,&irecv,MPI_STATUS_IGNORE); + if (plan->self) { + isend = plan->nsend; + irecv = plan->nrecv; - int scratch_offset = plan->recv_bufloc[irecv]; - int out_offset = plan->recv_offset[irecv]; + int in_offset = plan->send_offset[isend]; + int scratch_offset = plan->recv_bufloc[irecv]; + int out_offset = plan->recv_offset[irecv]; - if (!plan->usegpu_aware) - Kokkos::deep_copy(d_scratch,plan->h_scratch); + plan->pack(d_in,in_offset, + d_scratch,scratch_offset, + &plan->packplan[isend]); + plan->unpack(d_scratch,scratch_offset, + d_out,out_offset,&plan->unpackplan[irecv]); + } - plan->unpack(d_scratch,scratch_offset, - d_out,out_offset,&plan->unpackplan[irecv]); + // unpack all messages from scratch -> out + + for (i = 0; i < plan->nrecv; i++) { + MPI_Waitany(plan->nrecv,plan->request,&irecv,MPI_STATUS_IGNORE); + + int scratch_offset = plan->recv_bufloc[irecv]; + int out_offset = plan->recv_offset[irecv]; + + if (!plan->usegpu_aware) + Kokkos::deep_copy(d_scratch,plan->h_scratch); + + plan->unpack(d_scratch,scratch_offset, + d_out,out_offset,&plan->unpackplan[irecv]); + } + } else { + if (plan->commringlen > 0) { + int isend,irecv; + + + // populate send data + // buffers are allocated and count/displacement buffers + // are populated in remap_3d_create_plan_kokkos + + int currentSendBufferOffset = 0; + for (isend = 0; isend < plan->commringlen; isend++) { + int foundentry = 0; + for (int i=0;(insend && !foundentry); i++) { + if (plan->send_proc[i] == plan->commringlist[isend]) { + foundentry = 1; + plan->pack(d_in,plan->send_offset[i], + plan->d_sendbuf,currentSendBufferOffset, + &plan->packplan[i]); + currentSendBufferOffset += plan->send_size[i]; + } + } + } + if (!plan->usegpu_aware) + Kokkos::deep_copy(plan->h_sendbuf,plan->d_sendbuf); + + MPI_Alltoallv(v_sendbuf, plan->sendcnts, plan->sdispls, + MPI_FFT_SCALAR, v_scratch, plan->rcvcnts, + plan->rdispls, MPI_FFT_SCALAR, plan->comm); + + // unpack the data from the recv buffer into out + + if (!plan->usegpu_aware) + Kokkos::deep_copy(d_scratch,plan->h_scratch); + + int currentRecvBufferOffset = 0; + for (irecv = 0; irecv < plan->commringlen; irecv++) { + if (plan->nrecvmap[irecv] > -1) { + plan->unpack(d_scratch,currentRecvBufferOffset, + d_out,plan->recv_offset[plan->nrecvmap[irecv]], + &plan->unpackplan[plan->nrecvmap[irecv]]); + currentRecvBufferOffset += plan->recv_size[plan->nrecvmap[irecv]]; + } + } + } } } @@ -223,7 +268,7 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat struct remap_plan_3d_kokkos *plan; struct extent_3d *inarray, *outarray; struct extent_3d in,out,overlap; - int i,iproc,nsend,nrecv,ibuf,size,me,nprocs; + int i,j,iproc,nsend,nrecv,ibuf,size,me,nprocs,isend,irecv; // query MPI info @@ -435,6 +480,108 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat } } + // create sub-comm rank list + if (plan->usecollective) { + plan->commringlist = nullptr; + + // merge recv and send rank lists + // ask Steve Plimpton about method to more accurately determine + // maximum number of procs contributing to pencil + + int maxcommsize = nprocs; + int *commringlist = (int *) malloc(maxcommsize*sizeof(int)); + int commringlen = 0; + + for (i = 0; i < nrecv; i++) { + commringlist[i] = plan->recv_proc[i]; + commringlen++; + } + + for (i = 0; i < nsend; i++) { + int foundentry = 0; + for (j = 0; j < commringlen;j++) + if (commringlist[j] == plan->send_proc[i]) foundentry = 1; + if (!foundentry) { + commringlist[commringlen] = plan->send_proc[i]; + commringlen++; + } + } + + // sort initial commringlist + + int swap = 0; + for (i = 0 ; i < (commringlen - 1); i++) { + for (j = 0 ; j < commringlen - i - 1; j++) { + if (commringlist[j] > commringlist[j+1]) { + swap = commringlist[j]; + commringlist[j] = commringlist[j+1]; + commringlist[j+1] = swap; + } + } + } + + // collide all inarray extents for the comm ring with all output + // extents and all outarray extents for the comm ring with all input + // extents - if there is a collison add the rank to the comm ring, + // keep iterating until nothing is added to commring + + int commringappend = 1; + while (commringappend) { + int newcommringlen = commringlen; + commringappend = 0; + for (i = 0; i < commringlen; i++) { + for (j = 0; j < nprocs; j++) { + if (remap_3d_collide(&inarray[commringlist[i]], + &outarray[j],&overlap)) { + int alreadyinlist = 0; + for (int k = 0; k < newcommringlen; k++) { + if (commringlist[k] == j) { + alreadyinlist = 1; + } + } + if (!alreadyinlist) { + commringlist[newcommringlen++] = j; + commringappend = 1; + } + } + if (remap_3d_collide(&outarray[commringlist[i]], + &inarray[j],&overlap)) { + int alreadyinlist = 0; + for (int k = 0 ; k < newcommringlen; k++) { + if (commringlist[k] == j) alreadyinlist = 1; + } + if (!alreadyinlist) { + commringlist[newcommringlen++] = j; + commringappend = 1; + } + } + } + } + commringlen = newcommringlen; + } + + // sort the final commringlist + + for (i = 0 ; i < ( commringlen - 1 ); i++) { + for (j = 0 ; j < commringlen - i - 1; j++) { + if (commringlist[j] > commringlist[j+1]) { + swap = commringlist[j]; + commringlist[j] = commringlist[j+1]; + commringlist[j+1] = swap; + } + } + } + + // resize commringlist to final size + + commringlist = (int *) realloc(commringlist, commringlen*sizeof(int)); + + // set the plan->commringlist + + plan->commringlen = commringlen; + plan->commringlist = commringlist; + } + // plan->nrecv = # of recvs not including self // for collectives include self in the nsend list @@ -455,15 +602,83 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat free(inarray); free(outarray); - // find biggest send message (not including self) and malloc space for it + // the plan->d_sendbuf and plan->d_recvbuf are used by both the + // collective & non-collective implementations. + // For non-collective, the buffer size is MAX(send_size) for any one send + // For collective, the buffer size is SUM(send_size) for all sends - size = 0; - for (nsend = 0; nsend < plan->nsend; nsend++) - size = MAX(size,plan->send_size[nsend]); + if (!plan->usecollective) { - if (size) { - plan->d_sendbuf = typename FFT_AT::t_FFT_SCALAR_1d("remap3d:sendbuf",size); - if (!plan->d_sendbuf.data()) return nullptr; + // find biggest send message (not including self) and malloc space for it + + size = 0; + for (nsend = 0; nsend < plan->nsend; nsend++) + size = MAX(size,plan->send_size[nsend]); + + if (size) { + plan->d_sendbuf = typename FFT_AT::t_FFT_SCALAR_1d("remap3d:sendbuf",size); + if (!plan->d_sendbuf.data()) return nullptr; + } + } else { + + // allocate buffer for all send messages (including self) + // the method to allocate receive scratch space is sufficient + // for collectives + + size = 0; + for (nsend = 0; nsend < plan->nsend; nsend++) + size += plan->send_size[nsend]; + + if (size) { + plan->d_sendbuf = typename FFT_AT::t_FFT_SCALAR_1d("remap3d:sendbuf",size); + if (!plan->d_sendbuf.data()) return nullptr; + } + + // allocate buffers for send and receive counts, displacements + + if (plan->commringlen) { + plan->sendcnts = (int *) malloc(sizeof(int) * plan->commringlen); + plan->rcvcnts = (int *) malloc(sizeof(int) * plan->commringlen); + plan->sdispls = (int *) malloc(sizeof(int) * plan->commringlen); + plan->rdispls = (int *) malloc(sizeof(int) * plan->commringlen); + plan->nrecvmap = (int *) malloc(sizeof(int) * plan->commringlen); + + // populate buffers for send counts & displacements + + int currentSendBufferOffset = 0; + for (isend = 0; isend < plan->commringlen; isend++) { + plan->sendcnts[isend] = 0; + plan->sdispls[isend] = 0; + int foundentry = 0; + for (int i=0;(insend && !foundentry); i++) { + if (plan->send_proc[i] == plan->commringlist[isend]) { + foundentry = 1; + plan->sendcnts[isend] = plan->send_size[i]; + plan->sdispls[isend] = currentSendBufferOffset; + currentSendBufferOffset += plan->send_size[i]; + } + } + } + + // populate buffers for recv counts & displacements + + int currentRecvBufferOffset = 0; + for (irecv = 0; irecv < plan->commringlen; irecv++) { + plan->rcvcnts[irecv] = 0; + plan->rdispls[irecv] = 0; + plan->nrecvmap[irecv] = -1; + int foundentry = 0; + for (int i=0;(inrecv && !foundentry); i++) { + if (plan->recv_proc[i] == plan->commringlist[irecv]) { + foundentry = 1; + plan->rcvcnts[irecv] = plan->recv_size[i]; + plan->rdispls[irecv] = currentRecvBufferOffset; + currentRecvBufferOffset += plan->recv_size[i]; + plan->nrecvmap[irecv] = i; + } + } + } + } } // if requested, allocate internal scratch space for recvs, @@ -477,9 +692,28 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat } } + // if using collective and the commringlist is NOT empty create a + // communicator for the plan based off an MPI_Group created with + // ranks from the commringlist + + if ((plan->usecollective && (plan->commringlen > 0))) { + MPI_Group orig_group, new_group; + MPI_Comm_group(comm, &orig_group); + MPI_Group_incl(orig_group, plan->commringlen, + plan->commringlist, &new_group); + MPI_Comm_create(comm, new_group, &plan->comm); + } + + // if using collective and the comm ring list is empty create + // a communicator for the plan with an empty group + + else if ((plan->usecollective) && (plan->commringlen == 0)) { + MPI_Comm_create(comm, MPI_GROUP_EMPTY, &plan->comm); + } + // not using collective - dup comm - MPI_Comm_dup(comm,&plan->comm); + else MPI_Comm_dup(comm,&plan->comm); // return pointer to plan @@ -500,6 +734,17 @@ void RemapKokkos::remap_3d_destroy_plan_kokkos(struct remap_plan_3d_ if (!((plan->usecollective) && (plan->commringlen == 0))) MPI_Comm_free(&plan->comm); + if (plan->usecollective) { + if (plan->commringlist != nullptr) { + free(plan->commringlist); + free(plan->sendcnts); + free(plan->rcvcnts); + free(plan->sdispls); + free(plan->rdispls); + free(plan->nrecvmap); + } + } + // free internal arrays if (plan->nsend || plan->self) { diff --git a/src/KOKKOS/remap_kokkos.h b/src/KOKKOS/remap_kokkos.h index 77a3b1a37a..9dbd8d683c 100644 --- a/src/KOKKOS/remap_kokkos.h +++ b/src/KOKKOS/remap_kokkos.h @@ -52,9 +52,15 @@ struct remap_plan_3d_kokkos { int memory; // user provides scratch space or not MPI_Comm comm; // group of procs performing remap int usecollective; // use collective or point-to-point MPI + int usegpu_aware; // use GPU-Aware MPI or not + // variables specific to collective MPI int commringlen; // length of commringlist int *commringlist; // ranks on communication ring of this plan - int usegpu_aware; // use GPU-Aware MPI or not + int *sendcnts; // # of elements in send buffer for each rank + int *rcvcnts; // # of elements in recv buffer for each rank + int *sdispls; // extraction location in send buffer for each rank + int *rdispls; // extraction location in recv buffer for each rank + int *nrecvmap; // maps receive index to rank index }; template From 0af4fe270281e49ee9e0cb243742d1f19bd408a7 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Sat, 20 Apr 2024 09:39:41 -0600 Subject: [PATCH 006/355] Enable collective comm for PPPMKokkos --- src/KOKKOS/pppm_kokkos.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/KOKKOS/pppm_kokkos.cpp b/src/KOKKOS/pppm_kokkos.cpp index 73e2c1f06f..5a936626d9 100644 --- a/src/KOKKOS/pppm_kokkos.cpp +++ b/src/KOKKOS/pppm_kokkos.cpp @@ -794,7 +794,6 @@ void PPPMKokkos::allocate() // 2nd FFT returns data in 3d brick decomposition // remap takes data from 3d brick to FFT decomposition - int collective_flag = 0; // not yet supported in Kokkos version int gpu_aware_flag = lmp->kokkos->gpu_aware_flag; int tmp; From f43fec417d33734a93b180b1759cab8dc34998b2 Mon Sep 17 00:00:00 2001 From: Nick Hagerty Date: Mon, 22 Apr 2024 10:23:14 -0400 Subject: [PATCH 007/355] Updated collective flag keyword in KOKKOS PPPM to use setting from input file --- src/KOKKOS/pppm_kokkos.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/KOKKOS/pppm_kokkos.cpp b/src/KOKKOS/pppm_kokkos.cpp index 73e2c1f06f..273a53ab8f 100644 --- a/src/KOKKOS/pppm_kokkos.cpp +++ b/src/KOKKOS/pppm_kokkos.cpp @@ -794,7 +794,7 @@ void PPPMKokkos::allocate() // 2nd FFT returns data in 3d brick decomposition // remap takes data from 3d brick to FFT decomposition - int collective_flag = 0; // not yet supported in Kokkos version + int collective_flag = force->kspace->collective_flag; int gpu_aware_flag = lmp->kokkos->gpu_aware_flag; int tmp; From f9e349a2bc5f9adc9d868dc6832af9a736873c26 Mon Sep 17 00:00:00 2001 From: Nick Hagerty Date: Tue, 23 Apr 2024 13:42:46 -0400 Subject: [PATCH 008/355] Fix load balancing issue with 2D FFTs --- src/KSPACE/pppm.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/KSPACE/pppm.cpp b/src/KSPACE/pppm.cpp index 4fe5075f44..06cbf119e3 100644 --- a/src/KSPACE/pppm.cpp +++ b/src/KSPACE/pppm.cpp @@ -1389,10 +1389,7 @@ void PPPM::set_grid_local() // of the global FFT mesh that I own in x-pencil decomposition int npey_fft,npez_fft; - if (nz_pppm >= nprocs) { - npey_fft = 1; - npez_fft = nprocs; - } else procs2grid2d(nprocs,ny_pppm,nz_pppm,&npey_fft,&npez_fft); + procs2grid2d(nprocs,ny_pppm,nz_pppm,&npey_fft,&npez_fft); int me_y = me % npey_fft; int me_z = me / npey_fft; From 65e8a5c981c559ef3ba5f5d1b2fb9e26667206ce Mon Sep 17 00:00:00 2001 From: Nick Hagerty Date: Tue, 23 Apr 2024 14:59:42 -0400 Subject: [PATCH 009/355] Relocated send & recv initializations to occur together, since they are independent --- src/KOKKOS/remap_kokkos.cpp | 97 ++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 55 deletions(-) diff --git a/src/KOKKOS/remap_kokkos.cpp b/src/KOKKOS/remap_kokkos.cpp index 7fbfdd1130..404fddd7c4 100644 --- a/src/KOKKOS/remap_kokkos.cpp +++ b/src/KOKKOS/remap_kokkos.cpp @@ -308,28 +308,29 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat out.khi = out_khi; out.ksize = out.khi - out.klo + 1; - // combine output extents across all procs - inarray = (struct extent_3d *) malloc(nprocs*sizeof(struct extent_3d)); if (inarray == nullptr) return nullptr; outarray = (struct extent_3d *) malloc(nprocs*sizeof(struct extent_3d)); if (outarray == nullptr) return nullptr; + // combine input & output extents across all procs + + MPI_Allgather(&in,sizeof(struct extent_3d),MPI_BYTE, + inarray,sizeof(struct extent_3d),MPI_BYTE,comm); MPI_Allgather(&out,sizeof(struct extent_3d),MPI_BYTE, outarray,sizeof(struct extent_3d),MPI_BYTE,comm); - // count send collides, including self + // count send & recv collides, including self nsend = 0; - iproc = me; + nrecv = 0; for (i = 0; i < nprocs; i++) { - iproc++; - if (iproc == nprocs) iproc = 0; - nsend += remap_3d_collide(&in,&outarray[iproc],&overlap); + nsend += remap_3d_collide(&in,&outarray[i],&overlap); + nrecv += remap_3d_collide(&out,&inarray[i],&overlap); } - // malloc space for send info + // malloc space for send & recv info if (nsend) { plan->pack = PackKokkos::pack_3d; @@ -344,6 +345,39 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat plan->send_proc == nullptr || plan->packplan == nullptr) return nullptr; } + if (nrecv) { + if (permute == 0) + plan->unpack = PackKokkos::unpack_3d; + else if (permute == 1) { + if (nqty == 1) + plan->unpack = PackKokkos::unpack_3d_permute1_1; + else if (nqty == 2) + plan->unpack = PackKokkos::unpack_3d_permute1_2; + else + plan->unpack = PackKokkos::unpack_3d_permute1_n; + } + else if (permute == 2) { + if (nqty == 1) + plan->unpack = PackKokkos::unpack_3d_permute2_1; + else if (nqty == 2) + plan->unpack = PackKokkos::unpack_3d_permute2_2; + else + plan->unpack = PackKokkos::unpack_3d_permute2_n; + } + + plan->recv_offset = (int *) malloc(nrecv*sizeof(int)); + plan->recv_size = (int *) malloc(nrecv*sizeof(int)); + plan->recv_proc = (int *) malloc(nrecv*sizeof(int)); + plan->recv_bufloc = (int *) malloc(nrecv*sizeof(int)); + plan->request = (MPI_Request *) malloc(nrecv*sizeof(MPI_Request)); + plan->unpackplan = (struct pack_plan_3d *) + malloc(nrecv*sizeof(struct pack_plan_3d)); + + if (plan->recv_offset == nullptr || plan->recv_size == nullptr || + plan->recv_proc == nullptr || plan->recv_bufloc == nullptr || + plan->request == nullptr || plan->unpackplan == nullptr) return nullptr; + } + // store send info, with self as last entry nsend = 0; @@ -377,55 +411,8 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat } else plan->nsend = nsend; - // combine input extents across all procs - - MPI_Allgather(&in,sizeof(struct extent_3d),MPI_BYTE, - inarray,sizeof(struct extent_3d),MPI_BYTE,comm); - - // count recv collides, including self - - nrecv = 0; - iproc = me; - for (i = 0; i < nprocs; i++) { - iproc++; - if (iproc == nprocs) iproc = 0; - nrecv += remap_3d_collide(&out,&inarray[iproc],&overlap); - } - // malloc space for recv info - if (nrecv) { - if (permute == 0) - plan->unpack = PackKokkos::unpack_3d; - else if (permute == 1) { - if (nqty == 1) - plan->unpack = PackKokkos::unpack_3d_permute1_1; - else if (nqty == 2) - plan->unpack = PackKokkos::unpack_3d_permute1_2; - else - plan->unpack = PackKokkos::unpack_3d_permute1_n; - } - else if (permute == 2) { - if (nqty == 1) - plan->unpack = PackKokkos::unpack_3d_permute2_1; - else if (nqty == 2) - plan->unpack = PackKokkos::unpack_3d_permute2_2; - else - plan->unpack = PackKokkos::unpack_3d_permute2_n; - } - - plan->recv_offset = (int *) malloc(nrecv*sizeof(int)); - plan->recv_size = (int *) malloc(nrecv*sizeof(int)); - plan->recv_proc = (int *) malloc(nrecv*sizeof(int)); - plan->recv_bufloc = (int *) malloc(nrecv*sizeof(int)); - plan->request = (MPI_Request *) malloc(nrecv*sizeof(MPI_Request)); - plan->unpackplan = (struct pack_plan_3d *) - malloc(nrecv*sizeof(struct pack_plan_3d)); - - if (plan->recv_offset == nullptr || plan->recv_size == nullptr || - plan->recv_proc == nullptr || plan->recv_bufloc == nullptr || - plan->request == nullptr || plan->unpackplan == nullptr) return nullptr; - } // store recv info, with self as last entry From 1a431b02aed66fba073f22458ffefb6c81021ba7 Mon Sep 17 00:00:00 2001 From: Nick Hagerty Date: Wed, 24 Apr 2024 09:23:29 -0400 Subject: [PATCH 010/355] Split collective and non-collective into conditionals. Multi-node test passing. Beginning optimization of collective --- src/KOKKOS/remap_kokkos.cpp | 611 ++++++++++++++++++++++-------------- 1 file changed, 382 insertions(+), 229 deletions(-) diff --git a/src/KOKKOS/remap_kokkos.cpp b/src/KOKKOS/remap_kokkos.cpp index 404fddd7c4..1dcf6c4938 100644 --- a/src/KOKKOS/remap_kokkos.cpp +++ b/src/KOKKOS/remap_kokkos.cpp @@ -321,169 +321,349 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat MPI_Allgather(&out,sizeof(struct extent_3d),MPI_BYTE, outarray,sizeof(struct extent_3d),MPI_BYTE,comm); - // count send & recv collides, including self + // for efficiency, handle collective & non-collective setup separately - nsend = 0; - nrecv = 0; - for (i = 0; i < nprocs; i++) { - nsend += remap_3d_collide(&in,&outarray[i],&overlap); - nrecv += remap_3d_collide(&out,&inarray[i],&overlap); - } + if (!plan->usecollective) { + // count send & recv collides, including self - // malloc space for send & recv info - - if (nsend) { - plan->pack = PackKokkos::pack_3d; - - plan->send_offset = (int *) malloc(nsend*sizeof(int)); - plan->send_size = (int *) malloc(nsend*sizeof(int)); - plan->send_proc = (int *) malloc(nsend*sizeof(int)); - plan->packplan = (struct pack_plan_3d *) - malloc(nsend*sizeof(struct pack_plan_3d)); - - if (plan->send_offset == nullptr || plan->send_size == nullptr || - plan->send_proc == nullptr || plan->packplan == nullptr) return nullptr; - } - - if (nrecv) { - if (permute == 0) - plan->unpack = PackKokkos::unpack_3d; - else if (permute == 1) { - if (nqty == 1) - plan->unpack = PackKokkos::unpack_3d_permute1_1; - else if (nqty == 2) - plan->unpack = PackKokkos::unpack_3d_permute1_2; - else - plan->unpack = PackKokkos::unpack_3d_permute1_n; - } - else if (permute == 2) { - if (nqty == 1) - plan->unpack = PackKokkos::unpack_3d_permute2_1; - else if (nqty == 2) - plan->unpack = PackKokkos::unpack_3d_permute2_2; - else - plan->unpack = PackKokkos::unpack_3d_permute2_n; + nsend = 0; + nrecv = 0; + for (i = 0; i < nprocs; i++) { + nsend += remap_3d_collide(&in,&outarray[i],&overlap); + nrecv += remap_3d_collide(&out,&inarray[i],&overlap); } - plan->recv_offset = (int *) malloc(nrecv*sizeof(int)); - plan->recv_size = (int *) malloc(nrecv*sizeof(int)); - plan->recv_proc = (int *) malloc(nrecv*sizeof(int)); - plan->recv_bufloc = (int *) malloc(nrecv*sizeof(int)); - plan->request = (MPI_Request *) malloc(nrecv*sizeof(MPI_Request)); - plan->unpackplan = (struct pack_plan_3d *) - malloc(nrecv*sizeof(struct pack_plan_3d)); + // malloc space for send & recv info - if (plan->recv_offset == nullptr || plan->recv_size == nullptr || - plan->recv_proc == nullptr || plan->recv_bufloc == nullptr || - plan->request == nullptr || plan->unpackplan == nullptr) return nullptr; - } + if (nsend) { + plan->pack = PackKokkos::pack_3d; - // store send info, with self as last entry + plan->send_offset = (int *) malloc(nsend*sizeof(int)); + plan->send_size = (int *) malloc(nsend*sizeof(int)); + plan->send_proc = (int *) malloc(nsend*sizeof(int)); + plan->packplan = (struct pack_plan_3d *) + malloc(nsend*sizeof(struct pack_plan_3d)); - nsend = 0; - iproc = me; - for (i = 0; i < nprocs; i++) { - iproc++; - if (iproc == nprocs) iproc = 0; - if (remap_3d_collide(&in,&outarray[iproc],&overlap)) { - plan->send_proc[nsend] = iproc; - plan->send_offset[nsend] = nqty * - ((overlap.klo-in.klo)*in.jsize*in.isize + - ((overlap.jlo-in.jlo)*in.isize + overlap.ilo-in.ilo)); - plan->packplan[nsend].nfast = nqty*overlap.isize; - plan->packplan[nsend].nmid = overlap.jsize; - plan->packplan[nsend].nslow = overlap.ksize; - plan->packplan[nsend].nstride_line = nqty*in.isize; - plan->packplan[nsend].nstride_plane = nqty*in.jsize*in.isize; - plan->packplan[nsend].nqty = nqty; - plan->send_size[nsend] = nqty*overlap.isize*overlap.jsize*overlap.ksize; - nsend++; + if (plan->send_offset == nullptr || plan->send_size == nullptr || + plan->send_proc == nullptr || plan->packplan == nullptr) return nullptr; } - } - // plan->nsend = # of sends not including self - - if (nsend && plan->send_proc[nsend-1] == me) { - if (plan->usecollective) // for collectives include self in nsend list - plan->nsend = nsend; - else - plan->nsend = nsend - 1; - } else - plan->nsend = nsend; - - // malloc space for recv info - - - // store recv info, with self as last entry - - ibuf = 0; - nrecv = 0; - iproc = me; - - for (i = 0; i < nprocs; i++) { - iproc++; - if (iproc == nprocs) iproc = 0; - if (remap_3d_collide(&out,&inarray[iproc],&overlap)) { - plan->recv_proc[nrecv] = iproc; - plan->recv_bufloc[nrecv] = ibuf; - - if (permute == 0) { - plan->recv_offset[nrecv] = nqty * - ((overlap.klo-out.klo)*out.jsize*out.isize + - (overlap.jlo-out.jlo)*out.isize + (overlap.ilo-out.ilo)); - plan->unpackplan[nrecv].nfast = nqty*overlap.isize; - plan->unpackplan[nrecv].nmid = overlap.jsize; - plan->unpackplan[nrecv].nslow = overlap.ksize; - plan->unpackplan[nrecv].nstride_line = nqty*out.isize; - plan->unpackplan[nrecv].nstride_plane = nqty*out.jsize*out.isize; - plan->unpackplan[nrecv].nqty = nqty; - } + if (nrecv) { + if (permute == 0) + plan->unpack = PackKokkos::unpack_3d; else if (permute == 1) { - plan->recv_offset[nrecv] = nqty * - ((overlap.ilo-out.ilo)*out.ksize*out.jsize + - (overlap.klo-out.klo)*out.jsize + (overlap.jlo-out.jlo)); - plan->unpackplan[nrecv].nfast = overlap.isize; - plan->unpackplan[nrecv].nmid = overlap.jsize; - plan->unpackplan[nrecv].nslow = overlap.ksize; - plan->unpackplan[nrecv].nstride_line = nqty*out.jsize; - plan->unpackplan[nrecv].nstride_plane = nqty*out.ksize*out.jsize; - plan->unpackplan[nrecv].nqty = nqty; + if (nqty == 1) + plan->unpack = PackKokkos::unpack_3d_permute1_1; + else if (nqty == 2) + plan->unpack = PackKokkos::unpack_3d_permute1_2; + else + plan->unpack = PackKokkos::unpack_3d_permute1_n; } - else { - plan->recv_offset[nrecv] = nqty * - ((overlap.jlo-out.jlo)*out.isize*out.ksize + - (overlap.ilo-out.ilo)*out.ksize + (overlap.klo-out.klo)); - plan->unpackplan[nrecv].nfast = overlap.isize; - plan->unpackplan[nrecv].nmid = overlap.jsize; - plan->unpackplan[nrecv].nslow = overlap.ksize; - plan->unpackplan[nrecv].nstride_line = nqty*out.ksize; - plan->unpackplan[nrecv].nstride_plane = nqty*out.isize*out.ksize; - plan->unpackplan[nrecv].nqty = nqty; + else if (permute == 2) { + if (nqty == 1) + plan->unpack = PackKokkos::unpack_3d_permute2_1; + else if (nqty == 2) + plan->unpack = PackKokkos::unpack_3d_permute2_2; + else + plan->unpack = PackKokkos::unpack_3d_permute2_n; } - plan->recv_size[nrecv] = nqty*overlap.isize*overlap.jsize*overlap.ksize; - ibuf += plan->recv_size[nrecv]; - nrecv++; + plan->recv_offset = (int *) malloc(nrecv*sizeof(int)); + plan->recv_size = (int *) malloc(nrecv*sizeof(int)); + plan->recv_proc = (int *) malloc(nrecv*sizeof(int)); + plan->recv_bufloc = (int *) malloc(nrecv*sizeof(int)); + plan->request = (MPI_Request *) malloc(nrecv*sizeof(MPI_Request)); + plan->unpackplan = (struct pack_plan_3d *) + malloc(nrecv*sizeof(struct pack_plan_3d)); + + if (plan->recv_offset == nullptr || plan->recv_size == nullptr || + plan->recv_proc == nullptr || plan->recv_bufloc == nullptr || + plan->request == nullptr || plan->unpackplan == nullptr) return nullptr; } - } - // create sub-comm rank list - if (plan->usecollective) { + // store send info, with self as last entry + + nsend = 0; + iproc = me; + for (i = 0; i < nprocs; i++) { + iproc++; + if (iproc == nprocs) iproc = 0; + if (remap_3d_collide(&in,&outarray[iproc],&overlap)) { + plan->send_proc[nsend] = iproc; + plan->send_offset[nsend] = nqty * + ((overlap.klo-in.klo)*in.jsize*in.isize + + ((overlap.jlo-in.jlo)*in.isize + overlap.ilo-in.ilo)); + plan->packplan[nsend].nfast = nqty*overlap.isize; + plan->packplan[nsend].nmid = overlap.jsize; + plan->packplan[nsend].nslow = overlap.ksize; + plan->packplan[nsend].nstride_line = nqty*in.isize; + plan->packplan[nsend].nstride_plane = nqty*in.jsize*in.isize; + plan->packplan[nsend].nqty = nqty; + plan->send_size[nsend] = nqty*overlap.isize*overlap.jsize*overlap.ksize; + nsend++; + } + } + + // plan->nsend = # of sends not including self + + if (nsend && plan->send_proc[nsend-1] == me) plan->nsend = nsend - 1; + else plan->nsend = nsend; + + // store recv info, with self as last entry + + ibuf = 0; + nrecv = 0; + iproc = me; + + for (i = 0; i < nprocs; i++) { + iproc++; + if (iproc == nprocs) iproc = 0; + if (remap_3d_collide(&out,&inarray[iproc],&overlap)) { + plan->recv_proc[nrecv] = iproc; + plan->recv_bufloc[nrecv] = ibuf; + + if (permute == 0) { + plan->recv_offset[nrecv] = nqty * + ((overlap.klo-out.klo)*out.jsize*out.isize + + (overlap.jlo-out.jlo)*out.isize + (overlap.ilo-out.ilo)); + plan->unpackplan[nrecv].nfast = nqty*overlap.isize; + plan->unpackplan[nrecv].nmid = overlap.jsize; + plan->unpackplan[nrecv].nslow = overlap.ksize; + plan->unpackplan[nrecv].nstride_line = nqty*out.isize; + plan->unpackplan[nrecv].nstride_plane = nqty*out.jsize*out.isize; + plan->unpackplan[nrecv].nqty = nqty; + } + else if (permute == 1) { + plan->recv_offset[nrecv] = nqty * + ((overlap.ilo-out.ilo)*out.ksize*out.jsize + + (overlap.klo-out.klo)*out.jsize + (overlap.jlo-out.jlo)); + plan->unpackplan[nrecv].nfast = overlap.isize; + plan->unpackplan[nrecv].nmid = overlap.jsize; + plan->unpackplan[nrecv].nslow = overlap.ksize; + plan->unpackplan[nrecv].nstride_line = nqty*out.jsize; + plan->unpackplan[nrecv].nstride_plane = nqty*out.ksize*out.jsize; + plan->unpackplan[nrecv].nqty = nqty; + } + else { + plan->recv_offset[nrecv] = nqty * + ((overlap.jlo-out.jlo)*out.isize*out.ksize + + (overlap.ilo-out.ilo)*out.ksize + (overlap.klo-out.klo)); + plan->unpackplan[nrecv].nfast = overlap.isize; + plan->unpackplan[nrecv].nmid = overlap.jsize; + plan->unpackplan[nrecv].nslow = overlap.ksize; + plan->unpackplan[nrecv].nstride_line = nqty*out.ksize; + plan->unpackplan[nrecv].nstride_plane = nqty*out.isize*out.ksize; + plan->unpackplan[nrecv].nqty = nqty; + } + + plan->recv_size[nrecv] = nqty*overlap.isize*overlap.jsize*overlap.ksize; + ibuf += plan->recv_size[nrecv]; + nrecv++; + } + } + + // plan->nrecv = # of recvs not including self + + if (nrecv && plan->recv_proc[nrecv-1] == me) plan->nrecv = nrecv - 1; + else plan->nrecv = nrecv; + + // init remaining fields in remap plan + + plan->memory = memory; + + if (nrecv == plan->nrecv) plan->self = 0; + else plan->self = 1; + + + // the plan->d_sendbuf and plan->d_recvbuf are used by both the + // collective & non-collective implementations. + // For non-collective, the buffer size is MAX(send_size) for any one send + + // find biggest send message (not including self) and malloc space for it + + size = 0; + for (nsend = 0; nsend < plan->nsend; nsend++) + size = MAX(size,plan->send_size[nsend]); + + if (size) { + plan->d_sendbuf = typename FFT_AT::t_FFT_SCALAR_1d("remap3d:sendbuf",size); + if (!plan->d_sendbuf.data()) return nullptr; + } + + // if requested, allocate internal scratch space for recvs, + // only need it if I will receive any data (including self) + + if (memory == 1) { + if (nrecv > 0) { + plan->d_scratch = + typename FFT_AT::t_FFT_SCALAR_1d("remap3d:scratch",nqty*out.isize*out.jsize*out.ksize); + if (!plan->d_scratch.data()) return nullptr; + } + } + + // Non-collectives do not use MPI Communicator Groups + + MPI_Comm_dup(comm,&plan->comm); + } else { + // count send & recv collides, including self + + nsend = 0; + nrecv = 0; + for (i = 0; i < nprocs; i++) { + nsend += remap_3d_collide(&in,&outarray[i],&overlap); + nrecv += remap_3d_collide(&out,&inarray[i],&overlap); + } + + // malloc space for send & recv info + + if (nsend) { + plan->pack = PackKokkos::pack_3d; + + plan->send_offset = (int *) malloc(nsend*sizeof(int)); + plan->send_size = (int *) malloc(nsend*sizeof(int)); + plan->send_proc = (int *) malloc(nsend*sizeof(int)); + plan->packplan = (struct pack_plan_3d *) + malloc(nsend*sizeof(struct pack_plan_3d)); + + if (plan->send_offset == nullptr || plan->send_size == nullptr || + plan->send_proc == nullptr || plan->packplan == nullptr) return nullptr; + } + + if (nrecv) { + if (permute == 0) + plan->unpack = PackKokkos::unpack_3d; + else if (permute == 1) { + if (nqty == 1) + plan->unpack = PackKokkos::unpack_3d_permute1_1; + else if (nqty == 2) + plan->unpack = PackKokkos::unpack_3d_permute1_2; + else + plan->unpack = PackKokkos::unpack_3d_permute1_n; + } + else if (permute == 2) { + if (nqty == 1) + plan->unpack = PackKokkos::unpack_3d_permute2_1; + else if (nqty == 2) + plan->unpack = PackKokkos::unpack_3d_permute2_2; + else + plan->unpack = PackKokkos::unpack_3d_permute2_n; + } + + plan->recv_offset = (int *) malloc(nrecv*sizeof(int)); + plan->recv_size = (int *) malloc(nrecv*sizeof(int)); + plan->recv_proc = (int *) malloc(nrecv*sizeof(int)); + plan->recv_bufloc = (int *) malloc(nrecv*sizeof(int)); + plan->request = (MPI_Request *) malloc(nrecv*sizeof(MPI_Request)); + plan->unpackplan = (struct pack_plan_3d *) + malloc(nrecv*sizeof(struct pack_plan_3d)); + + if (plan->recv_offset == nullptr || plan->recv_size == nullptr || + plan->recv_proc == nullptr || plan->recv_bufloc == nullptr || + plan->request == nullptr || plan->unpackplan == nullptr) return nullptr; + } + + // store send info, with self as last entry + + nsend = 0; + iproc = me; + for (i = 0; i < nprocs; i++) { + iproc++; + if (iproc == nprocs) iproc = 0; + if (remap_3d_collide(&in,&outarray[iproc],&overlap)) { + plan->send_proc[nsend] = iproc; + plan->send_offset[nsend] = nqty * + ((overlap.klo-in.klo)*in.jsize*in.isize + + ((overlap.jlo-in.jlo)*in.isize + overlap.ilo-in.ilo)); + plan->packplan[nsend].nfast = nqty*overlap.isize; + plan->packplan[nsend].nmid = overlap.jsize; + plan->packplan[nsend].nslow = overlap.ksize; + plan->packplan[nsend].nstride_line = nqty*in.isize; + plan->packplan[nsend].nstride_plane = nqty*in.jsize*in.isize; + plan->packplan[nsend].nqty = nqty; + plan->send_size[nsend] = nqty*overlap.isize*overlap.jsize*overlap.ksize; + nsend++; + } + } + + // plan->nsend = # of sends not including self + + plan->nsend = nsend; + + // store recv info, with self as last entry + + ibuf = 0; + nrecv = 0; + iproc = me; + + for (i = 0; i < nprocs; i++) { + iproc++; + if (iproc == nprocs) iproc = 0; + if (remap_3d_collide(&out,&inarray[iproc],&overlap)) { + plan->recv_proc[nrecv] = iproc; + plan->recv_bufloc[nrecv] = ibuf; + + if (permute == 0) { + plan->recv_offset[nrecv] = nqty * + ((overlap.klo-out.klo)*out.jsize*out.isize + + (overlap.jlo-out.jlo)*out.isize + (overlap.ilo-out.ilo)); + plan->unpackplan[nrecv].nfast = nqty*overlap.isize; + plan->unpackplan[nrecv].nmid = overlap.jsize; + plan->unpackplan[nrecv].nslow = overlap.ksize; + plan->unpackplan[nrecv].nstride_line = nqty*out.isize; + plan->unpackplan[nrecv].nstride_plane = nqty*out.jsize*out.isize; + plan->unpackplan[nrecv].nqty = nqty; + } + else if (permute == 1) { + plan->recv_offset[nrecv] = nqty * + ((overlap.ilo-out.ilo)*out.ksize*out.jsize + + (overlap.klo-out.klo)*out.jsize + (overlap.jlo-out.jlo)); + plan->unpackplan[nrecv].nfast = overlap.isize; + plan->unpackplan[nrecv].nmid = overlap.jsize; + plan->unpackplan[nrecv].nslow = overlap.ksize; + plan->unpackplan[nrecv].nstride_line = nqty*out.jsize; + plan->unpackplan[nrecv].nstride_plane = nqty*out.ksize*out.jsize; + plan->unpackplan[nrecv].nqty = nqty; + } + else { + plan->recv_offset[nrecv] = nqty * + ((overlap.jlo-out.jlo)*out.isize*out.ksize + + (overlap.ilo-out.ilo)*out.ksize + (overlap.klo-out.klo)); + plan->unpackplan[nrecv].nfast = overlap.isize; + plan->unpackplan[nrecv].nmid = overlap.jsize; + plan->unpackplan[nrecv].nslow = overlap.ksize; + plan->unpackplan[nrecv].nstride_line = nqty*out.ksize; + plan->unpackplan[nrecv].nstride_plane = nqty*out.isize*out.ksize; + plan->unpackplan[nrecv].nqty = nqty; + } + + plan->recv_size[nrecv] = nqty*overlap.isize*overlap.jsize*overlap.ksize; + ibuf += plan->recv_size[nrecv]; + nrecv++; + } + } + + // plan->nrecv = # of recvs not including self + // for collectives include self in the nsend list + + plan->nrecv = nrecv; + + // create sub-comm rank list + plan->commringlist = nullptr; - + // merge recv and send rank lists // ask Steve Plimpton about method to more accurately determine // maximum number of procs contributing to pencil - + int maxcommsize = nprocs; int *commringlist = (int *) malloc(maxcommsize*sizeof(int)); int commringlen = 0; - + for (i = 0; i < nrecv; i++) { commringlist[i] = plan->recv_proc[i]; commringlen++; } - + for (i = 0; i < nsend; i++) { int foundentry = 0; for (j = 0; j < commringlen;j++) @@ -493,9 +673,9 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat commringlen++; } } - + // sort initial commringlist - + int swap = 0; for (i = 0 ; i < (commringlen - 1); i++) { for (j = 0 ; j < commringlen - i - 1; j++) { @@ -506,12 +686,12 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat } } } - + // collide all inarray extents for the comm ring with all output // extents and all outarray extents for the comm ring with all input // extents - if there is a collison add the rank to the comm ring, // keep iterating until nothing is added to commring - + int commringappend = 1; while (commringappend) { int newcommringlen = commringlen; @@ -519,7 +699,7 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat for (i = 0; i < commringlen; i++) { for (j = 0; j < nprocs; j++) { if (remap_3d_collide(&inarray[commringlist[i]], - &outarray[j],&overlap)) { + &outarray[j],&overlap)) { int alreadyinlist = 0; for (int k = 0; k < newcommringlen; k++) { if (commringlist[k] == j) { @@ -532,7 +712,7 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat } } if (remap_3d_collide(&outarray[commringlist[i]], - &inarray[j],&overlap)) { + &inarray[j],&overlap)) { int alreadyinlist = 0; for (int k = 0 ; k < newcommringlen; k++) { if (commringlist[k] == j) alreadyinlist = 1; @@ -546,9 +726,9 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat } commringlen = newcommringlen; } - + // sort the final commringlist - + for (i = 0 ; i < ( commringlen - 1 ); i++) { for (j = 0 ; j < commringlen - i - 1; j++) { if (commringlist[j] > commringlist[j+1]) { @@ -558,80 +738,52 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat } } } - + // resize commringlist to final size - + commringlist = (int *) realloc(commringlist, commringlen*sizeof(int)); - + // set the plan->commringlist - + plan->commringlen = commringlen; plan->commringlist = commringlist; - } - - // plan->nrecv = # of recvs not including self - // for collectives include self in the nsend list - - if (nrecv && plan->recv_proc[nrecv-1] == me) { - if (plan->usecollective) plan->nrecv = nrecv; - else plan->nrecv = nrecv - 1; - } else plan->nrecv = nrecv; - - // init remaining fields in remap plan - - plan->memory = memory; - - if (nrecv == plan->nrecv) plan->self = 0; - else plan->self = 1; - - // free locally malloced space - - free(inarray); - free(outarray); - - // the plan->d_sendbuf and plan->d_recvbuf are used by both the - // collective & non-collective implementations. - // For non-collective, the buffer size is MAX(send_size) for any one send - // For collective, the buffer size is SUM(send_size) for all sends - - if (!plan->usecollective) { - - // find biggest send message (not including self) and malloc space for it - - size = 0; - for (nsend = 0; nsend < plan->nsend; nsend++) - size = MAX(size,plan->send_size[nsend]); - - if (size) { - plan->d_sendbuf = typename FFT_AT::t_FFT_SCALAR_1d("remap3d:sendbuf",size); - if (!plan->d_sendbuf.data()) return nullptr; - } - } else { - + + // init remaining fields in remap plan + + plan->memory = memory; + + if (nrecv == plan->nrecv) plan->self = 0; + else plan->self = 1; + + // the plan->d_sendbuf and plan->d_recvbuf are used by both the + // collective & non-collective implementations. + // For non-collective, the buffer size is MAX(send_size) for any one send + // For collective, the buffer size is SUM(send_size) for all sends + // allocate buffer for all send messages (including self) // the method to allocate receive scratch space is sufficient // for collectives - + size = 0; for (nsend = 0; nsend < plan->nsend; nsend++) size += plan->send_size[nsend]; - + if (size) { plan->d_sendbuf = typename FFT_AT::t_FFT_SCALAR_1d("remap3d:sendbuf",size); if (!plan->d_sendbuf.data()) return nullptr; } - + // allocate buffers for send and receive counts, displacements - + if (plan->commringlen) { plan->sendcnts = (int *) malloc(sizeof(int) * plan->commringlen); plan->rcvcnts = (int *) malloc(sizeof(int) * plan->commringlen); plan->sdispls = (int *) malloc(sizeof(int) * plan->commringlen); plan->rdispls = (int *) malloc(sizeof(int) * plan->commringlen); plan->nrecvmap = (int *) malloc(sizeof(int) * plan->commringlen); - + // populate buffers for send counts & displacements - + int currentSendBufferOffset = 0; for (isend = 0; isend < plan->commringlen; isend++) { plan->sendcnts[isend] = 0; @@ -646,9 +798,9 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat } } } - + // populate buffers for recv counts & displacements - + int currentRecvBufferOffset = 0; for (irecv = 0; irecv < plan->commringlen; irecv++) { plan->rcvcnts[irecv] = 0; @@ -666,41 +818,42 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat } } } - } - - // if requested, allocate internal scratch space for recvs, - // only need it if I will receive any data (including self) - - if (memory == 1) { - if (nrecv > 0) { - plan->d_scratch = - typename FFT_AT::t_FFT_SCALAR_1d("remap3d:scratch",nqty*out.isize*out.jsize*out.ksize); - if (!plan->d_scratch.data()) return nullptr; + + // if requested, allocate internal scratch space for recvs, + // only need it if I will receive any data (including self) + + if (memory == 1) { + if (nrecv > 0) { + plan->d_scratch = + typename FFT_AT::t_FFT_SCALAR_1d("remap3d:scratch",nqty*out.isize*out.jsize*out.ksize); + if (!plan->d_scratch.data()) return nullptr; + } } + + // if using collective and the commringlist is NOT empty create a + // communicator for the plan based off an MPI_Group created with + // ranks from the commringlist + + if (plan->commringlen > 0) { + MPI_Group orig_group, new_group; + MPI_Comm_group(comm, &orig_group); + MPI_Group_incl(orig_group, plan->commringlen, + plan->commringlist, &new_group); + MPI_Comm_create(comm, new_group, &plan->comm); + } + + // if using collective and the comm ring list is empty create + // a communicator for the plan with an empty group + + else + MPI_Comm_create(comm, MPI_GROUP_EMPTY, &plan->comm); + } - // if using collective and the commringlist is NOT empty create a - // communicator for the plan based off an MPI_Group created with - // ranks from the commringlist + // free locally malloced space - if ((plan->usecollective && (plan->commringlen > 0))) { - MPI_Group orig_group, new_group; - MPI_Comm_group(comm, &orig_group); - MPI_Group_incl(orig_group, plan->commringlen, - plan->commringlist, &new_group); - MPI_Comm_create(comm, new_group, &plan->comm); - } - - // if using collective and the comm ring list is empty create - // a communicator for the plan with an empty group - - else if ((plan->usecollective) && (plan->commringlen == 0)) { - MPI_Comm_create(comm, MPI_GROUP_EMPTY, &plan->comm); - } - - // not using collective - dup comm - - else MPI_Comm_dup(comm,&plan->comm); + free(inarray); + free(outarray); // return pointer to plan From 71f82e70ef896f76f22f98d60f2bf396ac20763f Mon Sep 17 00:00:00 2001 From: Nick Hagerty Date: Thu, 25 Apr 2024 13:16:03 -0400 Subject: [PATCH 011/355] Refactored kspace+kokkos collective remap. Need to go back and tidy up memory allocations --- src/KOKKOS/remap_kokkos.cpp | 381 ++++++++++++++---------------------- 1 file changed, 150 insertions(+), 231 deletions(-) diff --git a/src/KOKKOS/remap_kokkos.cpp b/src/KOKKOS/remap_kokkos.cpp index 1dcf6c4938..fafb3dc37b 100644 --- a/src/KOKKOS/remap_kokkos.cpp +++ b/src/KOKKOS/remap_kokkos.cpp @@ -185,22 +185,17 @@ void RemapKokkos::remap_3d_kokkos(typename FFT_AT::t_FFT_SCALAR_1d d if (plan->commringlen > 0) { int isend,irecv; - // populate send data // buffers are allocated and count/displacement buffers // are populated in remap_3d_create_plan_kokkos - int currentSendBufferOffset = 0; + int numpacked = 0; for (isend = 0; isend < plan->commringlen; isend++) { - int foundentry = 0; - for (int i=0;(insend && !foundentry); i++) { - if (plan->send_proc[i] == plan->commringlist[isend]) { - foundentry = 1; - plan->pack(d_in,plan->send_offset[i], - plan->d_sendbuf,currentSendBufferOffset, - &plan->packplan[i]); - currentSendBufferOffset += plan->send_size[i]; - } + if (plan->sendcnts[isend] > 0) { + plan->pack(d_in,plan->send_offset[numpacked], + plan->d_sendbuf,plan->sdispls[isend], + &plan->packplan[numpacked]); + numpacked++; } } if (!plan->usegpu_aware) @@ -215,13 +210,13 @@ void RemapKokkos::remap_3d_kokkos(typename FFT_AT::t_FFT_SCALAR_1d d if (!plan->usegpu_aware) Kokkos::deep_copy(d_scratch,plan->h_scratch); - int currentRecvBufferOffset = 0; + numpacked = 0; for (irecv = 0; irecv < plan->commringlen; irecv++) { - if (plan->nrecvmap[irecv] > -1) { - plan->unpack(d_scratch,currentRecvBufferOffset, - d_out,plan->recv_offset[plan->nrecvmap[irecv]], - &plan->unpackplan[plan->nrecvmap[irecv]]); - currentRecvBufferOffset += plan->recv_size[plan->nrecvmap[irecv]]; + if (plan->rcvcnts[irecv] > 0) { + plan->unpack(d_scratch,plan->rdispls[irecv], + d_out,plan->recv_offset[numpacked], + &plan->unpackplan[numpacked]); + numpacked++; } } } @@ -505,31 +500,105 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat MPI_Comm_dup(comm,&plan->comm); } else { + + // Improved approach - use an AllReduce to aggregate which ranks need to be included + // To do this, we build the local proc's send/receive list, then do an AllReduce + // to create the send/recv count for the Alltoallv + + // local arrays to be used in the allreduce + // start with max length -- nprocs. Unused entries will be removed later + + int *local_cnts = (int*) malloc(2*nprocs*sizeof(int)); + if (local_cnts == nullptr) return nullptr; + int *local_sendcnts = local_cnts; + int *local_recvcnts = (local_cnts + nprocs); + + // local arrays used to store the results of the allreduce + + int *global_cnts = (int*) malloc(2*nprocs*sizeof(int)); + if (global_cnts == nullptr) return nullptr; + int *global_sendcnts = global_cnts; + int *global_recvcnts = (global_cnts + nprocs); + // count send & recv collides, including self nsend = 0; nrecv = 0; for (i = 0; i < nprocs; i++) { - nsend += remap_3d_collide(&in,&outarray[i],&overlap); - nrecv += remap_3d_collide(&out,&inarray[i],&overlap); + local_sendcnts[i] = remap_3d_collide(&in,&outarray[i],&overlap); + local_recvcnts[i] = remap_3d_collide(&out,&inarray[i],&overlap); + nsend += local_sendcnts[i]; + nrecv += local_recvcnts[i]; } - // malloc space for send & recv info + // perform an AllReduce to get the counts from all other processors and build sendcnts list + + MPI_Allreduce(local_cnts, global_cnts, 2*nprocs, MPI_INT, MPI_SUM, comm); - if (nsend) { + // now remove procs that are 0 in send or recv to create minimized sendcnts/recvcnts for AlltoAllv + // also builds commringlist -- which is already sorted + + int *commringlist = (int*) malloc(nprocs * sizeof(int)); + int commringlen = 0; + + for (i = 0; i < nprocs; i++) { + if (global_sendcnts[i] > 0 || global_recvcnts[i] > 0) { + commringlist[commringlen] = i; + commringlen++; + } + } + + // resize commringlist to final size + + commringlist = (int *) realloc(commringlist, commringlen*sizeof(int)); + + // set the plan->commringlist + + plan->commringlen = commringlen; + plan->commringlist = commringlist; + + // clean up local buffers that are finished + + local_sendcnts = nullptr; + local_recvcnts = nullptr; + global_recvcnts = nullptr; + global_sendcnts = nullptr; + free(local_cnts); + free(global_cnts); + + // malloc space for send & recv info + // if the current proc is involved in any way in the communication, allocate space + // because of the Alltoallv, both send and recv have to be initialized even if + // only one of those is performed + + if (nsend || nrecv) { + + // send space + + plan->nsend = nsend; plan->pack = PackKokkos::pack_3d; plan->send_offset = (int *) malloc(nsend*sizeof(int)); - plan->send_size = (int *) malloc(nsend*sizeof(int)); - plan->send_proc = (int *) malloc(nsend*sizeof(int)); + plan->send_size = (int *) malloc(plan->commringlen*sizeof(int)); + + plan->sendcnts = (int *) malloc(plan->commringlen*sizeof(int)); + plan->sdispls = (int *) malloc(plan->commringlen*sizeof(int)); + + // unused + plan->send_proc = (int *) malloc(plan->commringlen*sizeof(int)); + + // only used when sendcnt > 0 + plan->packplan = (struct pack_plan_3d *) malloc(nsend*sizeof(struct pack_plan_3d)); if (plan->send_offset == nullptr || plan->send_size == nullptr || plan->send_proc == nullptr || plan->packplan == nullptr) return nullptr; - } - if (nrecv) { + // recv space + + plan->nrecv = nrecv; + if (permute == 0) plan->unpack = PackKokkos::unpack_3d; else if (permute == 1) { @@ -550,10 +619,18 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat } plan->recv_offset = (int *) malloc(nrecv*sizeof(int)); - plan->recv_size = (int *) malloc(nrecv*sizeof(int)); + plan->recv_size = (int *) malloc(plan->commringlen*sizeof(int)); + + plan->rcvcnts = (int *) malloc(plan->commringlen*sizeof(int)); + plan->rdispls = (int *) malloc(plan->commringlen*sizeof(int)); + + // unused plan->recv_proc = (int *) malloc(nrecv*sizeof(int)); plan->recv_bufloc = (int *) malloc(nrecv*sizeof(int)); plan->request = (MPI_Request *) malloc(nrecv*sizeof(MPI_Request)); + + // only used when recvcnt > 0 + plan->unpackplan = (struct pack_plan_3d *) malloc(nrecv*sizeof(struct pack_plan_3d)); @@ -565,47 +642,56 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat // store send info, with self as last entry nsend = 0; - iproc = me; - for (i = 0; i < nprocs; i++) { - iproc++; - if (iproc == nprocs) iproc = 0; + ibuf = 0; + int total_send_size = 0; + for (i = 0; i < plan->commringlen; i++) { + iproc = plan->commringlist[i]; if (remap_3d_collide(&in,&outarray[iproc],&overlap)) { - plan->send_proc[nsend] = iproc; + //plan->send_proc[nsend] = i; + // number of entries required for this pack's 3-d coords plan->send_offset[nsend] = nqty * ((overlap.klo-in.klo)*in.jsize*in.isize + - ((overlap.jlo-in.jlo)*in.isize + overlap.ilo-in.ilo)); + ((overlap.jlo-in.jlo)*in.isize + overlap.ilo-in.ilo)); plan->packplan[nsend].nfast = nqty*overlap.isize; plan->packplan[nsend].nmid = overlap.jsize; plan->packplan[nsend].nslow = overlap.ksize; plan->packplan[nsend].nstride_line = nqty*in.isize; plan->packplan[nsend].nstride_plane = nqty*in.jsize*in.isize; plan->packplan[nsend].nqty = nqty; - plan->send_size[nsend] = nqty*overlap.isize*overlap.jsize*overlap.ksize; + // total amount of overlap + plan->send_size[i] = nqty*overlap.isize*overlap.jsize*overlap.ksize; + plan->sendcnts[i] = plan->send_size[i]; + plan->sdispls[i] = ibuf; + ibuf += plan->send_size[i]; nsend++; + } else { + plan->send_size[i] = 0; + plan->sdispls[i] = ibuf; + plan->sendcnts[i] = 0; } + total_send_size += plan->send_size[i]; + } + + if (total_send_size) { + plan->d_sendbuf = typename FFT_AT::t_FFT_SCALAR_1d("remap3d:sendbuf",total_send_size); + if (!plan->d_sendbuf.data()) return nullptr; } - - // plan->nsend = # of sends not including self - - plan->nsend = nsend; // store recv info, with self as last entry ibuf = 0; nrecv = 0; - iproc = me; - for (i = 0; i < nprocs; i++) { - iproc++; - if (iproc == nprocs) iproc = 0; + for (i = 0; i < plan->commringlen; i++) { + iproc = plan->commringlist[i]; if (remap_3d_collide(&out,&inarray[iproc],&overlap)) { - plan->recv_proc[nrecv] = iproc; + //plan->recv_proc[nrecv] = iproc; plan->recv_bufloc[nrecv] = ibuf; if (permute == 0) { plan->recv_offset[nrecv] = nqty * ((overlap.klo-out.klo)*out.jsize*out.isize + - (overlap.jlo-out.jlo)*out.isize + (overlap.ilo-out.ilo)); + (overlap.jlo-out.jlo)*out.isize + (overlap.ilo-out.ilo)); plan->unpackplan[nrecv].nfast = nqty*overlap.isize; plan->unpackplan[nrecv].nmid = overlap.jsize; plan->unpackplan[nrecv].nslow = overlap.ksize; @@ -616,7 +702,7 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat else if (permute == 1) { plan->recv_offset[nrecv] = nqty * ((overlap.ilo-out.ilo)*out.ksize*out.jsize + - (overlap.klo-out.klo)*out.jsize + (overlap.jlo-out.jlo)); + (overlap.klo-out.klo)*out.jsize + (overlap.jlo-out.jlo)); plan->unpackplan[nrecv].nfast = overlap.isize; plan->unpackplan[nrecv].nmid = overlap.jsize; plan->unpackplan[nrecv].nslow = overlap.ksize; @@ -627,7 +713,7 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat else { plan->recv_offset[nrecv] = nqty * ((overlap.jlo-out.jlo)*out.isize*out.ksize + - (overlap.ilo-out.ilo)*out.ksize + (overlap.klo-out.klo)); + (overlap.ilo-out.ilo)*out.ksize + (overlap.klo-out.klo)); plan->unpackplan[nrecv].nfast = overlap.isize; plan->unpackplan[nrecv].nmid = overlap.jsize; plan->unpackplan[nrecv].nslow = overlap.ksize; @@ -636,192 +722,26 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat plan->unpackplan[nrecv].nqty = nqty; } - plan->recv_size[nrecv] = nqty*overlap.isize*overlap.jsize*overlap.ksize; - ibuf += plan->recv_size[nrecv]; + plan->recv_size[i] = nqty*overlap.isize*overlap.jsize*overlap.ksize; + plan->rcvcnts[i] = plan->recv_size[i]; + plan->rdispls[i] = ibuf; + ibuf += plan->recv_size[i]; nrecv++; + } else { + plan->recv_size[i] = 0; + plan->rcvcnts[i] = 0; + plan->rdispls[i] = ibuf; } } - - // plan->nrecv = # of recvs not including self - // for collectives include self in the nsend list - - plan->nrecv = nrecv; - - // create sub-comm rank list - - plan->commringlist = nullptr; - - // merge recv and send rank lists - // ask Steve Plimpton about method to more accurately determine - // maximum number of procs contributing to pencil - - int maxcommsize = nprocs; - int *commringlist = (int *) malloc(maxcommsize*sizeof(int)); - int commringlen = 0; - - for (i = 0; i < nrecv; i++) { - commringlist[i] = plan->recv_proc[i]; - commringlen++; - } - - for (i = 0; i < nsend; i++) { - int foundentry = 0; - for (j = 0; j < commringlen;j++) - if (commringlist[j] == plan->send_proc[i]) foundentry = 1; - if (!foundentry) { - commringlist[commringlen] = plan->send_proc[i]; - commringlen++; - } - } - - // sort initial commringlist - - int swap = 0; - for (i = 0 ; i < (commringlen - 1); i++) { - for (j = 0 ; j < commringlen - i - 1; j++) { - if (commringlist[j] > commringlist[j+1]) { - swap = commringlist[j]; - commringlist[j] = commringlist[j+1]; - commringlist[j+1] = swap; - } - } - } - - // collide all inarray extents for the comm ring with all output - // extents and all outarray extents for the comm ring with all input - // extents - if there is a collison add the rank to the comm ring, - // keep iterating until nothing is added to commring - - int commringappend = 1; - while (commringappend) { - int newcommringlen = commringlen; - commringappend = 0; - for (i = 0; i < commringlen; i++) { - for (j = 0; j < nprocs; j++) { - if (remap_3d_collide(&inarray[commringlist[i]], - &outarray[j],&overlap)) { - int alreadyinlist = 0; - for (int k = 0; k < newcommringlen; k++) { - if (commringlist[k] == j) { - alreadyinlist = 1; - } - } - if (!alreadyinlist) { - commringlist[newcommringlen++] = j; - commringappend = 1; - } - } - if (remap_3d_collide(&outarray[commringlist[i]], - &inarray[j],&overlap)) { - int alreadyinlist = 0; - for (int k = 0 ; k < newcommringlen; k++) { - if (commringlist[k] == j) alreadyinlist = 1; - } - if (!alreadyinlist) { - commringlist[newcommringlen++] = j; - commringappend = 1; - } - } - } - } - commringlen = newcommringlen; - } - - // sort the final commringlist - - for (i = 0 ; i < ( commringlen - 1 ); i++) { - for (j = 0 ; j < commringlen - i - 1; j++) { - if (commringlist[j] > commringlist[j+1]) { - swap = commringlist[j]; - commringlist[j] = commringlist[j+1]; - commringlist[j+1] = swap; - } - } - } - - // resize commringlist to final size - - commringlist = (int *) realloc(commringlist, commringlen*sizeof(int)); - - // set the plan->commringlist - - plan->commringlen = commringlen; - plan->commringlist = commringlist; - + // init remaining fields in remap plan - + plan->memory = memory; - - if (nrecv == plan->nrecv) plan->self = 0; - else plan->self = 1; - - // the plan->d_sendbuf and plan->d_recvbuf are used by both the - // collective & non-collective implementations. - // For non-collective, the buffer size is MAX(send_size) for any one send - // For collective, the buffer size is SUM(send_size) for all sends - - // allocate buffer for all send messages (including self) - // the method to allocate receive scratch space is sufficient - // for collectives - - size = 0; - for (nsend = 0; nsend < plan->nsend; nsend++) - size += plan->send_size[nsend]; - - if (size) { - plan->d_sendbuf = typename FFT_AT::t_FFT_SCALAR_1d("remap3d:sendbuf",size); - if (!plan->d_sendbuf.data()) return nullptr; - } - - // allocate buffers for send and receive counts, displacements - - if (plan->commringlen) { - plan->sendcnts = (int *) malloc(sizeof(int) * plan->commringlen); - plan->rcvcnts = (int *) malloc(sizeof(int) * plan->commringlen); - plan->sdispls = (int *) malloc(sizeof(int) * plan->commringlen); - plan->rdispls = (int *) malloc(sizeof(int) * plan->commringlen); - plan->nrecvmap = (int *) malloc(sizeof(int) * plan->commringlen); - - // populate buffers for send counts & displacements - - int currentSendBufferOffset = 0; - for (isend = 0; isend < plan->commringlen; isend++) { - plan->sendcnts[isend] = 0; - plan->sdispls[isend] = 0; - int foundentry = 0; - for (int i=0;(insend && !foundentry); i++) { - if (plan->send_proc[i] == plan->commringlist[isend]) { - foundentry = 1; - plan->sendcnts[isend] = plan->send_size[i]; - plan->sdispls[isend] = currentSendBufferOffset; - currentSendBufferOffset += plan->send_size[i]; - } - } - } - - // populate buffers for recv counts & displacements - - int currentRecvBufferOffset = 0; - for (irecv = 0; irecv < plan->commringlen; irecv++) { - plan->rcvcnts[irecv] = 0; - plan->rdispls[irecv] = 0; - plan->nrecvmap[irecv] = -1; - int foundentry = 0; - for (int i=0;(inrecv && !foundentry); i++) { - if (plan->recv_proc[i] == plan->commringlist[irecv]) { - foundentry = 1; - plan->rcvcnts[irecv] = plan->recv_size[i]; - plan->rdispls[irecv] = currentRecvBufferOffset; - currentRecvBufferOffset += plan->recv_size[i]; - plan->nrecvmap[irecv] = i; - } - } - } - } - + plan->self = 0; + // if requested, allocate internal scratch space for recvs, // only need it if I will receive any data (including self) - + if (memory == 1) { if (nrecv > 0) { plan->d_scratch = @@ -829,25 +749,24 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat if (!plan->d_scratch.data()) return nullptr; } } - + // if using collective and the commringlist is NOT empty create a // communicator for the plan based off an MPI_Group created with // ranks from the commringlist - + if (plan->commringlen > 0) { MPI_Group orig_group, new_group; MPI_Comm_group(comm, &orig_group); MPI_Group_incl(orig_group, plan->commringlen, - plan->commringlist, &new_group); + plan->commringlist, &new_group); MPI_Comm_create(comm, new_group, &plan->comm); } - + // if using collective and the comm ring list is empty create // a communicator for the plan with an empty group - + else MPI_Comm_create(comm, MPI_GROUP_EMPTY, &plan->comm); - } // free locally malloced space @@ -881,7 +800,7 @@ void RemapKokkos::remap_3d_destroy_plan_kokkos(struct remap_plan_3d_ free(plan->rcvcnts); free(plan->sdispls); free(plan->rdispls); - free(plan->nrecvmap); + //free(plan->nrecvmap); } } From 3f9d96d38db993c6dbc91145017d4b9fe0bc3b79 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 24 Apr 2024 01:55:13 -0400 Subject: [PATCH 012/355] make pip install packages in virtual environment --- .github/workflows/unittest-macos.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/unittest-macos.yml b/.github/workflows/unittest-macos.yml index f9c2a838d6..0a9d31bd84 100644 --- a/.github/workflows/unittest-macos.yml +++ b/.github/workflows/unittest-macos.yml @@ -43,6 +43,8 @@ jobs: working-directory: build run: | ccache -z + python3 -m venv macosenv + source macosenv/bin/activate python3 -m pip install numpy python3 -m pip install pyyaml cmake -C ../cmake/presets/clang.cmake \ From 8c3dab03b7969d3c52291f1407e15608393de2bd Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 24 Apr 2024 02:45:36 -0400 Subject: [PATCH 013/355] downgrade macOS to version 13 --- .github/workflows/unittest-macos.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unittest-macos.yml b/.github/workflows/unittest-macos.yml index 0a9d31bd84..b0bc4b2727 100644 --- a/.github/workflows/unittest-macos.yml +++ b/.github/workflows/unittest-macos.yml @@ -15,7 +15,7 @@ jobs: build: name: MacOS Unit Test if: ${{ github.repository == 'lammps/lammps' }} - runs-on: macos-latest + runs-on: macos-13 env: CCACHE_DIR: ${{ github.workspace }}/.ccache From bd52e31128f19b6f7b87200f13f8ac9f8f8f9db6 Mon Sep 17 00:00:00 2001 From: Nick Hagerty Date: Sat, 11 May 2024 20:58:47 -0400 Subject: [PATCH 014/355] Added optimization to collective to local copy to self --- src/KOKKOS/remap_kokkos.cpp | 46 ++++++++++++++++++++++++++++++++++--- src/KOKKOS/remap_kokkos.h | 3 +++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/KOKKOS/remap_kokkos.cpp b/src/KOKKOS/remap_kokkos.cpp index fafb3dc37b..39b3aff4fa 100644 --- a/src/KOKKOS/remap_kokkos.cpp +++ b/src/KOKKOS/remap_kokkos.cpp @@ -105,6 +105,8 @@ void RemapKokkos::remap_3d_kokkos(typename FFT_AT::t_FFT_SCALAR_1d d { typename FFT_AT::t_FFT_SCALAR_1d d_scratch; + int me; + MPI_Comm_rank(plan->comm,&me); if (plan->memory == 0) d_scratch = d_buf; @@ -191,13 +193,17 @@ void RemapKokkos::remap_3d_kokkos(typename FFT_AT::t_FFT_SCALAR_1d d int numpacked = 0; for (isend = 0; isend < plan->commringlen; isend++) { - if (plan->sendcnts[isend] > 0) { + if (plan->sendcnts[isend]) { plan->pack(d_in,plan->send_offset[numpacked], plan->d_sendbuf,plan->sdispls[isend], &plan->packplan[numpacked]); numpacked++; } + else if (plan->commringlist[isend] == me && plan->self) { + numpacked++; + } } + if (!plan->usegpu_aware) Kokkos::deep_copy(plan->h_sendbuf,plan->d_sendbuf); @@ -210,14 +216,28 @@ void RemapKokkos::remap_3d_kokkos(typename FFT_AT::t_FFT_SCALAR_1d d if (!plan->usegpu_aware) Kokkos::deep_copy(d_scratch,plan->h_scratch); + // copy in -> scratch -> out for self data + + if (plan->self) { + plan->pack(d_in,plan->send_offset[plan->selfnsendloc], + plan->d_sendbuf,plan->sdispls[plan->selfcommringloc], + &plan->packplan[plan->selfnsendloc]); + plan->unpack(plan->d_sendbuf,plan->sdispls[plan->selfcommringloc], + d_out,plan->recv_offset[plan->selfnrecvloc], + &plan->unpackplan[plan->selfnrecvloc]); + } + numpacked = 0; for (irecv = 0; irecv < plan->commringlen; irecv++) { - if (plan->rcvcnts[irecv] > 0) { + if (plan->rcvcnts[irecv]) { plan->unpack(d_scratch,plan->rdispls[irecv], d_out,plan->recv_offset[numpacked], &plan->unpackplan[numpacked]); numpacked++; } + else if (plan->commringlist[irecv] == me && plan->self) { + numpacked++; + } } } } @@ -575,6 +595,10 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat // send space + plan->selfcommringloc = -1; + plan->selfnsendloc = -1; + plan->selfnrecvloc = -1; + plan->nsend = nsend; plan->pack = PackKokkos::pack_3d; @@ -646,6 +670,10 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat int total_send_size = 0; for (i = 0; i < plan->commringlen; i++) { iproc = plan->commringlist[i]; + if (iproc == me) { + plan->selfcommringloc = i; + plan->selfnsendloc = nsend; + } if (remap_3d_collide(&in,&outarray[iproc],&overlap)) { //plan->send_proc[nsend] = i; // number of entries required for this pack's 3-d coords @@ -684,6 +712,9 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat for (i = 0; i < plan->commringlen; i++) { iproc = plan->commringlist[i]; + if (iproc == me) { + plan->selfnrecvloc = nrecv; + } if (remap_3d_collide(&out,&inarray[iproc],&overlap)) { //plan->recv_proc[nrecv] = iproc; plan->recv_bufloc[nrecv] = ibuf; @@ -737,7 +768,16 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat // init remaining fields in remap plan plan->memory = memory; - plan->self = 0; + //plan->self = 0; + if (plan->sendcnts[plan->selfcommringloc]) { + plan->self = 1; + plan->sendcnts[plan->selfcommringloc] = 0; + plan->rcvcnts[plan->selfcommringloc] = 0; + } + else { + plan->self = 0; + } + // if requested, allocate internal scratch space for recvs, // only need it if I will receive any data (including self) diff --git a/src/KOKKOS/remap_kokkos.h b/src/KOKKOS/remap_kokkos.h index 9dbd8d683c..737b45a398 100644 --- a/src/KOKKOS/remap_kokkos.h +++ b/src/KOKKOS/remap_kokkos.h @@ -61,6 +61,9 @@ struct remap_plan_3d_kokkos { int *sdispls; // extraction location in send buffer for each rank int *rdispls; // extraction location in recv buffer for each rank int *nrecvmap; // maps receive index to rank index + int selfcommringloc; + int selfnsendloc; + int selfnrecvloc; }; template From c9049c090dc8d7a6b5d59d8517739e20861162b8 Mon Sep 17 00:00:00 2001 From: Nick Hagerty Date: Mon, 13 May 2024 15:47:25 -0400 Subject: [PATCH 015/355] Cleaned up memory deallocation logic, improved comments on remap_kokkos.h for collective fields --- src/KOKKOS/remap_kokkos.cpp | 54 ++++++++++++++++++++----------------- src/KOKKOS/remap_kokkos.h | 10 +++---- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/KOKKOS/remap_kokkos.cpp b/src/KOKKOS/remap_kokkos.cpp index 39b3aff4fa..7ebe35dddb 100644 --- a/src/KOKKOS/remap_kokkos.cpp +++ b/src/KOKKOS/remap_kokkos.cpp @@ -608,9 +608,6 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat plan->sendcnts = (int *) malloc(plan->commringlen*sizeof(int)); plan->sdispls = (int *) malloc(plan->commringlen*sizeof(int)); - // unused - plan->send_proc = (int *) malloc(plan->commringlen*sizeof(int)); - // only used when sendcnt > 0 plan->packplan = (struct pack_plan_3d *) @@ -648,11 +645,6 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat plan->rcvcnts = (int *) malloc(plan->commringlen*sizeof(int)); plan->rdispls = (int *) malloc(plan->commringlen*sizeof(int)); - // unused - plan->recv_proc = (int *) malloc(nrecv*sizeof(int)); - plan->recv_bufloc = (int *) malloc(nrecv*sizeof(int)); - plan->request = (MPI_Request *) malloc(nrecv*sizeof(MPI_Request)); - // only used when recvcnt > 0 plan->unpackplan = (struct pack_plan_3d *) @@ -768,7 +760,7 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat // init remaining fields in remap plan plan->memory = memory; - //plan->self = 0; + if (plan->sendcnts[plan->selfcommringloc]) { plan->self = 1; plan->sendcnts[plan->selfcommringloc] = 0; @@ -840,26 +832,38 @@ void RemapKokkos::remap_3d_destroy_plan_kokkos(struct remap_plan_3d_ free(plan->rcvcnts); free(plan->sdispls); free(plan->rdispls); - //free(plan->nrecvmap); } - } - // free internal arrays + if (plan->nsend) { + free(plan->send_offset); + free(plan->send_size); + free(plan->packplan); + } - if (plan->nsend || plan->self) { - free(plan->send_offset); - free(plan->send_size); - free(plan->send_proc); - free(plan->packplan); - } + if (plan->nrecv) { + free(plan->recv_offset); + free(plan->recv_size); + free(plan->unpackplan); + } + } else { + + // free arrays used in pt2pt communication - if (plan->nrecv || plan->self) { - free(plan->recv_offset); - free(plan->recv_size); - free(plan->recv_proc); - free(plan->recv_bufloc); - free(plan->request); - free(plan->unpackplan); + if (plan->nsend || plan->self) { + free(plan->send_offset); + free(plan->send_size); + free(plan->send_proc); + free(plan->packplan); + } + + if (plan->nrecv || plan->self) { + free(plan->recv_offset); + free(plan->recv_size); + free(plan->recv_proc); + free(plan->recv_bufloc); + free(plan->request); + free(plan->unpackplan); + } } // free plan itself diff --git a/src/KOKKOS/remap_kokkos.h b/src/KOKKOS/remap_kokkos.h index 737b45a398..b0ccdb342d 100644 --- a/src/KOKKOS/remap_kokkos.h +++ b/src/KOKKOS/remap_kokkos.h @@ -44,6 +44,7 @@ struct remap_plan_3d_kokkos { int *recv_size; // size of each recv message int *recv_proc; // proc to recv each message from int *recv_bufloc; // offset in scratch buf for each recv + int *nrecvmap; // maps receive index to rank index MPI_Request *request; // MPI request for each posted recv struct pack_plan_3d *unpackplan; // unpack plan for each recv message int nrecv; // # of recvs from other procs @@ -53,17 +54,16 @@ struct remap_plan_3d_kokkos { MPI_Comm comm; // group of procs performing remap int usecollective; // use collective or point-to-point MPI int usegpu_aware; // use GPU-Aware MPI or not - // variables specific to collective MPI + // variables for collective MPI only int commringlen; // length of commringlist int *commringlist; // ranks on communication ring of this plan int *sendcnts; // # of elements in send buffer for each rank int *rcvcnts; // # of elements in recv buffer for each rank int *sdispls; // extraction location in send buffer for each rank int *rdispls; // extraction location in recv buffer for each rank - int *nrecvmap; // maps receive index to rank index - int selfcommringloc; - int selfnsendloc; - int selfnrecvloc; + int selfcommringloc; // current proc's location in commringlist + int selfnsendloc; // current proc's location in send lists + int selfnrecvloc; // current proc's location in recv lists }; template From 7da7e69ccb9594d763b0bdcce5c540d9658e758f Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Wed, 26 Jun 2024 15:14:55 -0400 Subject: [PATCH 016/355] Fix integer overflow for large ReaxFF systems with KOKKOS package --- src/KOKKOS/fix_acks2_reaxff_kokkos.cpp | 70 +++++++++++++------------- src/KOKKOS/fix_acks2_reaxff_kokkos.h | 27 +++++----- src/KOKKOS/fix_qeq_reaxff_kokkos.cpp | 41 +++++++-------- src/KOKKOS/fix_qeq_reaxff_kokkos.h | 19 +++---- src/KOKKOS/kokkos.cpp | 35 +++++++------ src/KOKKOS/kokkos.h | 2 +- src/KOKKOS/kokkos_type.h | 29 +++++++++++ 7 files changed, 128 insertions(+), 95 deletions(-) diff --git a/src/KOKKOS/fix_acks2_reaxff_kokkos.cpp b/src/KOKKOS/fix_acks2_reaxff_kokkos.cpp index 308df20c0e..c0b263d736 100644 --- a/src/KOKKOS/fix_acks2_reaxff_kokkos.cpp +++ b/src/KOKKOS/fix_acks2_reaxff_kokkos.cpp @@ -53,7 +53,8 @@ FixACKS2ReaxFFKokkos(LAMMPS *lmp, int narg, char **arg) : datamask_read = X_MASK | V_MASK | F_MASK | MASK_MASK | Q_MASK | TYPE_MASK | TAG_MASK; datamask_modify = Q_MASK | X_MASK; - nmax = m_cap = 0; + nmax = 0; + m_cap_big = 0; allocated_flag = 0; nprev = 4; @@ -66,7 +67,7 @@ FixACKS2ReaxFFKokkos(LAMMPS *lmp, int narg, char **arg) : buf = new double[2*nprev]; prev_last_rows_rank = 0; - d_mfill_offset = typename AT::t_int_scalar("acks2/kk:mfill_offset"); + d_mfill_offset = typename AT::t_bigint_scalar("acks2/kk:mfill_offset"); } /* ---------------------------------------------------------------------- */ @@ -418,10 +419,10 @@ void FixACKS2ReaxFFKokkos::pre_force(int /*vflag*/) template KOKKOS_INLINE_FUNCTION -void FixACKS2ReaxFFKokkos::num_neigh_item(int ii, int &maxneigh) const +void FixACKS2ReaxFFKokkos::num_neigh_item(int ii, bigint &totneigh) const { const int i = d_ilist[ii]; - maxneigh += d_numneigh[i]; + totneigh += d_numneigh[i]; } /* ---------------------------------------------------------------------- */ @@ -433,39 +434,39 @@ void FixACKS2ReaxFFKokkos::allocate_matrix() // determine the total space for the H matrix - m_cap = 0; + m_cap_big = 0; // limit scope of functor to allow deallocation of views { FixACKS2ReaxFFKokkosNumNeighFunctor neigh_functor(this); - Kokkos::parallel_reduce(nn,neigh_functor,m_cap); + Kokkos::parallel_reduce(nn,neigh_functor,m_cap_big); } // deallocate first to reduce memory overhead - d_firstnbr = typename AT::t_int_1d(); + d_firstnbr = typename AT::t_bigint_1d(); d_numnbrs = typename AT::t_int_1d(); d_jlist = typename AT::t_int_1d(); d_val = typename AT::t_ffloat_1d(); - d_firstnbr_X = typename AT::t_int_1d(); + d_firstnbr_X = typename AT::t_bigint_1d(); d_numnbrs_X = typename AT::t_int_1d(); d_jlist_X = typename AT::t_int_1d(); d_val_X = typename AT::t_ffloat_1d(); // H matrix - d_firstnbr = typename AT::t_int_1d("acks2/kk:firstnbr",nmax); + d_firstnbr = typename AT::t_bigint_1d("acks2/kk:firstnbr",nmax); d_numnbrs = typename AT::t_int_1d("acks2/kk:numnbrs",nmax); - d_jlist = typename AT::t_int_1d("acks2/kk:jlist",m_cap); - d_val = typename AT::t_ffloat_1d("acks2/kk:val",m_cap); + d_jlist = typename AT::t_int_1d("acks2/kk:jlist",m_cap_big); + d_val = typename AT::t_ffloat_1d("acks2/kk:val",m_cap_big); // X matrix - d_firstnbr_X = typename AT::t_int_1d("acks2/kk:firstnbr_X",nmax); + d_firstnbr_X = typename AT::t_bigint_1d("acks2/kk:firstnbr_X",nmax); d_numnbrs_X = typename AT::t_int_1d("acks2/kk:numnbrs_X",nmax); - d_jlist_X = typename AT::t_int_1d("acks2/kk:jlist_X",m_cap); - d_val_X = typename AT::t_ffloat_1d("acks2/kk:val_X",m_cap); + d_jlist_X = typename AT::t_int_1d("acks2/kk:jlist_X",m_cap_big); + d_val_X = typename AT::t_ffloat_1d("acks2/kk:val_X",m_cap_big); } /* ---------------------------------------------------------------------- */ @@ -566,7 +567,7 @@ void FixACKS2ReaxFFKokkos::operator() (TagACKS2Zero, const int &ii) template template KOKKOS_INLINE_FUNCTION -void FixACKS2ReaxFFKokkos::compute_h_item(int ii, int &m_fill, const bool &final) const +void FixACKS2ReaxFFKokkos::compute_h_item(int ii, bigint &m_fill, const bool &final) const { const int i = d_ilist[ii]; int j,jj,jtype; @@ -619,7 +620,7 @@ void FixACKS2ReaxFFKokkos::compute_h_item(int ii, int &m_fill, const m_fill++; } if (final) - d_numnbrs[i] = m_fill - d_firstnbr[i]; + d_numnbrs[i] = int(m_fill - d_firstnbr[i]); } } @@ -698,9 +699,9 @@ void FixACKS2ReaxFFKokkos::compute_h_team( // calculate the global memory offset from where the H matrix values to be // calculated by the current team will be stored in d_val - int team_firstnbr_idx = 0; + bigint team_firstnbr_idx = 0; Kokkos::single(Kokkos::PerTeam(team), - [=](int &val) { + [=](bigint &val) { int totalnbrs = s_firstnbr[lastatom - firstatom - 1] + s_numnbrs[lastatom - firstatom - 1]; val = Kokkos::atomic_fetch_add(&d_mfill_offset(), totalnbrs); @@ -726,7 +727,7 @@ void FixACKS2ReaxFFKokkos::compute_h_team( int jnum = s_numnbrs[idx]; // calculate the write-offset for atom-i's first neighbor - int atomi_firstnbr_idx = team_firstnbr_idx + s_firstnbr[idx]; + bigint atomi_firstnbr_idx = team_firstnbr_idx + s_firstnbr[idx]; Kokkos::single(Kokkos::PerThread(team), [&]() { d_firstnbr[i] = atomi_firstnbr_idx; }); @@ -739,7 +740,7 @@ void FixACKS2ReaxFFKokkos::compute_h_team( // are processed in batches and the batch size is vector_length for (int jj_start = 0; jj_start < jnum; jj_start += vector_length) { - int atomi_nbr_writeIdx = atomi_firstnbr_idx + atomi_nbrs_inH; + bigint atomi_nbr_writeIdx = atomi_firstnbr_idx + atomi_nbrs_inH; // count the # of neighbor atoms with non-zero electrostatic // interaction coefficients with atom-i in the current batch @@ -782,7 +783,8 @@ void FixACKS2ReaxFFKokkos::compute_h_team( valid = false; if (x(j, 2) == ztmp && x(j, 1) < ytmp) valid = false; - if (x(j, 2) == ztmp && x(j, 1) == ytmp && x(j, 0) < xtmp) + if (x(j, 2) == ztmp && x(j, 1) == ytmp && + x(j, 0) < xtmp) valid = false; } } @@ -851,7 +853,7 @@ double FixACKS2ReaxFFKokkos::calculate_H_k(const F_FLOAT &r, const F taper = taper * r + d_tap[0]; denom = r * r * r + shld; - denom = pow(denom,1.0/3.0); + denom = cbrt(denom); return taper * EV_TO_KCAL_PER_MOL / denom; } @@ -861,7 +863,7 @@ double FixACKS2ReaxFFKokkos::calculate_H_k(const F_FLOAT &r, const F template template KOKKOS_INLINE_FUNCTION -void FixACKS2ReaxFFKokkos::compute_x_item(int ii, int &m_fill, const bool &final) const +void FixACKS2ReaxFFKokkos::compute_x_item(int ii, bigint &m_fill, const bool &final) const { // The X_diag array is duplicated for OpenMP, atomic for GPU, and neither for Serial auto v_X_diag = ScatterViewHelper,decltype(dup_X_diag),decltype(ndup_X_diag)>::get(dup_X_diag,ndup_X_diag); @@ -927,7 +929,7 @@ void FixACKS2ReaxFFKokkos::compute_x_item(int ii, int &m_fill, const } if (final) { a_X_diag[i] += tmp; - d_numnbrs_X[i] = m_fill - d_firstnbr_X[i]; + d_numnbrs_X[i] = int(m_fill - d_firstnbr_X[i]); } } } @@ -1005,9 +1007,9 @@ void FixACKS2ReaxFFKokkos::compute_x_team( // calculate the global memory offset from where the H matrix values to be // calculated by the current team will be stored in d_val_X - int team_firstnbr_idx = 0; + bigint team_firstnbr_idx = 0; Kokkos::single(Kokkos::PerTeam(team), - [=](int &val) { + [=](bigint &val) { int totalnbrs = s_firstnbr[lastatom - firstatom - 1] + s_numnbrs[lastatom - firstatom - 1]; val = Kokkos::atomic_fetch_add(&d_mfill_offset(), totalnbrs); @@ -1033,7 +1035,7 @@ void FixACKS2ReaxFFKokkos::compute_x_team( int jnum = s_numnbrs[idx]; // calculate the write-offset for atom-i's first neighbor - int atomi_firstnbr_idx = team_firstnbr_idx + s_firstnbr[idx]; + bigint atomi_firstnbr_idx = team_firstnbr_idx + s_firstnbr[idx]; Kokkos::single(Kokkos::PerThread(team), [&]() { d_firstnbr_X[i] = atomi_firstnbr_idx; }); @@ -1046,7 +1048,7 @@ void FixACKS2ReaxFFKokkos::compute_x_team( // are processed in batches and the batch size is vector_length for (int jj_start = 0; jj_start < jnum; jj_start += vector_length) { - int atomi_nbr_writeIdx = atomi_firstnbr_idx + atomi_nbrs_inH; + bigint atomi_nbr_writeIdx = atomi_firstnbr_idx + atomi_nbrs_inH; // count the # of neighbor atoms with non-zero electrostatic // interaction coefficients with atom-i in the current batch @@ -1464,7 +1466,7 @@ void FixACKS2ReaxFFKokkos::operator() (TagACKS2SparseMatvec3_Half::operator() (TagACKS2SparseMatvec3_Half::operator() (TagACKS2SparseMatvec3_Full, c F_FLOAT sum; F_FLOAT sum2; - Kokkos::parallel_reduce(Kokkos::TeamThreadRange(team, d_firstnbr[i], d_firstnbr[i] + d_numnbrs[i]), [&] (const int &jj, F_FLOAT &sum) { + Kokkos::parallel_reduce(Kokkos::TeamThreadRange(team, d_firstnbr[i], d_firstnbr[i] + d_numnbrs[i]), [&] (const bigint &jj, F_FLOAT &sum) { const int j = d_jlist(jj); sum += d_val(jj) * d_xx[j]; }, sum); team.team_barrier(); - Kokkos::parallel_reduce(Kokkos::TeamThreadRange(team, d_firstnbr_X[i], d_firstnbr_X[i] + d_numnbrs_X[i]), [&] (const int &jj, F_FLOAT &sum2) { + Kokkos::parallel_reduce(Kokkos::TeamThreadRange(team, d_firstnbr_X[i], d_firstnbr_X[i] + d_numnbrs_X[i]), [&] (const bigint &jj, F_FLOAT &sum2) { const int j = d_jlist_X(jj); sum2 += d_val_X(jj) * d_xx[NN + j]; }, sum2); @@ -1865,8 +1867,8 @@ double FixACKS2ReaxFFKokkos::memory_usage() bytes += nmax*4 * sizeof(double); // storage bytes += size*11 * sizeof(double); // storage bytes += n_cap*4 * sizeof(int); // matrix... - bytes += m_cap*2 * sizeof(int); - bytes += m_cap*2 * sizeof(double); + bytes += m_cap_big*2 * sizeof(int); + bytes += m_cap_big*2 * sizeof(double); return bytes; } diff --git a/src/KOKKOS/fix_acks2_reaxff_kokkos.h b/src/KOKKOS/fix_acks2_reaxff_kokkos.h index cb16b4cd24..6adca39d17 100644 --- a/src/KOKKOS/fix_acks2_reaxff_kokkos.h +++ b/src/KOKKOS/fix_acks2_reaxff_kokkos.h @@ -74,7 +74,7 @@ class FixACKS2ReaxFFKokkos : public FixACKS2ReaxFF, public KokkosBase { DAT::tdual_ffloat_1d get_s() {return k_s;} KOKKOS_INLINE_FUNCTION - void num_neigh_item(int, int&) const; + void num_neigh_item(int, bigint&) const; KOKKOS_INLINE_FUNCTION void operator()(TagACKS2Zero, const int&) const; @@ -84,7 +84,7 @@ class FixACKS2ReaxFFKokkos : public FixACKS2ReaxFF, public KokkosBase { template KOKKOS_INLINE_FUNCTION - void compute_h_item(int, int &, const bool &) const; + void compute_h_item(int, bigint &, const bool &) const; template KOKKOS_INLINE_FUNCTION @@ -92,7 +92,7 @@ class FixACKS2ReaxFFKokkos : public FixACKS2ReaxFF, public KokkosBase { template KOKKOS_INLINE_FUNCTION - void compute_x_item(int, int &, const bool &) const; + void compute_x_item(int, bigint &, const bool &) const; template KOKKOS_INLINE_FUNCTION @@ -173,8 +173,9 @@ class FixACKS2ReaxFFKokkos : public FixACKS2ReaxFF, public KokkosBase { int allocated_flag, last_allocate; int need_dup,prev_last_rows_rank; double* buf; + bigint m_cap_big; - typename AT::t_int_scalar d_mfill_offset; + typename AT::t_bigint_scalar d_mfill_offset; typedef Kokkos::DualView tdual_int_1d; Kokkos::DualView k_params; @@ -197,12 +198,12 @@ class FixACKS2ReaxFFKokkos : public FixACKS2ReaxFF, public KokkosBase { DAT::tdual_ffloat_2d k_bcut; typename AT::t_ffloat_2d d_bcut; - typename AT::t_int_1d d_firstnbr; + typename AT::t_bigint_1d d_firstnbr; typename AT::t_int_1d d_numnbrs; typename AT::t_int_1d d_jlist; typename AT::t_ffloat_1d d_val; - typename AT::t_int_1d d_firstnbr_X; + typename AT::t_bigint_1d d_firstnbr_X; typename AT::t_int_1d d_numnbrs_X; typename AT::t_int_1d d_jlist_X; typename AT::t_ffloat_1d d_val_X; @@ -264,21 +265,21 @@ class FixACKS2ReaxFFKokkos : public FixACKS2ReaxFF, public KokkosBase { template struct FixACKS2ReaxFFKokkosNumNeighFunctor { typedef DeviceType device_type; - typedef int value_type; + typedef bigint value_type; FixACKS2ReaxFFKokkos c; FixACKS2ReaxFFKokkosNumNeighFunctor(FixACKS2ReaxFFKokkos* c_ptr):c(*c_ptr) { c.cleanup_copy(); }; KOKKOS_INLINE_FUNCTION - void operator()(const int ii, int &maxneigh) const { - c.num_neigh_item(ii, maxneigh); + void operator()(const int ii, bigint &totneigh) const { + c.num_neigh_item(ii, totneigh); } }; template struct FixACKS2ReaxFFKokkosComputeHFunctor { int atoms_per_team, vector_length; - typedef int value_type; + typedef bigint value_type; typedef Kokkos::ScratchMemorySpace scratch_space; FixACKS2ReaxFFKokkos c; @@ -293,7 +294,7 @@ struct FixACKS2ReaxFFKokkosComputeHFunctor { }; KOKKOS_INLINE_FUNCTION - void operator()(const int ii, int &m_fill, const bool &final) const { + void operator()(const int ii, bigint &m_fill, const bool &final) const { c.template compute_h_item(ii,m_fill,final); } @@ -325,7 +326,7 @@ struct FixACKS2ReaxFFKokkosComputeHFunctor { template struct FixACKS2ReaxFFKokkosComputeXFunctor { int atoms_per_team, vector_length; - typedef int value_type; + typedef bigint value_type; typedef Kokkos::ScratchMemorySpace scratch_space; FixACKS2ReaxFFKokkos c; @@ -340,7 +341,7 @@ struct FixACKS2ReaxFFKokkosComputeXFunctor { }; KOKKOS_INLINE_FUNCTION - void operator()(const int ii, int &m_fill, const bool &final) const { + void operator()(const int ii, bigint &m_fill, const bool &final) const { c.template compute_x_item(ii,m_fill,final); } diff --git a/src/KOKKOS/fix_qeq_reaxff_kokkos.cpp b/src/KOKKOS/fix_qeq_reaxff_kokkos.cpp index deb41944bc..f93f6cb70e 100644 --- a/src/KOKKOS/fix_qeq_reaxff_kokkos.cpp +++ b/src/KOKKOS/fix_qeq_reaxff_kokkos.cpp @@ -62,7 +62,8 @@ FixQEqReaxFFKokkos(LAMMPS *lmp, int narg, char **arg) : datamask_read = X_MASK | V_MASK | F_MASK | Q_MASK | MASK_MASK | TYPE_MASK | TAG_MASK; datamask_modify = X_MASK; - nmax = m_cap = 0; + nmax = 0; + m_cap_big = 0; allocated_flag = 0; nprev = 4; maxexchange = nprev*2; @@ -71,7 +72,7 @@ FixQEqReaxFFKokkos(LAMMPS *lmp, int narg, char **arg) : memory->destroy(t_hist); grow_arrays(atom->nmax); - d_mfill_offset = typename AT::t_int_scalar("qeq/kk:mfill_offset"); + d_mfill_offset = typename AT::t_bigint_scalar("qeq/kk:mfill_offset"); converged = 0; } @@ -301,10 +302,10 @@ void FixQEqReaxFFKokkos::pre_force(int /*vflag*/) template KOKKOS_INLINE_FUNCTION -void FixQEqReaxFFKokkos::num_neigh_item(int ii, int &maxneigh) const +void FixQEqReaxFFKokkos::num_neigh_item(int ii, bigint &totneigh) const { const int i = d_ilist[ii]; - maxneigh += d_numneigh[i]; + totneigh += d_numneigh[i]; } /* ---------------------------------------------------------------------- */ @@ -316,25 +317,25 @@ void FixQEqReaxFFKokkos::allocate_matrix() // determine the total space for the H matrix - m_cap = 0; + m_cap_big = 0; // limit scope of functor to allow deallocation of views { FixQEqReaxFFKokkosNumNeighFunctor neigh_functor(this); - Kokkos::parallel_reduce(nn,neigh_functor,m_cap); + Kokkos::parallel_reduce(nn,neigh_functor,m_cap_big); } // deallocate first to reduce memory overhead - d_firstnbr = typename AT::t_int_1d(); + d_firstnbr = typename AT::t_bigint_1d(); d_numnbrs = typename AT::t_int_1d(); d_jlist = typename AT::t_int_1d(); d_val = typename AT::t_ffloat_1d(); - d_firstnbr = typename AT::t_int_1d("qeq/kk:firstnbr",nmax); + d_firstnbr = typename AT::t_bigint_1d("qeq/kk:firstnbr",nmax); d_numnbrs = typename AT::t_int_1d("qeq/kk:numnbrs",nmax); - d_jlist = typename AT::t_int_1d("qeq/kk:jlist",m_cap); - d_val = typename AT::t_ffloat_1d("qeq/kk:val",m_cap); + d_jlist = typename AT::t_int_1d("qeq/kk:jlist",m_cap_big); + d_val = typename AT::t_ffloat_1d("qeq/kk:val",m_cap_big); } /* ---------------------------------------------------------------------- */ @@ -405,7 +406,7 @@ void FixQEqReaxFFKokkos::operator()(TagQEqZero, const int &ii) const template template KOKKOS_INLINE_FUNCTION -void FixQEqReaxFFKokkos::compute_h_item(int ii, int &m_fill, const bool &final) const +void FixQEqReaxFFKokkos::compute_h_item(int ii, bigint &m_fill, const bool &final) const { const int i = d_ilist[ii]; int j,jj,jtype; @@ -458,7 +459,7 @@ void FixQEqReaxFFKokkos::compute_h_item(int ii, int &m_fill, const b m_fill++; } if (final) - d_numnbrs[i] = m_fill - d_firstnbr[i]; + d_numnbrs[i] = int(m_fill - d_firstnbr[i]); } } @@ -537,9 +538,9 @@ void FixQEqReaxFFKokkos::compute_h_team( // calculate the global memory offset from where the H matrix values to be // calculated by the current team will be stored in d_val - int team_firstnbr_idx = 0; + bigint team_firstnbr_idx = 0; Kokkos::single(Kokkos::PerTeam(team), - [=](int &val) { + [=](bigint &val) { int totalnbrs = s_firstnbr[lastatom - firstatom - 1] + s_numnbrs[lastatom - firstatom - 1]; val = Kokkos::atomic_fetch_add(&d_mfill_offset(), totalnbrs); @@ -565,7 +566,7 @@ void FixQEqReaxFFKokkos::compute_h_team( int jnum = s_numnbrs[idx]; // removed "const" to work around GCC 7 bug // calculate the write-offset for atom-i's first neighbor - int atomi_firstnbr_idx = team_firstnbr_idx + s_firstnbr[idx]; + bigint atomi_firstnbr_idx = team_firstnbr_idx + s_firstnbr[idx]; Kokkos::single(Kokkos::PerThread(team), [&]() { d_firstnbr[i] = atomi_firstnbr_idx; }); @@ -578,7 +579,7 @@ void FixQEqReaxFFKokkos::compute_h_team( // are processed in batches and the batch size is vector_length for (int jj_start = 0; jj_start < jnum; jj_start += vector_length) { - int atomi_nbr_writeIdx = atomi_firstnbr_idx + atomi_nbrs_inH; + bigint atomi_nbr_writeIdx = atomi_firstnbr_idx + atomi_nbrs_inH; // count the # of neighbor atoms with non-zero electrostatic // interaction coefficients with atom-i in the current batch @@ -935,7 +936,7 @@ void FixQEqReaxFFKokkos::operator()(TagQEqSparseMatvec2_Half::operator()(TagQEqSparseMatvec2_Full, const const int i = d_ilist[k]; if (mask[i] & groupbit) { F_FLOAT2 doitmp; - Kokkos::parallel_reduce(Kokkos::ThreadVectorRange(team, d_firstnbr[i], d_firstnbr[i] + d_numnbrs[i]), [&] (const int &jj, F_FLOAT2& doi) { + Kokkos::parallel_reduce(Kokkos::ThreadVectorRange(team, d_firstnbr[i], d_firstnbr[i] + d_numnbrs[i]), [&] (const bigint &jj, F_FLOAT2& doi) { const int j = d_jlist(jj); const auto d_val_jj = d_val(jj); if (!(converged & 1)) @@ -1286,8 +1287,8 @@ double FixQEqReaxFFKokkos::memory_usage() bytes = atom->nmax*nprev*2 * sizeof(F_FLOAT); // s_hist & t_hist bytes += (double)atom->nmax*8 * sizeof(F_FLOAT); // storage bytes += (double)n_cap*2 * sizeof(int); // matrix... - bytes += (double)m_cap * sizeof(int); - bytes += (double)m_cap * sizeof(F_FLOAT); + bytes += (double)m_cap_big * sizeof(int); + bytes += (double)m_cap_big * sizeof(F_FLOAT); return bytes; } diff --git a/src/KOKKOS/fix_qeq_reaxff_kokkos.h b/src/KOKKOS/fix_qeq_reaxff_kokkos.h index 92026b209d..0733a518a2 100644 --- a/src/KOKKOS/fix_qeq_reaxff_kokkos.h +++ b/src/KOKKOS/fix_qeq_reaxff_kokkos.h @@ -70,7 +70,7 @@ class FixQEqReaxFFKokkos : public FixQEqReaxFF, public KokkosBase { void pre_force(int) override; KOKKOS_INLINE_FUNCTION - void num_neigh_item(int, int&) const; + void num_neigh_item(int, bigint&) const; KOKKOS_INLINE_FUNCTION void operator()(TagQEqZero, const int&) const; @@ -80,7 +80,7 @@ class FixQEqReaxFFKokkos : public FixQEqReaxFF, public KokkosBase { template KOKKOS_INLINE_FUNCTION - void compute_h_item(int, int &, const bool &) const; + void compute_h_item(int, bigint &, const bool &) const; template KOKKOS_INLINE_FUNCTION @@ -201,8 +201,9 @@ class FixQEqReaxFFKokkos : public FixQEqReaxFF, public KokkosBase { int allocated_flag, last_allocate; int need_dup; int converged; + bigint m_cap_big; - typename AT::t_int_scalar d_mfill_offset; + typename AT::t_bigint_scalar d_mfill_offset; typedef Kokkos::DualView tdual_int_1d; Kokkos::DualView k_params; @@ -227,7 +228,7 @@ class FixQEqReaxFFKokkos : public FixQEqReaxFF, public KokkosBase { DAT::tdual_ffloat_1d k_tap; typename AT::t_ffloat_1d d_tap; - typename AT::t_int_1d d_firstnbr; + typename AT::t_bigint_1d d_firstnbr; typename AT::t_int_1d d_numnbrs; typename AT::t_int_1d d_jlist; typename AT::t_ffloat_1d d_val; @@ -290,21 +291,21 @@ class FixQEqReaxFFKokkos : public FixQEqReaxFF, public KokkosBase { template struct FixQEqReaxFFKokkosNumNeighFunctor { typedef DeviceType device_type; - typedef int value_type; + typedef bigint value_type; FixQEqReaxFFKokkos c; FixQEqReaxFFKokkosNumNeighFunctor(FixQEqReaxFFKokkos* c_ptr):c(*c_ptr) { c.cleanup_copy(); }; KOKKOS_INLINE_FUNCTION - void operator()(const int ii, int &maxneigh) const { - c.num_neigh_item(ii, maxneigh); + void operator()(const int ii, bigint &totneigh) const { + c.num_neigh_item(ii, totneigh); } }; template struct FixQEqReaxFFKokkosComputeHFunctor { int atoms_per_team, vector_length; - typedef int value_type; + typedef bigint value_type; typedef Kokkos::ScratchMemorySpace scratch_space; FixQEqReaxFFKokkos c; @@ -319,7 +320,7 @@ struct FixQEqReaxFFKokkosComputeHFunctor { }; KOKKOS_INLINE_FUNCTION - void operator()(const int ii, int &m_fill, const bool &final) const { + void operator()(const int ii, bigint &m_fill, const bool &final) const { c.template compute_h_item(ii,m_fill,final); } diff --git a/src/KOKKOS/kokkos.cpp b/src/KOKKOS/kokkos.cpp index 58b9436af6..7dba47f889 100644 --- a/src/KOKKOS/kokkos.cpp +++ b/src/KOKKOS/kokkos.cpp @@ -638,33 +638,32 @@ void KokkosLMP::accelerator(int narg, char **arg) called by Finish ------------------------------------------------------------------------- */ -int KokkosLMP::neigh_count(int m) +bigint KokkosLMP::neigh_count(int m) { - int inum = 0; - int nneigh = 0; - - ArrayTypes::t_int_1d h_ilist; - ArrayTypes::t_int_1d h_numneigh; + bigint nneigh = 0; NeighborKokkos *nk = (NeighborKokkos *) neighbor; if (nk->lists[m]->execution_space == Host) { NeighListKokkos* nlistKK = (NeighListKokkos*) nk->lists[m]; - inum = nlistKK->inum; - h_ilist = Kokkos::create_mirror_view(nlistKK->d_ilist); - h_numneigh = Kokkos::create_mirror_view(nlistKK->d_numneigh); - Kokkos::deep_copy(h_ilist,nlistKK->d_ilist); - Kokkos::deep_copy(h_numneigh,nlistKK->d_numneigh); + int inum = nlistKK->inum; + auto d_ilist = nlistKK->d_ilist; + auto d_numneigh = nlistKK->d_numneigh; + Kokkos::parallel_reduce(Kokkos::RangePolicy(0,inum), LAMMPS_LAMBDA(int ii, bigint &nneigh) { + const int i = d_ilist[ii]; + nneigh += d_numneigh[i]; + },nneigh); + } else if (nk->lists[m]->execution_space == Device) { NeighListKokkos* nlistKK = (NeighListKokkos*) nk->lists[m]; - inum = nlistKK->inum; - h_ilist = Kokkos::create_mirror_view(nlistKK->d_ilist); - h_numneigh = Kokkos::create_mirror_view(nlistKK->d_numneigh); - Kokkos::deep_copy(h_ilist,nlistKK->d_ilist); - Kokkos::deep_copy(h_numneigh,nlistKK->d_numneigh); + int inum = nlistKK->inum; + auto d_ilist = nlistKK->d_ilist; + auto d_numneigh = nlistKK->d_numneigh; + Kokkos::parallel_reduce(Kokkos::RangePolicy(0,inum), LAMMPS_LAMBDA(int ii, bigint &nneigh) { + const int i = d_ilist[ii]; + nneigh += d_numneigh[i]; + },nneigh); } - for (int i = 0; i < inum; i++) nneigh += h_numneigh[h_ilist[i]]; - return nneigh; } diff --git a/src/KOKKOS/kokkos.h b/src/KOKKOS/kokkos.h index 748aff7f83..419de62dec 100644 --- a/src/KOKKOS/kokkos.h +++ b/src/KOKKOS/kokkos.h @@ -64,7 +64,7 @@ class KokkosLMP : protected Pointers { static void initialize(const Kokkos::InitializationSettings&, Error *); static void finalize(); void accelerator(int, char **); - int neigh_count(int); + bigint neigh_count(int); template int need_dup(int qeq_flag = 0) diff --git a/src/KOKKOS/kokkos_type.h b/src/KOKKOS/kokkos_type.h index 7f0eb5c105..e4c1bdda6f 100644 --- a/src/KOKKOS/kokkos_type.h +++ b/src/KOKKOS/kokkos_type.h @@ -641,6 +641,14 @@ typedef tdual_int_scalar::t_dev_const t_int_scalar_const; typedef tdual_int_scalar::t_dev_um t_int_scalar_um; typedef tdual_int_scalar::t_dev_const_um t_int_scalar_const_um; +typedef Kokkos:: + DualView tdual_bigint_scalar; +typedef tdual_bigint_scalar::t_dev t_bigint_scalar; +typedef tdual_bigint_scalar::t_dev_const t_bigint_scalar_const; +typedef tdual_bigint_scalar::t_dev_um t_bigint_scalar_um; +typedef tdual_bigint_scalar::t_dev_const_um t_bigint_scalar_const_um; +typedef tdual_bigint_scalar::t_dev_const_randomread t_bigint_scalar_randomread; + typedef Kokkos:: DualView tdual_tagint_scalar; typedef tdual_tagint_scalar::t_dev t_tagint_scalar; @@ -666,6 +674,14 @@ typedef tdual_int_1d::t_dev_um t_int_1d_um; typedef tdual_int_1d::t_dev_const_um t_int_1d_const_um; typedef tdual_int_1d::t_dev_const_randomread t_int_1d_randomread; +typedef Kokkos:: + DualView tdual_bigint_1d; +typedef tdual_bigint_1d::t_dev t_bigint_1d; +typedef tdual_bigint_1d::t_dev_const t_bigint_1d_const; +typedef tdual_bigint_1d::t_dev_um t_bigint_1d_um; +typedef tdual_bigint_1d::t_dev_const_um t_bigint_1d_const_um; +typedef tdual_bigint_1d::t_dev_const_randomread t_bigint_1d_randomread; + typedef Kokkos:: DualView tdual_int_1d_3; typedef tdual_int_1d_3::t_dev t_int_1d_3; @@ -974,6 +990,12 @@ typedef tdual_int_scalar::t_host_const t_int_scalar_const; typedef tdual_int_scalar::t_host_um t_int_scalar_um; typedef tdual_int_scalar::t_host_const_um t_int_scalar_const_um; +typedef Kokkos::DualView tdual_bigint_scalar; +typedef tdual_bigint_scalar::t_host t_bigint_scalar; +typedef tdual_bigint_scalar::t_host_const t_bigint_scalar_const; +typedef tdual_bigint_scalar::t_host_um t_bigint_scalar_um; +typedef tdual_bigint_scalar::t_host_const_um t_bigint_scalar_const_um; + typedef Kokkos::DualView tdual_tagint_scalar; typedef tdual_tagint_scalar::t_host t_tagint_scalar; typedef tdual_tagint_scalar::t_host_const t_tagint_scalar_const; @@ -994,6 +1016,13 @@ typedef tdual_int_1d::t_host_um t_int_1d_um; typedef tdual_int_1d::t_host_const_um t_int_1d_const_um; typedef tdual_int_1d::t_host_const_randomread t_int_1d_randomread; +typedef Kokkos::DualView tdual_bigint_1d; +typedef tdual_bigint_1d::t_host t_bigint_1d; +typedef tdual_bigint_1d::t_host_const t_bigint_1d_const; +typedef tdual_bigint_1d::t_host_um t_bigint_1d_um; +typedef tdual_bigint_1d::t_host_const_um t_bigint_1d_const_um; +typedef tdual_bigint_1d::t_host_const_randomread t_bigint_1d_randomread; + typedef Kokkos::DualView tdual_int_1d_3; typedef tdual_int_1d_3::t_host t_int_1d_3; typedef tdual_int_1d_3::t_host_const t_int_1d_3_const; From 09c121ebbc4c46602580f1cb410ebf44aa56a228 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Wed, 26 Jun 2024 14:55:36 -0600 Subject: [PATCH 017/355] Prevent overflow in neighbor output --- src/finish.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/finish.cpp b/src/finish.cpp index 554f250fe1..c774f51e58 100644 --- a/src/finish.cpp +++ b/src/finish.cpp @@ -62,7 +62,8 @@ Finish::Finish(LAMMPS *lmp) : Pointers(lmp) {} void Finish::end(int flag) { - int i,nneigh,nneighfull; + int i; + bigint nneigh,nneighfull; int histo[10]; int minflag,prdflag,tadflag,hyperflag; int timeflag,fftflag,histoflag,neighflag; From 0e706d4dcc99649a3e1f752c8d6c20f029f7345c Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 27 Jun 2024 10:37:31 -0400 Subject: [PATCH 018/355] add support for building the PLUMED package as plugin --- cmake/Modules/Packages/PLUMED.cmake | 10 +- .../PACKAGES/plumed/plugin/CMakeLists.txt | 59 +++++++ .../plumed/plugin/LAMMPSInterfacePlugin.cmake | 1 + examples/PACKAGES/plumed/plugin/PLUMED.cmake | 1 + examples/PACKAGES/plumed/plugin/README.txt | 2 + .../plumed/plugin/lammps-text-logo-wide.bmp | Bin 0 -> 25818 bytes examples/PACKAGES/plumed/plugin/lammps.ico | Bin 0 -> 209266 bytes .../PACKAGES/plumed/plugin/plumedplugin.cpp | 28 +++ .../PACKAGES/plumed/plugin/plumedplugin.nsis | 167 ++++++++++++++++++ 9 files changed, 267 insertions(+), 1 deletion(-) create mode 100644 examples/PACKAGES/plumed/plugin/CMakeLists.txt create mode 120000 examples/PACKAGES/plumed/plugin/LAMMPSInterfacePlugin.cmake create mode 120000 examples/PACKAGES/plumed/plugin/PLUMED.cmake create mode 100644 examples/PACKAGES/plumed/plugin/README.txt create mode 100644 examples/PACKAGES/plumed/plugin/lammps-text-logo-wide.bmp create mode 100644 examples/PACKAGES/plumed/plugin/lammps.ico create mode 100644 examples/PACKAGES/plumed/plugin/plumedplugin.cpp create mode 100644 examples/PACKAGES/plumed/plugin/plumedplugin.nsis diff --git a/cmake/Modules/Packages/PLUMED.cmake b/cmake/Modules/Packages/PLUMED.cmake index 8312589478..b411e5d269 100644 --- a/cmake/Modules/Packages/PLUMED.cmake +++ b/cmake/Modules/Packages/PLUMED.cmake @@ -81,6 +81,9 @@ if((CMAKE_SYSTEM_NAME STREQUAL "Windows") AND (CMAKE_CROSSCOMPILING)) DEPENDS plumed_build COMMENT "Copying Plumed files" ) + if(CMAKE_PROJECT_NAME STREQUAL "lammps") + target_link_libraries(lammps INTERFACE LAMMPS::PLUMED) + endif() else() @@ -155,6 +158,9 @@ else() endif() set_target_properties(LAMMPS::PLUMED PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include) file(MAKE_DIRECTORY ${INSTALL_DIR}/include) + if(CMAKE_PROJECT_NAME STREQUAL "lammps") + target_link_libraries(lammps PRIVATE LAMMPS::PLUMED) + endif() else() find_package(PkgConfig REQUIRED) pkg_check_modules(PLUMED REQUIRED plumed${PLUMED_SUFFIX}) @@ -169,7 +175,9 @@ else() endif() set_target_properties(LAMMPS::PLUMED PROPERTIES INTERFACE_LINK_LIBRARIES "${PLUMED_LOAD}") set_target_properties(LAMMPS::PLUMED PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${PLUMED_INCLUDE_DIRS}") + if(CMAKE_PROJECT_NAME STREQUAL "lammps") + target_link_libraries(lammps PUBLIC LAMMPS::PLUMED) + endif() endif() endif() -target_link_libraries(lammps PRIVATE LAMMPS::PLUMED) diff --git a/examples/PACKAGES/plumed/plugin/CMakeLists.txt b/examples/PACKAGES/plumed/plugin/CMakeLists.txt new file mode 100644 index 0000000000..2a8f439c2f --- /dev/null +++ b/examples/PACKAGES/plumed/plugin/CMakeLists.txt @@ -0,0 +1,59 @@ +# -*- CMake -*- build system for plugin examples. +# The is meant to be used as a template for plugins that are +# distributed independent from the LAMMPS package. +########################################## + +cmake_minimum_required(VERSION 3.16) + +project(plumedplugin VERSION 1.0 LANGUAGES CXX) + +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) +include(CheckIncludeFileCXX) +include(LAMMPSInterfacePlugin) +include(PLUMED) + +########################## +# building the plugins + +add_library(plumedplugin MODULE plumedplugin.cpp ${LAMMPS_SOURCE_DIR}/PLUMED/fix_plumed.cpp) +target_link_libraries(plumedplugin PRIVATE LAMMPS::PLUMED) +target_link_libraries(plumedplugin PRIVATE lammps) +target_include_directories(plumedplugin PRIVATE ${LAMMPS_SOURCE_DIR}/PLUMED) +set_target_properties(plumedplugin PROPERTIES PREFIX "" SUFFIX ".so") + +# MacOS seems to need this +if(CMAKE_SYSTEM_NAME STREQUAL Darwin) + set_target_properties(plumedplugin PROPERTIES LINK_FLAGS "-Wl,-undefined,dynamic_lookup") +elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") +# tell CMake to export all symbols to a .dll on Windows with special case for MinGW cross-compilers + set_target_properties(plumedplugin PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE) + if(CMAKE_CROSSCOMPILING) + set_target_properties(plumedplugin PROPERTIES LINK_FLAGS "-Wl,--export-all-symbols") + endif() + + get_lammps_version(${LAMMPS_SOURCE_DIR}/version.h LAMMPS_VERSION) + find_program(MAKENSIS_PATH makensis) + if(MAKENSIS_PATH) + execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/lammps.ico + ${CMAKE_SOURCE_DIR}/lammps-text-logo-wide.bmp ${CMAKE_SOURCE_DIR}/plumedplugin.nsis + ${CMAKE_SOURCE_DIR}/patches ${CMAKE_BINARY_DIR}) + if(BUILD_MPI) + if(USE_MSMPI) + add_custom_target(package ${MAKENSIS_PATH} -V1 -DVERSION=${LAMMPS_VERSION}-MSMPI plumedplugin.nsis + DEPENDS plumedplugin + BYPRODUCTS LAMMPS-PLUMED-plugin-${LAMMPS_VERSION}-MSMPI.exe) + else() + add_custom_target(package ${MAKENSIS_PATH} -V1 -DVERSION=${LAMMPS_VERSION}-MPI plumedplugin.nsis + DEPENDS plumedplugin + BYPRODUCTS LAMMPS-PLUMED-plugin-${LAMMPS_VERSION}-MPI.exe) + endif() + else() + add_custom_target(package ${MAKENSIS_PATH} -V1 -DVERSION=${LAMMPS_VERSION} plumedplugin.nsis + COMMAND ${CMAKE_COMMAND} -E echo ${PWD} + DEPENDS plumedplugin lammps.ico lammps-text-logo-wide.bmp plumedplugin.nsis + BYPRODUCTS LAMMPS-PLUMED-plugin-${LAMMPS_VERSION}.exe) + endif() + endif() +else() + set_target_properties(plumedplugin PROPERTIES LINK_FLAGS "-rdynamic") +endif() diff --git a/examples/PACKAGES/plumed/plugin/LAMMPSInterfacePlugin.cmake b/examples/PACKAGES/plumed/plugin/LAMMPSInterfacePlugin.cmake new file mode 120000 index 0000000000..2ac6d20a54 --- /dev/null +++ b/examples/PACKAGES/plumed/plugin/LAMMPSInterfacePlugin.cmake @@ -0,0 +1 @@ +../../../../cmake/Modules/LAMMPSInterfacePlugin.cmake \ No newline at end of file diff --git a/examples/PACKAGES/plumed/plugin/PLUMED.cmake b/examples/PACKAGES/plumed/plugin/PLUMED.cmake new file mode 120000 index 0000000000..b69e8b04f5 --- /dev/null +++ b/examples/PACKAGES/plumed/plugin/PLUMED.cmake @@ -0,0 +1 @@ +../../../../cmake/Modules/Packages/PLUMED.cmake \ No newline at end of file diff --git a/examples/PACKAGES/plumed/plugin/README.txt b/examples/PACKAGES/plumed/plugin/README.txt new file mode 100644 index 0000000000..2b1971ddbc --- /dev/null +++ b/examples/PACKAGES/plumed/plugin/README.txt @@ -0,0 +1,2 @@ +This folder contains a loader and support files to build the PLUMED package as plugin. +For more information please see: https://docs.lammps.org/Developer_plugins.html diff --git a/examples/PACKAGES/plumed/plugin/lammps-text-logo-wide.bmp b/examples/PACKAGES/plumed/plugin/lammps-text-logo-wide.bmp new file mode 100644 index 0000000000000000000000000000000000000000..b9ec4c35f22ef07acea3256fa80f58b8e9b5f5cc GIT binary patch literal 25818 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 cJb# Date: Thu, 27 Jun 2024 18:52:58 -0400 Subject: [PATCH 019/355] silence warnings --- cmake/CMakeLists.txt | 5 +++++ cmake/Modules/Packages/ML-PACE.cmake | 8 +++++++- cmake/Modules/Packages/PLUMED.cmake | 5 +++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index d8b4f0a4f6..217d25e476 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -12,6 +12,11 @@ endif() if(POLICY CMP0075) cmake_policy(SET CMP0075 NEW) endif() +# set policy to silence warnings about requiring execute permission for find_program +# we use OLD because the python-config script for the Fedora MinGW cross-compiler requires it currently +if(POLICY CMP0109) + cmake_policy(SET CMP0109 OLD) +endif() # set policy to silence warnings about timestamps of downloaded files. review occasionally if it may be set to NEW if(POLICY CMP0135) cmake_policy(SET CMP0135 OLD) diff --git a/cmake/Modules/Packages/ML-PACE.cmake b/cmake/Modules/Packages/ML-PACE.cmake index 248b8eea76..8660898138 100644 --- a/cmake/Modules/Packages/ML-PACE.cmake +++ b/cmake/Modules/Packages/ML-PACE.cmake @@ -1,5 +1,11 @@ -set(PACELIB_URL "https://github.com/ICAMS/lammps-user-pace/archive/refs/tags/v.2023.11.25.fix.tar.gz" CACHE STRING "URL for PACE evaluator library sources") +# PACE library support for ML-PACE package +# set policy to silence warnings about timestamps of downloaded files. review occasionally if it may be set to NEW +if(POLICY CMP0135) + cmake_policy(SET CMP0135 OLD) +endif() + +set(PACELIB_URL "https://github.com/ICAMS/lammps-user-pace/archive/refs/tags/v.2023.11.25.fix.tar.gz" CACHE STRING "URL for PACE evaluator library sources") set(PACELIB_MD5 "b45de9a633f42ed65422567e3ce56f9f" CACHE STRING "MD5 checksum of PACE evaluator library tarball") mark_as_advanced(PACELIB_URL) mark_as_advanced(PACELIB_MD5) diff --git a/cmake/Modules/Packages/PLUMED.cmake b/cmake/Modules/Packages/PLUMED.cmake index b411e5d269..595b6824c1 100644 --- a/cmake/Modules/Packages/PLUMED.cmake +++ b/cmake/Modules/Packages/PLUMED.cmake @@ -1,5 +1,10 @@ # Plumed2 support for PLUMED package +# set policy to silence warnings about timestamps of downloaded files. review occasionally if it may be set to NEW +if(POLICY CMP0135) + cmake_policy(SET CMP0135 OLD) +endif() + # for supporting multiple concurrent plumed2 installations for debugging and testing set(PLUMED_SUFFIX "" CACHE STRING "Suffix for Plumed2 library") mark_as_advanced(PLUMED_SUFFIX) From 5014e408c0675a165c364c301c6ea7da0b111aff Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 27 Jun 2024 20:07:52 -0400 Subject: [PATCH 020/355] use correct path to (downloaded) plumed source tree --- examples/PACKAGES/plumed/plugin/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/PACKAGES/plumed/plugin/CMakeLists.txt b/examples/PACKAGES/plumed/plugin/CMakeLists.txt index 2a8f439c2f..3db479822c 100644 --- a/examples/PACKAGES/plumed/plugin/CMakeLists.txt +++ b/examples/PACKAGES/plumed/plugin/CMakeLists.txt @@ -36,7 +36,7 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") if(MAKENSIS_PATH) execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/lammps.ico ${CMAKE_SOURCE_DIR}/lammps-text-logo-wide.bmp ${CMAKE_SOURCE_DIR}/plumedplugin.nsis - ${CMAKE_SOURCE_DIR}/patches ${CMAKE_BINARY_DIR}) + ${PLUMED_LIBDIR}/plumed${PLUMED_SUFFIX}/patches ${CMAKE_BINARY_DIR}) if(BUILD_MPI) if(USE_MSMPI) add_custom_target(package ${MAKENSIS_PATH} -V1 -DVERSION=${LAMMPS_VERSION}-MSMPI plumedplugin.nsis From e75e63c218fbd15a61bcb09faf040ea6da1e5248 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 27 Jun 2024 20:08:15 -0400 Subject: [PATCH 021/355] also package the plumed.exe executable with the plugin --- examples/PACKAGES/plumed/plugin/plumedplugin.nsis | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/examples/PACKAGES/plumed/plugin/plumedplugin.nsis b/examples/PACKAGES/plumed/plugin/plumedplugin.nsis index 3fbf0a92e0..9db5a8123a 100644 --- a/examples/PACKAGES/plumed/plugin/plumedplugin.nsis +++ b/examples/PACKAGES/plumed/plugin/plumedplugin.nsis @@ -93,12 +93,15 @@ Section "${PLUMEDPLUGIN}" SecPlumedplugin SetOutPath "$INSTDIR" CreateDirectory "$INSTDIR\patches" + CreateDirectory "$INSTDIR\bin" File lammps.ico File plumedplugin.so SetOutPath "$INSTDIR\patches" File /r patches/* + SetOutPath "$INSTDIR\bin" + File /r *.exe # Register Application and its uninstaller WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\LAMMPS-PLUMED" \ @@ -125,6 +128,8 @@ Section "${PLUMEDPLUGIN}" SecPlumedplugin # update path variables EnVar::SetHKCU + # add plumed executable path + EnVar::AddValue "PATH" "$INSTDIR\bin" # add to LAMMPS plugin search path EnVar::AddValue "LAMMPS_PLUGIN_PATH" "$INSTDIR" # add plumed2 patch files @@ -150,12 +155,15 @@ Section "Uninstall" # update path variables EnVar::SetHKCU + # remove plumed executable path + EnVar::DeleteValue "PATH" "$INSTDIR\bin" # remove entry from LAMMPS plugin search path EnVar::DeleteValue "LAMMPS_PLUGIN_PATH" "$INSTDIR" # remove plumed patch environment EnVar::Delete "PLUMED_ROOT" RMDir /r /REBOOTOK "$INSTDIR\patches" + RMDir /r /REBOOTOK "$INSTDIR\bin" Delete /REBOOTOK "$INSTDIR\plumedplugin.so" Delete /REBOOTOK "$INSTDIR\Uninstall.exe" Delete /REBOOTOK "$INSTDIR\lammps.ico" From 42724fda742b1cd7828626392660c3d8c52b7609 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 27 Jun 2024 20:56:20 -0400 Subject: [PATCH 022/355] note the ability to build ML-PACE and PLUMED as plugins --- doc/src/Build_extras.rst | 10 ++++++++++ doc/src/Developer_plugins.rst | 2 +- doc/src/Packages_details.rst | 7 +++++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/doc/src/Build_extras.rst b/doc/src/Build_extras.rst index f66238c3c9..f1526d2934 100644 --- a/doc/src/Build_extras.rst +++ b/doc/src/Build_extras.rst @@ -1397,6 +1397,11 @@ in lib/pace or somewhere else, which must be done before building LAMMPS with this package. The code for the library can be found at: `https://github.com/ICAMS/lammps-user-pace/ `_ +Instead of including the ML-PACE package directly into LAMMPS, it +is also possible to skip this step and build the ML-PACE package as +a plugin using the CMake script files in the ``examples/PACKAGE/pace/plugin`` +folder and then load this plugin at runtime with the :doc:`plugin command `. + .. tabs:: .. tab:: CMake build @@ -1561,6 +1566,11 @@ try a different one, switch to a different build system, consider a global PLUMED installation or consider downloading PLUMED during the LAMMPS build. +Instead of including the PLUMED package directly into LAMMPS, it +is also possible to skip this step and build the PLUMED package as +a plugin using the CMake script files in the ``examples/PACKAGE/plumed/plugin`` +folder and then load this plugin at runtime with the :doc:`plugin command `. + .. tabs:: .. tab:: CMake build diff --git a/doc/src/Developer_plugins.rst b/doc/src/Developer_plugins.rst index dd5431507e..4d822130f3 100644 --- a/doc/src/Developer_plugins.rst +++ b/doc/src/Developer_plugins.rst @@ -283,7 +283,7 @@ in the ``examples/kim/plugin`` folder. No changes to the sources of the KIM package themselves are needed; only the plugin interface and loader code needs to be added. This example only supports building with CMake, but is probably a more typical example. To compile you need to run CMake -with -DLAMMPS_SOURCE_DIR=. Other +with ``-DLAMMPS_SOURCE_DIR=``. Other configuration setting are identical to those for compiling LAMMPS. A second example for a plugin from a package is in the diff --git a/doc/src/Packages_details.rst b/doc/src/Packages_details.rst index e759e3bd18..244e104698 100644 --- a/doc/src/Packages_details.rst +++ b/doc/src/Packages_details.rst @@ -1820,7 +1820,8 @@ Aidan Thompson^3, Gabor Csanyi^2, Christoph Ortner^4, Ralf Drautz^1. **Install:** This package has :ref:`specific installation instructions ` on the -:doc:`Build extras ` page. +:doc:`Build extras ` page. This package may also be compiled +as a plugin to avoid licensing conflicts when distributing binaries. **Supporting info:** @@ -2341,7 +2342,9 @@ and Gareth Tribello. **Install:** -This package has :ref:`specific installation instructions ` on the :doc:`Build extras ` page. +This package has :ref:`specific installation instructions ` on the +:doc:`Build extras ` page. This package may also be compiled +as a plugin to avoid licensing conflicts when distributing binaries. **Supporting info:** From 1f7fb7b6f52268bbb4f81d00791a60fde7e4e4bf Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 27 Jun 2024 20:57:35 -0400 Subject: [PATCH 023/355] correct path (again) --- examples/PACKAGES/plumed/plugin/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/PACKAGES/plumed/plugin/CMakeLists.txt b/examples/PACKAGES/plumed/plugin/CMakeLists.txt index 3db479822c..caadea9405 100644 --- a/examples/PACKAGES/plumed/plugin/CMakeLists.txt +++ b/examples/PACKAGES/plumed/plugin/CMakeLists.txt @@ -36,7 +36,7 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") if(MAKENSIS_PATH) execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/lammps.ico ${CMAKE_SOURCE_DIR}/lammps-text-logo-wide.bmp ${CMAKE_SOURCE_DIR}/plumedplugin.nsis - ${PLUMED_LIBDIR}/plumed${PLUMED_SUFFIX}/patches ${CMAKE_BINARY_DIR}) + ${PLUMED_LIBDIR}/patches ${CMAKE_BINARY_DIR}) if(BUILD_MPI) if(USE_MSMPI) add_custom_target(package ${MAKENSIS_PATH} -V1 -DVERSION=${LAMMPS_VERSION}-MSMPI plumedplugin.nsis From 2ad8119282da2caf28de8f2ff066430746dc0d9d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 27 Jun 2024 21:24:12 -0400 Subject: [PATCH 024/355] do not try to install plumed patches --- examples/PACKAGES/plumed/plugin/CMakeLists.txt | 2 +- examples/PACKAGES/plumed/plugin/plumedplugin.nsis | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/examples/PACKAGES/plumed/plugin/CMakeLists.txt b/examples/PACKAGES/plumed/plugin/CMakeLists.txt index caadea9405..b14d3e5f9b 100644 --- a/examples/PACKAGES/plumed/plugin/CMakeLists.txt +++ b/examples/PACKAGES/plumed/plugin/CMakeLists.txt @@ -36,7 +36,7 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") if(MAKENSIS_PATH) execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/lammps.ico ${CMAKE_SOURCE_DIR}/lammps-text-logo-wide.bmp ${CMAKE_SOURCE_DIR}/plumedplugin.nsis - ${PLUMED_LIBDIR}/patches ${CMAKE_BINARY_DIR}) + ${CMAKE_BINARY_DIR}) if(BUILD_MPI) if(USE_MSMPI) add_custom_target(package ${MAKENSIS_PATH} -V1 -DVERSION=${LAMMPS_VERSION}-MSMPI plumedplugin.nsis diff --git a/examples/PACKAGES/plumed/plugin/plumedplugin.nsis b/examples/PACKAGES/plumed/plugin/plumedplugin.nsis index 9db5a8123a..36b820f465 100644 --- a/examples/PACKAGES/plumed/plugin/plumedplugin.nsis +++ b/examples/PACKAGES/plumed/plugin/plumedplugin.nsis @@ -97,9 +97,6 @@ Section "${PLUMEDPLUGIN}" SecPlumedplugin File lammps.ico File plumedplugin.so - SetOutPath "$INSTDIR\patches" - File /r patches/* - SetOutPath "$INSTDIR\bin" File /r *.exe From 185141bd7d8cc5af2c52e23ee52277cefb0d19ec Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 27 Jun 2024 23:24:20 -0400 Subject: [PATCH 025/355] use faster compression --- examples/PACKAGES/pace/plugin/paceplugin.nsis | 2 +- examples/PACKAGES/plumed/plugin/plumedplugin.nsis | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/PACKAGES/pace/plugin/paceplugin.nsis b/examples/PACKAGES/pace/plugin/paceplugin.nsis index de8d1d8478..0ca96b95a6 100644 --- a/examples/PACKAGES/pace/plugin/paceplugin.nsis +++ b/examples/PACKAGES/pace/plugin/paceplugin.nsis @@ -39,7 +39,7 @@ InstallDir "$LOCALAPPDATA\${PACEPLUGIN}" ShowInstDetails show ShowUninstDetails show -SetCompressor lzma +SetCompressor zlib !define MUI_ABORTWARNING diff --git a/examples/PACKAGES/plumed/plugin/plumedplugin.nsis b/examples/PACKAGES/plumed/plugin/plumedplugin.nsis index 36b820f465..229473ddeb 100644 --- a/examples/PACKAGES/plumed/plugin/plumedplugin.nsis +++ b/examples/PACKAGES/plumed/plugin/plumedplugin.nsis @@ -39,7 +39,7 @@ InstallDir "$LOCALAPPDATA\${PLUMEDPLUGIN}" ShowInstDetails show ShowUninstDetails show -SetCompressor lzma +SetCompressor zlib !define MUI_ABORTWARNING From d630fc67abb8a1402924b20d61e1b15d1b7a30e4 Mon Sep 17 00:00:00 2001 From: Nick Hagerty Date: Tue, 2 Jul 2024 15:10:44 -0400 Subject: [PATCH 026/355] Updated remap kokkos to remove unused recv_proc and recv_bufloc buffers --- src/KOKKOS/remap_kokkos.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/KOKKOS/remap_kokkos.cpp b/src/KOKKOS/remap_kokkos.cpp index 7ebe35dddb..50d61cc1a3 100644 --- a/src/KOKKOS/remap_kokkos.cpp +++ b/src/KOKKOS/remap_kokkos.cpp @@ -614,7 +614,8 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat malloc(nsend*sizeof(struct pack_plan_3d)); if (plan->send_offset == nullptr || plan->send_size == nullptr || - plan->send_proc == nullptr || plan->packplan == nullptr) return nullptr; + plan->sendcnts == nullptr || plan->sdispls == nullptr || + plan->packplan == nullptr) return nullptr; // recv space @@ -651,8 +652,8 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat malloc(nrecv*sizeof(struct pack_plan_3d)); if (plan->recv_offset == nullptr || plan->recv_size == nullptr || - plan->recv_proc == nullptr || plan->recv_bufloc == nullptr || - plan->request == nullptr || plan->unpackplan == nullptr) return nullptr; + plan->rcvcnts == nullptr || plan->rdispls == nullptr || + plan->unpackplan == nullptr) return nullptr; } // store send info, with self as last entry @@ -708,8 +709,6 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat plan->selfnrecvloc = nrecv; } if (remap_3d_collide(&out,&inarray[iproc],&overlap)) { - //plan->recv_proc[nrecv] = iproc; - plan->recv_bufloc[nrecv] = ibuf; if (permute == 0) { plan->recv_offset[nrecv] = nqty * From 9513c0edac92f67f5ed18d4842f97822e40e502b Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Tue, 2 Jul 2024 13:28:46 -0600 Subject: [PATCH 027/355] small cleanup --- src/KOKKOS/remap_kokkos.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/KOKKOS/remap_kokkos.cpp b/src/KOKKOS/remap_kokkos.cpp index 50d61cc1a3..573f4c2508 100644 --- a/src/KOKKOS/remap_kokkos.cpp +++ b/src/KOKKOS/remap_kokkos.cpp @@ -283,7 +283,7 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat struct remap_plan_3d_kokkos *plan; struct extent_3d *inarray, *outarray; struct extent_3d in,out,overlap; - int i,j,iproc,nsend,nrecv,ibuf,size,me,nprocs,isend,irecv; + int i,iproc,nsend,nrecv,ibuf,size,me,nprocs; // query MPI info @@ -552,7 +552,7 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat } // perform an AllReduce to get the counts from all other processors and build sendcnts list - + MPI_Allreduce(local_cnts, global_cnts, 2*nprocs, MPI_INT, MPI_SUM, comm); // now remove procs that are 0 in send or recv to create minimized sendcnts/recvcnts for AlltoAllv @@ -569,11 +569,11 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat } // resize commringlist to final size - + commringlist = (int *) realloc(commringlist, commringlen*sizeof(int)); - + // set the plan->commringlist - + plan->commringlen = commringlen; plan->commringlist = commringlist; @@ -655,9 +655,9 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat plan->rcvcnts == nullptr || plan->rdispls == nullptr || plan->unpackplan == nullptr) return nullptr; } - + // store send info, with self as last entry - + nsend = 0; ibuf = 0; int total_send_size = 0; @@ -697,19 +697,19 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat plan->d_sendbuf = typename FFT_AT::t_FFT_SCALAR_1d("remap3d:sendbuf",total_send_size); if (!plan->d_sendbuf.data()) return nullptr; } - + // store recv info, with self as last entry - + ibuf = 0; nrecv = 0; - + for (i = 0; i < plan->commringlen; i++) { iproc = plan->commringlist[i]; if (iproc == me) { plan->selfnrecvloc = nrecv; } if (remap_3d_collide(&out,&inarray[iproc],&overlap)) { - + if (permute == 0) { plan->recv_offset[nrecv] = nqty * ((overlap.klo-out.klo)*out.jsize*out.isize + @@ -743,7 +743,7 @@ struct remap_plan_3d_kokkos* RemapKokkos::remap_3d_creat plan->unpackplan[nrecv].nstride_plane = nqty*out.isize*out.ksize; plan->unpackplan[nrecv].nqty = nqty; } - + plan->recv_size[i] = nqty*overlap.isize*overlap.jsize*overlap.ksize; plan->rcvcnts[i] = plan->recv_size[i]; plan->rdispls[i] = ibuf; @@ -845,7 +845,7 @@ void RemapKokkos::remap_3d_destroy_plan_kokkos(struct remap_plan_3d_ free(plan->unpackplan); } } else { - + // free arrays used in pt2pt communication if (plan->nsend || plan->self) { From bae822fea776335181d2e61eb0d0871f04c3c278 Mon Sep 17 00:00:00 2001 From: Chris Knight Date: Fri, 19 Jul 2024 15:23:49 +0000 Subject: [PATCH 028/355] update Makefiles --- lib/gpu/Makefile.aurora | 31 ++++++ src/MAKE/MACHINES/Makefile.aurora | 121 +++++++++++++++++++++++ src/MAKE/MACHINES/Makefile.aurora_kokkos | 2 + 3 files changed, 154 insertions(+) create mode 100644 lib/gpu/Makefile.aurora create mode 100644 src/MAKE/MACHINES/Makefile.aurora diff --git a/lib/gpu/Makefile.aurora b/lib/gpu/Makefile.aurora new file mode 100644 index 0000000000..c343e061ee --- /dev/null +++ b/lib/gpu/Makefile.aurora @@ -0,0 +1,31 @@ +# /* ---------------------------------------------------------------------- +# Generic Linux Makefile for OpenCL +# ------------------------------------------------------------------------- */ + +# which file will be copied to Makefile.lammps + +EXTRAMAKE = Makefile.lammps.opencl + +# OCL_TUNE = -DFERMI_OCL # -- Uncomment for NVIDIA Fermi +# OCL_TUNE = -DKEPLER_OCL # -- Uncomment for NVIDIA Kepler +# OCL_TUNE = -DCYPRESS_OCL # -- Uncomment for AMD Cypress +OCL_TUNE = -DGENERIC_OCL # -- Uncomment for generic device + +# this setting should match LAMMPS Makefile +# one of LAMMPS_SMALLBIG (default), LAMMPS_BIGBIG and LAMMPS_SMALLSMALL + +LMP_INC = -DLAMMPS_SMALLBIG + +OCL_INC = -I/opt/intel/oneapi/compiler/latest/linux/include/sycl/ # Path to CL directory +OCL_CPP = mpicxx -cxx=icpx -DCUDA_PROXY $(DEFAULT_DEVICE) -xHost -O2 -ffp-model=fast -qoverride-limits -DMPI_GERYON -DGERYON_NUMA_FISSION -DUCL_NO_EXIT -DMPICH_IGNORE_CXX_SEEK $(LMP_INC) $(OCL_INC) -DGERYON_NO_PROF +OCL_LINK = -L/opt/intel/oneapi/compiler/latest/linux/lib/ -lOpenCL +OCL_PREC = -D_SINGLE_DOUBLE + +BIN_DIR = ./ +OBJ_DIR = ./ +LIB_DIR = ./ +AR = ar +BSH = /bin/sh + +include Opencl.makefile + diff --git a/src/MAKE/MACHINES/Makefile.aurora b/src/MAKE/MACHINES/Makefile.aurora new file mode 100644 index 0000000000..1b16025f7c --- /dev/null +++ b/src/MAKE/MACHINES/Makefile.aurora @@ -0,0 +1,121 @@ +# aurora_kokkos = KOKKOS/SYCL, Intel Data Center Max (Ponte Vecchio) GPU, Intel Sapphire Rapids CPU, mpicxx compiler + +SHELL = /bin/sh + +# --------------------------------------------------------------------- +# compiler/linker settings +# specify flags and libraries needed for your compiler + +CC = mpicxx +OPTFLAGS = -DSHAKEATOMIC -DCOMMPARA -xSAPPHIRERAPIDS -O2 -ffp-model=fast -qoverride-limits -qopt-zmm-usage=high +CCFLAGS = -qopenmp -DLMP_INTEL_USELRT -DLMP_USE_MKL_RNG $(OPTFLAGS) +CCFLAGS += -I$(MKLROOT)/include +SHFLAGS = -fPIC +DEPFLAGS = -M + +LINK = mpicxx +LINKFLAGS = -qopenmp $(OPTFLAGS) +LIB = -ltbbmalloc -lmkl_intel_ilp64 -lmkl_sequential -lmkl_core +SIZE = size + +ARCHIVE = ar +ARFLAGS = -rc +SHLIBFLAGS = -shared + +# --------------------------------------------------------------------- +# LAMMPS-specific settings, all OPTIONAL +# specify settings for LAMMPS features you will use +# if you change any -D setting, do full re-compile after "make clean" + +# LAMMPS ifdef settings +# see possible settings in Section 3.5 of the manual + +LMP_INC = -DLAMMPS_GZIP + +# MPI library +# see discussion in Section 3.4 of the manual +# MPI wrapper compiler/linker can provide this info +# can point to dummy MPI library in src/STUBS as in Makefile.serial +# use -D MPICH and OMPI settings in INC to avoid C++ lib conflicts +# INC = path for mpi.h, MPI compiler settings +# PATH = path for MPI library +# LIB = name of MPI library + +MPI_INC = -DMPICH_SKIP_MPICXX -DOMPI_SKIP_MPICXX=1 +MPI_PATH = +MPI_LIB = + +# FFT library +# see discussion in Section 3.5.2 of manual +# can be left blank to use provided KISS FFT library +# INC = -DFFT setting, e.g. -DFFT_FFTW, FFT compiler settings +# PATH = path for FFT library +# LIB = name of FFT library + +FFT_INC = -DFFT_MKL -DFFT_SINGLE +FFT_PATH = +FFT_LIB = + +# JPEG and/or PNG library +# see discussion in Section 3.5.4 of manual +# only needed if -DLAMMPS_JPEG or -DLAMMPS_PNG listed with LMP_INC +# INC = path(s) for jpeglib.h and/or png.h +# PATH = path(s) for JPEG library and/or PNG library +# LIB = name(s) of JPEG library and/or PNG library + +JPG_INC = +JPG_PATH = +JPG_LIB = + +# --------------------------------------------------------------------- +# build rules and dependencies +# do not edit this section + +include Makefile.package.settings +include Makefile.package + +EXTRA_INC = $(LMP_INC) $(PKG_INC) $(MPI_INC) $(FFT_INC) $(JPG_INC) $(PKG_SYSINC) +EXTRA_PATH = $(PKG_PATH) $(MPI_PATH) $(FFT_PATH) $(JPG_PATH) $(PKG_SYSPATH) +EXTRA_LIB = $(PKG_LIB) $(MPI_LIB) $(FFT_LIB) $(JPG_LIB) $(PKG_SYSLIB) +EXTRA_CPP_DEPENDS = $(PKG_CPP_DEPENDS) +EXTRA_LINK_DEPENDS = $(PKG_LINK_DEPENDS) + +# Path to src files + +vpath %.cpp .. +vpath %.h .. + +# Link target + +$(EXE): main.o $(LMPLIB) $(EXTRA_LINK_DEPENDS) + $(LINK) $(LINKFLAGS) main.o $(EXTRA_PATH) $(LMPLINK) $(EXTRA_LIB) $(LIB) -o $@ + $(SIZE) $@ + +# Library targets + +$(ARLIB): $(OBJ) $(EXTRA_LINK_DEPENDS) + @rm -f ../$(ARLIB) + $(ARCHIVE) $(ARFLAGS) ../$(ARLIB) $(OBJ) + @rm -f $(ARLIB) + @ln -s ../$(ARLIB) $(ARLIB) + +$(SHLIB): $(OBJ) $(EXTRA_LINK_DEPENDS) + $(CC) $(CCFLAGS) $(SHFLAGS) $(SHLIBFLAGS) $(EXTRA_PATH) -o ../$(SHLIB) \ + $(OBJ) $(EXTRA_LIB) $(LIB) + @rm -f $(SHLIB) + @ln -s ../$(SHLIB) $(SHLIB) + +# Compilation rules + +%.o:%.cpp + $(CC) $(CCFLAGS) $(SHFLAGS) $(EXTRA_INC) -c $< + +# Individual dependencies + +depend : fastdep.exe $(SRC) + @./fastdep.exe $(EXTRA_INC) -- $^ > .depend || exit 1 + +fastdep.exe: ../DEPEND/fastdep.c + cc -O -o $@ $< + +sinclude .depend diff --git a/src/MAKE/MACHINES/Makefile.aurora_kokkos b/src/MAKE/MACHINES/Makefile.aurora_kokkos index a263d4cb8c..4aa737346a 100644 --- a/src/MAKE/MACHINES/Makefile.aurora_kokkos +++ b/src/MAKE/MACHINES/Makefile.aurora_kokkos @@ -8,11 +8,13 @@ SHELL = /bin/sh CC = mpicxx CCFLAGS = -g -O3 -DNDEBUG +CCFLAGS += -fsycl-device-code-split=per_kernel SHFLAGS = -fPIC DEPFLAGS = -M LINK = mpicxx LINKFLAGS = -g -O3 +LINKFLAGS += -flink-huge-device-code -fsycl-max-parallel-link-jobs=64 LIB = SIZE = size From 884b9dd9bd855acd55b83a75dfd95a3cf4eaf0f8 Mon Sep 17 00:00:00 2001 From: Chris Knight Date: Fri, 19 Jul 2024 15:34:02 +0000 Subject: [PATCH 029/355] add PALS env var --- src/KOKKOS/kokkos.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/KOKKOS/kokkos.cpp b/src/KOKKOS/kokkos.cpp index 58b9436af6..1c36169002 100644 --- a/src/KOKKOS/kokkos.cpp +++ b/src/KOKKOS/kokkos.cpp @@ -149,6 +149,14 @@ KokkosLMP::KokkosLMP(LAMMPS *lmp, int narg, char **arg) : Pointers(lmp) set_flag = 1; } } + if ((str = getenv("PALS_LOCAL_RANKID"))) { + if (ngpus > 0) { + int local_rank = atoi(str); + device = local_rank % ngpus; + if (device >= skip_gpu) device++; + set_flag = 1; + } + } if (ngpus > 1 && !set_flag) error->all(FLERR,"Could not determine local MPI rank for multiple " From b540f572a35bea57e0095bff99b9e93c735accb4 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Aug 2024 08:40:24 -0400 Subject: [PATCH 030/355] prototype workflow for a quick regression test --- .github/workflows/quick-regression.yml | 66 ++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 .github/workflows/quick-regression.yml diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml new file mode 100644 index 0000000000..e4f90326ea --- /dev/null +++ b/.github/workflows/quick-regression.yml @@ -0,0 +1,66 @@ +# GitHub action to build LAMMPS on Linux and run selected regression tests +name: "Quick Regression Test" + +on: + push: + branches: + - develop + - quick-regression + pull_request: + branches: + - develop + + workflow_dispatch: + +jobs: + build: + name: Quick Regression Test + if: ${{ github.repository == 'lammps/lammps' }} + runs-on: ubuntu-latest + env: + CCACHE_DIR: ${{ github.workspace }}/.ccache + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Install ccache + run: apt install ccache + + - name: Create Build Environment + run: mkdir build + + - name: Set up ccache + uses: actions/cache@v4 + with: + path: ${{ env.CCACHE_DIR }} + key: linux-ccache-${{ github.sha }} + restore-keys: linux-ccache- + + - name: Building LAMMPS via CMake + shell: bash + working-directory: build + run: | + ccache -z + python3 -m venv linuxenv + source linuxenv/bin/activate + python3 -m pip install numpy + python3 -m pip install pyyaml + cmake -C ../cmake/presets/gcc.cmake \ + -C ../cmake/presets/most.cmake \ + -D DOWNLOAD_POTENTIALS=off \ + -D CMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -D CMAKE_C_COMPILER_LAUNCHER=ccache \ + -D ENABLE_TESTING=on \ + -D BUILD_SHARED_LIBS=on \ + -D LAMMPS_EXCEPTIONS=on \ + ../cmake + cmake --build . --parallel 2 + ccache -s + + - name: Run Tests + working-directory: build + shell: bash + run: ctest -V From 5562d66931c83da1fccd52f426080e7397385e8c Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Aug 2024 08:43:07 -0400 Subject: [PATCH 031/355] need sudo for software installation --- .github/workflows/quick-regression.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index e4f90326ea..52c883823b 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -27,7 +27,7 @@ jobs: fetch-depth: 2 - name: Install ccache - run: apt install ccache + run: sudo apt-get install -y ccache - name: Create Build Environment run: mkdir build From 9d80c22a0bf1cf4b0df1282ce5a6ecb9d7c33015 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Aug 2024 09:43:43 -0400 Subject: [PATCH 032/355] install a few extra packages --- .github/workflows/quick-regression.yml | 31 +++++++++++++++++--------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index 52c883823b..219ce0a38a 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -24,10 +24,12 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: - fetch-depth: 2 + fetch-depth: 2000 - - name: Install ccache - run: sudo apt-get install -y ccache + - name: Install extra packages + run: | + sudo apt-get install -y ccache ninja-build + sudo apt-get install -y libeigen3-dev libgsl-dev libcurl4-openssl-dev python3-dev - name: Create Build Environment run: mkdir build @@ -41,23 +43,30 @@ jobs: - name: Building LAMMPS via CMake shell: bash - working-directory: build run: | ccache -z python3 -m venv linuxenv source linuxenv/bin/activate python3 -m pip install numpy python3 -m pip install pyyaml - cmake -C ../cmake/presets/gcc.cmake \ - -C ../cmake/presets/most.cmake \ - -D DOWNLOAD_POTENTIALS=off \ + cmake -S cmake -B build \ + -C cmake/presets/gcc.cmake \ + -C cmake/presets/most.cmake \ -D CMAKE_CXX_COMPILER_LAUNCHER=ccache \ -D CMAKE_C_COMPILER_LAUNCHER=ccache \ - -D ENABLE_TESTING=on \ -D BUILD_SHARED_LIBS=on \ - -D LAMMPS_EXCEPTIONS=on \ - ../cmake - cmake --build . --parallel 2 + -D DOWNLOAD_POTENTIALS=off \ + -D ENABLE_TESTING=on \ + -D PKG_MANIFOLD=on \ + -D PKG_ML-PACE=on \ + -D PKG_ML-RANN=on \ + -D PKG_RHEO=on \ + -D PKG_PTM=on \ + -D PKG_PYTHON=on \ + -D PKG_QTB=on \ + -D PKG_SMTBQ=on \ + -G Ninja + cmake --build build ccache -s - name: Run Tests From d5c245cb3bba0ae41cd8649dff52da4ddeacc4b2 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Aug 2024 10:40:19 -0400 Subject: [PATCH 033/355] add workflow for full regression testing --- .github/workflows/full-regression.yml | 69 +++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 .github/workflows/full-regression.yml diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml new file mode 100644 index 0000000000..b7ced102c1 --- /dev/null +++ b/.github/workflows/full-regression.yml @@ -0,0 +1,69 @@ +# GitHub action to build LAMMPS on Linux and run regression tests +name: "Full Regression Test" + +on: + push: + branches: + - develop + - quick-regression + + workflow_dispatch: + +jobs: + build: + name: Full Regression Test + # restrict to official LAMMPS repository + if: ${{ github.repository == 'lammps/lammps' }} + runs-on: ubuntu-latest + env: + CCACHE_DIR: ${{ github.workspace }}/.ccache + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Install extra packages + run: | + sudo apt-get install -y ccache ninja-build + sudo apt-get install -y libeigen3-dev libgsl-dev libcurl4-openssl-dev python3-dev + + - name: Create Build Environment + run: mkdir build + + - name: Set up ccache + uses: actions/cache@v4 + with: + path: ${{ env.CCACHE_DIR }} + key: linux-ccache-${{ github.sha }} + restore-keys: linux-ccache- + + - name: Building LAMMPS via CMake + shell: bash + run: | + ccache -z + cmake -S cmake -B build \ + -C cmake/presets/gcc.cmake \ + -C cmake/presets/most.cmake \ + -D CMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -D CMAKE_C_COMPILER_LAUNCHER=ccache \ + -D BUILD_SHARED_LIBS=off \ + -D DOWNLOAD_POTENTIALS=off \ + -D PKG_MANIFOLD=on \ + -D PKG_ML-PACE=on \ + -D PKG_ML-RANN=on \ + -D PKG_RHEO=on \ + -D PKG_PTM=on \ + -D PKG_PYTHON=on \ + -D PKG_QTB=on \ + -D PKG_SMTBQ=on \ + -G Ninja + cmake --build build + ccache -s + + - name: Run Regression Tests + shell: bash + run: | + echo 'Linux binary is here:' + ls -lh build/lmp From 8adc90b71fb870a217592a7a3e879d587cca2319 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Aug 2024 10:41:54 -0400 Subject: [PATCH 034/355] add Linux unit test with -DLAMMPS_BIGBIG --- .github/workflows/unittest-linux.yml | 76 ++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 .github/workflows/unittest-linux.yml diff --git a/.github/workflows/unittest-linux.yml b/.github/workflows/unittest-linux.yml new file mode 100644 index 0000000000..65e82face0 --- /dev/null +++ b/.github/workflows/unittest-linux.yml @@ -0,0 +1,76 @@ +# GitHub action to build LAMMPS on Linux and run standard unit tests +name: "Linux Unit Test" + +on: + push: + branches: + - develop + - quick-regression + pull_request: + branches: + - develop + + workflow_dispatch: + +jobs: + build: + name: Quick Regression Test + if: ${{ github.repository == 'lammps/lammps' }} + runs-on: ubuntu-latest + env: + CCACHE_DIR: ${{ github.workspace }}/.ccache + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Install extra packages + run: | + sudo apt-get install -y ccache mold ninja-build + sudo apt-get install -y libeigen3-dev libgsl-dev libcurl4-openssl-dev python3-dev + + - name: Create Build Environment + run: mkdir build + + - name: Set up ccache + uses: actions/cache@v4 + with: + path: ${{ env.CCACHE_DIR }} + key: linux-ccache-${{ github.sha }} + restore-keys: linux-ccache- + + - name: Building LAMMPS via CMake + shell: bash + run: | + ccache -z + python3 -m venv linuxenv + source linuxenv/bin/activate + python3 -m pip install numpy + python3 -m pip install pyyaml + cmake -S cmake -B build \ + -C cmake/presets/gcc.cmake \ + -C cmake/presets/most.cmake \ + -D CMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -D CMAKE_C_COMPILER_LAUNCHER=ccache \ + -D BUILD_SHARED_LIBS=on \ + -D LAMMPS_SIZES=bigbig \ + -D DOWNLOAD_POTENTIALS=off \ + -D ENABLE_TESTING=on \ + -D PKG_MANIFOLD=on \ + -D PKG_ML-PACE=on \ + -D PKG_ML-RANN=on \ + -D PKG_RHEO=on \ + -D PKG_PTM=on \ + -D PKG_PYTHON=on \ + -D PKG_QTB=on \ + -D PKG_SMTBQ=on \ + -G Ninja + cmake --build build + ccache -s + + - name: Run Tests + working-directory: build + shell: bash + run: ctest -V From 8c6351b6b9fd56a8de416608ff4ab07ab0493623 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Aug 2024 10:43:30 -0400 Subject: [PATCH 035/355] remove unneeded stuff from quick regression test --- .github/workflows/quick-regression.yml | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index 219ce0a38a..301c8ac99f 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -45,18 +45,13 @@ jobs: shell: bash run: | ccache -z - python3 -m venv linuxenv - source linuxenv/bin/activate - python3 -m pip install numpy - python3 -m pip install pyyaml cmake -S cmake -B build \ -C cmake/presets/gcc.cmake \ -C cmake/presets/most.cmake \ -D CMAKE_CXX_COMPILER_LAUNCHER=ccache \ -D CMAKE_C_COMPILER_LAUNCHER=ccache \ - -D BUILD_SHARED_LIBS=on \ + -D BUILD_SHARED_LIBS=off \ -D DOWNLOAD_POTENTIALS=off \ - -D ENABLE_TESTING=on \ -D PKG_MANIFOLD=on \ -D PKG_ML-PACE=on \ -D PKG_ML-RANN=on \ @@ -69,7 +64,8 @@ jobs: cmake --build build ccache -s - - name: Run Tests - working-directory: build + - name: Run Selected Regression Tests shell: bash - run: ctest -V + run: | + echo 'Linux binary is here:' + ls -lh build/lmp From 664c6f908aab6f368911c6e3c26f4d5f7dfaba17 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Aug 2024 11:02:43 -0400 Subject: [PATCH 036/355] must use different ccache keys to avoid conflicts between concurrent jobs --- .github/workflows/full-regression.yml | 4 ++-- .github/workflows/quick-regression.yml | 4 ++-- .github/workflows/unittest-linux.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index b7ced102c1..e3c6835e77 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -36,8 +36,8 @@ jobs: uses: actions/cache@v4 with: path: ${{ env.CCACHE_DIR }} - key: linux-ccache-${{ github.sha }} - restore-keys: linux-ccache- + key: linux-full-ccache-${{ github.sha }} + restore-keys: linux-full-ccache- - name: Building LAMMPS via CMake shell: bash diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index 301c8ac99f..1b25058ab6 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -38,8 +38,8 @@ jobs: uses: actions/cache@v4 with: path: ${{ env.CCACHE_DIR }} - key: linux-ccache-${{ github.sha }} - restore-keys: linux-ccache- + key: linux-quick-ccache-${{ github.sha }} + restore-keys: linux-quick-ccache- - name: Building LAMMPS via CMake shell: bash diff --git a/.github/workflows/unittest-linux.yml b/.github/workflows/unittest-linux.yml index 65e82face0..0837eae63f 100644 --- a/.github/workflows/unittest-linux.yml +++ b/.github/workflows/unittest-linux.yml @@ -38,8 +38,8 @@ jobs: uses: actions/cache@v4 with: path: ${{ env.CCACHE_DIR }} - key: linux-ccache-${{ github.sha }} - restore-keys: linux-ccache- + key: linux-unit-ccache-${{ github.sha }} + restore-keys: linux-unit-ccache- - name: Building LAMMPS via CMake shell: bash From 10dce38a76bc8ee1f0557001f2356ecd7b5d20f6 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Aug 2024 11:40:01 -0400 Subject: [PATCH 037/355] small tweaks --- .github/workflows/compile-msvc.yml | 4 ++-- .github/workflows/quick-regression.yml | 3 +-- .github/workflows/unittest-linux.yml | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/compile-msvc.yml b/.github/workflows/compile-msvc.yml index 1a0f1ea62f..04eac38ace 100644 --- a/.github/workflows/compile-msvc.yml +++ b/.github/workflows/compile-msvc.yml @@ -1,5 +1,5 @@ -# GitHub action to build LAMMPS on Windows with Visual C++ -name: "Native Windows Compilation and Unit Tests" +# GitHub action to build LAMMPS on Windows with Visual C++ and run unit tests +name: "Native Windows Unit Tests" on: push: diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index 1b25058ab6..4c029758ab 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -67,5 +67,4 @@ jobs: - name: Run Selected Regression Tests shell: bash run: | - echo 'Linux binary is here:' - ls -lh build/lmp + python3 tools/regression-tests/get-quick-list.py diff --git a/.github/workflows/unittest-linux.yml b/.github/workflows/unittest-linux.yml index 0837eae63f..49477f7765 100644 --- a/.github/workflows/unittest-linux.yml +++ b/.github/workflows/unittest-linux.yml @@ -1,5 +1,5 @@ # GitHub action to build LAMMPS on Linux and run standard unit tests -name: "Linux Unit Test" +name: "Unittest for Linux" on: push: From 61fd2ba25cc29ef3e9e47f301e2b6c726a55f46c Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Aug 2024 11:40:26 -0400 Subject: [PATCH 038/355] apply clang-format to have some files with changes --- src/MOLECULE/angle_harmonic.cpp | 12 ++--- src/MOLECULE/dihedral_multi_harmonic.cpp | 4 +- src/MOLECULE/dihedral_opls.cpp | 14 +++--- src/compute_reduce.cpp | 34 +++++++++----- src/create_box.cpp | 56 ++++++++++++++---------- src/fix_efield.cpp | 33 +++++++++----- 6 files changed, 92 insertions(+), 61 deletions(-) diff --git a/src/MOLECULE/angle_harmonic.cpp b/src/MOLECULE/angle_harmonic.cpp index e9f1c528ef..040cbe7530 100644 --- a/src/MOLECULE/angle_harmonic.cpp +++ b/src/MOLECULE/angle_harmonic.cpp @@ -276,17 +276,17 @@ void AngleHarmonic::born_matrix(int type, int i1, int i2, int i3, double &du, do double delx1 = x[i1][0] - x[i2][0]; double dely1 = x[i1][1] - x[i2][1]; double delz1 = x[i1][2] - x[i2][2]; - domain->minimum_image(delx1,dely1,delz1); - double r1 = sqrt(delx1*delx1 + dely1*dely1 + delz1*delz1); + domain->minimum_image(delx1, dely1, delz1); + double r1 = sqrt(delx1 * delx1 + dely1 * dely1 + delz1 * delz1); double delx2 = x[i3][0] - x[i2][0]; double dely2 = x[i3][1] - x[i2][1]; double delz2 = x[i3][2] - x[i2][2]; - domain->minimum_image(delx2,dely2,delz2); - double r2 = sqrt(delx2*delx2 + dely2*dely2 + delz2*delz2); + domain->minimum_image(delx2, dely2, delz2); + double r2 = sqrt(delx2 * delx2 + dely2 * dely2 + delz2 * delz2); - double c = delx1*delx2 + dely1*dely2 + delz1*delz2; - c /= r1*r2; + double c = delx1 * delx2 + dely1 * dely2 + delz1 * delz2; + c /= r1 * r2; if (c > 1.0) c = 1.0; if (c < -1.0) c = -1.0; double theta = acos(c); diff --git a/src/MOLECULE/dihedral_multi_harmonic.cpp b/src/MOLECULE/dihedral_multi_harmonic.cpp index 8e6685cac9..2d1e16b9e4 100644 --- a/src/MOLECULE/dihedral_multi_harmonic.cpp +++ b/src/MOLECULE/dihedral_multi_harmonic.cpp @@ -326,8 +326,8 @@ void DihedralMultiHarmonic::write_data(FILE *fp) /* ---------------------------------------------------------------------- */ -void DihedralMultiHarmonic::born_matrix(int nd, int i1, int i2, int i3, int i4, - double &du, double &du2) +void DihedralMultiHarmonic::born_matrix(int nd, int i1, int i2, int i3, int i4, double &du, + double &du2) { double vb1x, vb1y, vb1z, vb2x, vb2y, vb2z, vb3x, vb3y, vb3z, vb2xm, vb2ym, vb2zm; double sb1, sb3, rb1, rb3, c0, b1mag2, b1mag, b2mag2; diff --git a/src/MOLECULE/dihedral_opls.cpp b/src/MOLECULE/dihedral_opls.cpp index eced454d68..e99d83f631 100644 --- a/src/MOLECULE/dihedral_opls.cpp +++ b/src/MOLECULE/dihedral_opls.cpp @@ -336,8 +336,7 @@ void DihedralOPLS::write_data(FILE *fp) /* ----------------------------------------------------------------------*/ -void DihedralOPLS::born_matrix(int nd, int i1, int i2, int i3, int i4, - double &du, double &du2) +void DihedralOPLS::born_matrix(int nd, int i1, int i2, int i3, int i4, double &du, double &du2) { double vb1x, vb1y, vb1z, vb2x, vb2y, vb2z, vb3x, vb3y, vb3z, vb2xm, vb2ym, vb2zm; double sb1, sb3, rb1, rb3, c0, b1mag2, b1mag, b2mag2; @@ -425,9 +424,10 @@ void DihedralOPLS::born_matrix(int nd, int i1, int i2, int i3, int i4, si = sin(phi); if (fabs(si) < SMALLER) si = SMALLER; - du = k1[type] - 2.0 * k2[type] * sin(2.0 * phi) / si + 3.0 * k3[type] * sin(3.0 * phi) / si - - 4.0 * k4[type] * sin(4.0 * phi) / si; - du2 = (4.0 * k2[type] * si * cos(2.0 * phi) - 2.0 * k2[type] * sin(2.0 * phi) - - 9.0 * k3[type] * si * cos(3.0 * phi) + 3.0 * k3[type] * sin(3.0 * phi) - + 16.0 * k4[type] * si * cos(4.0 * phi) - 4.0 * k4[type] * sin(4.0 * phi)) / (si * si * si); + du = k1[type] - 2.0 * k2[type] * sin(2.0 * phi) / si + 3.0 * k3[type] * sin(3.0 * phi) / si - + 4.0 * k4[type] * sin(4.0 * phi) / si; + du2 = (4.0 * k2[type] * si * cos(2.0 * phi) - 2.0 * k2[type] * sin(2.0 * phi) - + 9.0 * k3[type] * si * cos(3.0 * phi) + 3.0 * k3[type] * sin(3.0 * phi) + + 16.0 * k4[type] * si * cos(4.0 * phi) - 4.0 * k4[type] * sin(4.0 * phi)) / + (si * si * si); } diff --git a/src/compute_reduce.cpp b/src/compute_reduce.cpp index ee94c2d9a7..40bb206bd2 100644 --- a/src/compute_reduce.cpp +++ b/src/compute_reduce.cpp @@ -214,8 +214,10 @@ ComputeReduce::ComputeReduce(LAMMPS *lmp, int narg, char **arg) : iarg += 2; } else if (strcmp(arg[iarg], "inputs") == 0) { if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, mycmd + " inputs", error); - if (strcmp(arg[iarg+1], "peratom") == 0) input_mode = PERATOM; - else if (strcmp(arg[iarg+1], "local") == 0) input_mode = LOCAL; + if (strcmp(arg[iarg + 1], "peratom") == 0) + input_mode = PERATOM; + else if (strcmp(arg[iarg + 1], "local") == 0) + input_mode = LOCAL; iarg += 2; } else error->all(FLERR, "Unknown compute {} keyword: {}", style, arg[iarg]); @@ -242,7 +244,7 @@ ComputeReduce::ComputeReduce(LAMMPS *lmp, int narg, char **arg) : for (auto &val : values) { if (val.which == ArgInfo::X || val.which == ArgInfo::V || val.which == ArgInfo::F) { - if (input_mode == LOCAL) error->all(FLERR,"Compute {} inputs must be all local"); + if (input_mode == LOCAL) error->all(FLERR, "Compute {} inputs must be all local"); } else if (val.which == ArgInfo::COMPUTE) { val.val.c = modify->get_compute_by_id(val.id); @@ -251,11 +253,14 @@ ComputeReduce::ComputeReduce(LAMMPS *lmp, int narg, char **arg) : if (input_mode == PERATOM) { if (!val.val.c->peratom_flag) - error->all(FLERR, "Compute {} compute {} does not calculate per-atom values", style, val.id); + error->all(FLERR, "Compute {} compute {} does not calculate per-atom values", style, + val.id); if (val.argindex == 0 && val.val.c->size_peratom_cols != 0) - error->all(FLERR, "Compute {} compute {} does not calculate a per-atom vector", style, val.id); + error->all(FLERR, "Compute {} compute {} does not calculate a per-atom vector", style, + val.id); if (val.argindex && val.val.c->size_peratom_cols == 0) - error->all(FLERR, "Compute {} compute {} does not calculate a per-atom array", style, val.id); + error->all(FLERR, "Compute {} compute {} does not calculate a per-atom array", style, + val.id); if (val.argindex && val.argindex > val.val.c->size_peratom_cols) error->all(FLERR, "Compute {} compute {} array is accessed out-of-range", style, val.id); @@ -263,9 +268,11 @@ ComputeReduce::ComputeReduce(LAMMPS *lmp, int narg, char **arg) : if (!val.val.c->local_flag) error->all(FLERR, "Compute {} compute {} does not calculate local values", style, val.id); if (val.argindex == 0 && val.val.c->size_local_cols != 0) - error->all(FLERR, "Compute {} compute {} does not calculate a local vector", style, val.id); + error->all(FLERR, "Compute {} compute {} does not calculate a local vector", style, + val.id); if (val.argindex && val.val.c->size_local_cols == 0) - error->all(FLERR, "Compute {} compute {} does not calculate a local array", style, val.id); + error->all(FLERR, "Compute {} compute {} does not calculate a local array", style, + val.id); if (val.argindex && val.argindex > val.val.c->size_local_cols) error->all(FLERR, "Compute {} compute {} array is accessed out-of-range", style, val.id); } @@ -278,7 +285,8 @@ ComputeReduce::ComputeReduce(LAMMPS *lmp, int narg, char **arg) : if (!val.val.f->peratom_flag) error->all(FLERR, "Compute {} fix {} does not calculate per-atom values", style, val.id); if (val.argindex == 0 && (val.val.f->size_peratom_cols != 0)) - error->all(FLERR, "Compute {} fix {} does not calculate a per-atom vector", style, val.id); + error->all(FLERR, "Compute {} fix {} does not calculate a per-atom vector", style, + val.id); if (val.argindex && (val.val.f->size_peratom_cols == 0)) error->all(FLERR, "Compute {} fix {} does not calculate a per-atom array", style, val.id); if (val.argindex && (val.argindex > val.val.f->size_peratom_cols)) @@ -296,7 +304,7 @@ ComputeReduce::ComputeReduce(LAMMPS *lmp, int narg, char **arg) : } } else if (val.which == ArgInfo::VARIABLE) { - if (input_mode == LOCAL) error->all(FLERR,"Compute {} inputs must be all local"); + if (input_mode == LOCAL) error->all(FLERR, "Compute {} inputs must be all local"); val.val.v = input->variable->find(val.id.c_str()); if (val.val.v < 0) error->all(FLERR, "Variable name {} for compute {} does not exist", val.id, style); @@ -417,7 +425,8 @@ void ComputeReduce::compute_vector() } else if (mode == MINN) { if (!replace) { for (int m = 0; m < nvalues; m++) - MPI_Allreduce(&onevec[m], &vector[m], 1, MPI_DOUBLE, this->scalar_reduction_operation, world); + MPI_Allreduce(&onevec[m], &vector[m], 1, MPI_DOUBLE, this->scalar_reduction_operation, + world); } else { for (int m = 0; m < nvalues; m++) @@ -437,7 +446,8 @@ void ComputeReduce::compute_vector() } else if (mode == MAXX) { if (!replace) { for (int m = 0; m < nvalues; m++) - MPI_Allreduce(&onevec[m], &vector[m], 1, MPI_DOUBLE, this->scalar_reduction_operation, world); + MPI_Allreduce(&onevec[m], &vector[m], 1, MPI_DOUBLE, this->scalar_reduction_operation, + world); } else { for (int m = 0; m < nvalues; m++) diff --git a/src/create_box.cpp b/src/create_box.cpp index 8a74ffd7bd..93e699e06b 100644 --- a/src/create_box.cpp +++ b/src/create_box.cpp @@ -49,11 +49,13 @@ void CreateBox::command(int narg, char **arg) Region *region = nullptr; int triclinic_general = 0; - if (strcmp(arg[1],"NULL") == 0) triclinic_general = 1; + if (strcmp(arg[1], "NULL") == 0) + triclinic_general = 1; else { region = domain->get_region_by_id(arg[1]); if (!region) error->all(FLERR, "Create_box region {} does not exist", arg[1]); - if (region->bboxflag == 0) error->all(FLERR, "Create_box region does not support a bounding box"); + if (region->bboxflag == 0) + error->all(FLERR, "Create_box region does not support a bounding box"); region->init(); } @@ -77,9 +79,9 @@ void CreateBox::command(int narg, char **arg) domain->boxlo[2] = region->extent_zlo; domain->boxhi[2] = region->extent_zhi; - // region is prism - // seutp restricted triclinic box - // set simulation domain from prism params + // region is prism + // seutp restricted triclinic box + // set simulation domain from prism params } else { domain->triclinic = 1; @@ -97,17 +99,17 @@ void CreateBox::command(int narg, char **arg) if (domain->dimension == 2) { if (domain->boxlo[2] >= 0.0 || domain->boxhi[2] <= 0.0) - error->all(FLERR,"Create_box region zlo/zhi for 2d simulation must straddle 0.0"); + error->all(FLERR, "Create_box region zlo/zhi for 2d simulation must straddle 0.0"); } - // setup general triclinic box (with no region) - // read next box extent arguments to create ABC edge vectors + origin - // define_general_triclinic() converts - // ABC edge vectors + origin to restricted triclinic + // setup general triclinic box (with no region) + // read next box extent arguments to create ABC edge vectors + origin + // define_general_triclinic() converts + // ABC edge vectors + origin to restricted triclinic } else if (triclinic_general) { if (!domain->lattice->is_general_triclinic()) - error->all(FLERR,"Create_box for general triclinic requires triclnic/general lattice"); + error->all(FLERR, "Create_box for general triclinic requires triclnic/general lattice"); if (iarg + 6 > narg) utils::missing_cmd_args(FLERR, "create_box general triclinic", error); @@ -121,42 +123,50 @@ void CreateBox::command(int narg, char **arg) if (domain->dimension == 2) if (clo != -0.5 || chi != 0.5) - error->all(FLERR,"Create_box for general triclinic requires clo = -0.5 and chi = 0.5"); + error->all(FLERR, "Create_box for general triclinic requires clo = -0.5 and chi = 0.5"); // use lattice2box() to generate origin and ABC vectors // origin = abc lo // ABC vectors = hi in one dim - origin - double avec[3],bvec[3],cvec[3],origin[3]; - double px,py,pz; + double avec[3], bvec[3], cvec[3], origin[3]; + double px, py, pz; - px = alo; py = blo; pz = clo; - domain->lattice->lattice2box(px,py,pz); + px = alo; + py = blo; + pz = clo; + domain->lattice->lattice2box(px, py, pz); origin[0] = px; origin[1] = py; origin[2] = pz; - px = ahi; py = blo; pz = clo; - domain->lattice->lattice2box(px,py,pz); + px = ahi; + py = blo; + pz = clo; + domain->lattice->lattice2box(px, py, pz); avec[0] = px - origin[0]; avec[1] = py - origin[1]; avec[2] = pz - origin[2]; - px = alo; py = bhi; pz = clo; - domain->lattice->lattice2box(px,py,pz); + px = alo; + py = bhi; + pz = clo; + domain->lattice->lattice2box(px, py, pz); bvec[0] = px - origin[0]; bvec[1] = py - origin[1]; bvec[2] = pz - origin[2]; - px = alo; py = blo; pz = chi; - domain->lattice->lattice2box(px,py,pz); + px = alo; + py = blo; + pz = chi; + domain->lattice->lattice2box(px, py, pz); cvec[0] = px - origin[0]; cvec[1] = py - origin[1]; cvec[2] = pz - origin[2]; // define general triclinic box within Domain class - domain->define_general_triclinic(avec,bvec,cvec,origin); + domain->define_general_triclinic(avec, bvec, cvec, origin); } // if molecular, zero out topology info diff --git a/src/fix_efield.cpp b/src/fix_efield.cpp index 81be66b3e3..a5f02cc7c8 100644 --- a/src/fix_efield.cpp +++ b/src/fix_efield.cpp @@ -114,7 +114,8 @@ FixEfield::FixEfield(LAMMPS *lmp, int narg, char **arg) : } if (estr && pstr) - error->all(FLERR, "Must not use energy and potential keywords at the same time with fix efield"); + error->all(FLERR, + "Must not use energy and potential keywords at the same time with fix efield"); force_flag = 0; fsum[0] = fsum[1] = fsum[2] = fsum[3] = 0.0; @@ -171,7 +172,8 @@ void FixEfield::init() if (xstr) { xvar = input->variable->find(xstr); - if (xvar < 0) error->all(FLERR, "Variable {} for x-field in fix {} does not exist", xstr, style); + if (xvar < 0) + error->all(FLERR, "Variable {} for x-field in fix {} does not exist", xstr, style); if (input->variable->equalstyle(xvar)) xstyle = EQUAL; else if (input->variable->atomstyle(xvar)) @@ -182,7 +184,8 @@ void FixEfield::init() if (ystr) { yvar = input->variable->find(ystr); - if (yvar < 0) error->all(FLERR, "Variable {} for y-field in fix {} does not exist", ystr, style); + if (yvar < 0) + error->all(FLERR, "Variable {} for y-field in fix {} does not exist", ystr, style); if (input->variable->equalstyle(yvar)) ystyle = EQUAL; else if (input->variable->atomstyle(yvar)) @@ -193,7 +196,8 @@ void FixEfield::init() if (zstr) { zvar = input->variable->find(zstr); - if (zvar < 0) error->all(FLERR, "Variable {} for z-field in fix {} does not exist", zstr, style); + if (zvar < 0) + error->all(FLERR, "Variable {} for z-field in fix {} does not exist", zstr, style); if (input->variable->equalstyle(zvar)) zstyle = EQUAL; else if (input->variable->atomstyle(zvar)) @@ -213,7 +217,8 @@ void FixEfield::init() if (pstr) { pvar = input->variable->find(pstr); - if (pvar < 0) error->all(FLERR, "Variable {} for potential in fix {} does not exist", pstr, style); + if (pvar < 0) + error->all(FLERR, "Variable {} for potential in fix {} does not exist", pstr, style); if (input->variable->atomstyle(pvar)) pstyle = ATOM; else @@ -244,8 +249,10 @@ void FixEfield::init() error->all(FLERR, "Cannot use variable energy with constant efield in fix {}", style); if (varflag == CONSTANT && pstyle != NONE) error->all(FLERR, "Cannot use variable potential with constant efield in fix {}", style); - if ((varflag == EQUAL || varflag == ATOM) && update->whichflag == 2 && estyle == NONE && pstyle == NONE) - error->all(FLERR, "Must use variable energy or potential with fix {} during minimization", style); + if ((varflag == EQUAL || varflag == ATOM) && update->whichflag == 2 && estyle == NONE && + pstyle == NONE) + error->all(FLERR, "Must use variable energy or potential with fix {} during minimization", + style); if (utils::strmatch(update->integrate_style, "^respa")) { ilevel_respa = (dynamic_cast(update->integrate))->nlevels - 1; @@ -403,8 +410,10 @@ void FixEfield::post_force(int vflag) } f[i][2] += fz; fsum[3] += fz; - if (pstyle == ATOM) fsum[0] += qe2f * q[i] * efield[i][3]; - else if (estyle == ATOM) fsum[0] += efield[i][3]; + if (pstyle == ATOM) + fsum[0] += qe2f * q[i] * efield[i][3]; + else if (estyle == ATOM) + fsum[0] += efield[i][3]; } } @@ -504,8 +513,10 @@ void FixEfield::update_efield_variables() } else if (zstyle == ATOM) { input->variable->compute_atom(zvar, igroup, &efield[0][2], 4, 0); } - if (pstyle == ATOM) input->variable->compute_atom(pvar, igroup, &efield[0][3], 4, 0); - else if (estyle == ATOM) input->variable->compute_atom(evar, igroup, &efield[0][3], 4, 0); + if (pstyle == ATOM) + input->variable->compute_atom(pvar, igroup, &efield[0][3], 4, 0); + else if (estyle == ATOM) + input->variable->compute_atom(evar, igroup, &efield[0][3], 4, 0); modify->addstep_compute(update->ntimestep + 1); } From 1916d0be06496e585ab97ec5dfe1d926e9213113 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Aug 2024 11:41:56 -0400 Subject: [PATCH 039/355] add incomplete draft of quick input file lister --- tools/regression-tests/get-quick-list.py | 154 +++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 tools/regression-tests/get-quick-list.py diff --git a/tools/regression-tests/get-quick-list.py b/tools/regression-tests/get-quick-list.py new file mode 100644 index 0000000000..60bc17f784 --- /dev/null +++ b/tools/regression-tests/get-quick-list.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +""" +Find all example input files containing commands changed in this branch versus develop. +Companion script to run_tests.py regression tester. +""" + +import os, re, sys +from glob import glob +import subprocess + +# infer top level lammps dir +LAMMPS_DIR = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..')) + +# get list of changed files relative to the develop branch from git +output = None +try: + output = subprocess.run('git diff --diff-filter=MA --name-status develop', + shell=True, capture_output=True) +except: + pass + +# collect header files to check for styles +headers = [] +if output: + for changed in output.stdout.decode().split(): + if (changed == 'A') or (changed == 'M'): continue + if not changed.startswith('src/'): continue + if changed.endswith('.h'): headers.append(changed) + if changed.endswith('.cpp'): headers.append(changed.replace('.cpp','.h')) + +# now loop over header files, search for XxxxStyle() macros and append style name +command = [] +atom = [] +compute = [] +fix = [] +pair = [] +body = [] +bond = [] +angle = [] +dihedral = [] +improper = [] +kspace = [] +dump = [] +region = [] +integrate = [] +minimize = [] + +style_pattern = re.compile(r"(.+)Style\((.+),(.+)\)") +upper = re.compile("[A-Z]+") +gpu = re.compile("(.+)/gpu$") +intel = re.compile("(.+)/intel$") +kokkos = re.compile("(.+)/kk$") +kokkos_skip = re.compile("(.+)/kk/(host|device)$") +omp = re.compile("(.+)/omp$") +opt = re.compile("(.+)/opt$") +removed = re.compile("(.*)Deprecated$") + +for file in headers: + with open(file) as f: + for line in f: + matches = style_pattern.findall(line) + for m in matches: + # skip over internal styles w/o explicit documentation + style = m[1] + if upper.match(style): + continue + + # skip over suffix styles: + suffix = kokkos_skip.match(style) + if suffix: + continue + suffix = gpu.match(style) + if suffix: + continue + suffix = intel.match(style) + if suffix: + continue + suffix = kokkos.match(style) + if suffix: + continue + suffix = omp.match(style) + if suffix: + continue + suffix = opt.match(style) + if suffix: + continue + deprecated = removed.match(m[2]) + if deprecated: + continue + + # register style and suffix flags + if m[0] == 'Angle': + angle.append(style) + elif m[0] == 'Atom': + atom.append(style) + elif m[0] == 'Body': + register_style(body,style,info) + elif m[0] == 'Bond': + bond.applend(style) + elif m[0] == 'Command': + command.append(style) + elif m[0] == 'Compute': + compute.append(style) + elif m[0] == 'Dihedral': + dihedral.append(style) + elif m[0] == 'Dump': + dump.append(style) + elif m[0] == 'Fix': + fix.append(style) + elif m[0] == 'Improper': + improper.append(style) + elif m[0] == 'Integrate': + integrate.append(style) + elif m[0] == 'KSpace': + kspace.append(style) + elif m[0] == 'Minimize': + minimize.append(style) + elif m[0] == 'Pair': + pair.append(style) + elif m[0] == 'Region': + region.append(style) + else: + pass + +if len(command): + print("Commands: ", '|'.join(command)) +if len(atom): + print("Atom styles: ", '|'.join(atom)) +if len(compute): + print("Compute styles: ", '|'.join(compute)) +if len(fix): + print("Fix styles: ", '|'.join(fix)) +if len(pair): + print("Pair styles: ", '|'.join(pair)) +if len(body): + print("Body styles: ", '|'.join(body)) +if len(bond): + print("Bond styles: ", '|'.join(bond)) +if len(angle): + print("Angle styles: ", '|'.join(angle)) +if len(dihedral): + print("Dihedral styles: ", '|'.join(dihedral)) +if len(improper): + print("Improper styles: ", '|'.join(improper)) +if len(kspace): + print("Kspace styles: ", '|'.join(kspace)) +if len(dump): + print("Dump styles: ", '|'.join(dump)) +if len(region): + print("Region styles: ", '|'.join(region)) +if len(integrate): + print("Integrate styles: ", '|'.join(integrate)) +if len(minimize): + print("Minimize styles: ", '|'.join(minimize)) From 022d1d795934feef29a628e571d407067c76ca6a Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Aug 2024 15:41:31 -0400 Subject: [PATCH 040/355] get-quick-list.py script is feature complete --- src/REPLICA/verlet_split.cpp | 2 +- src/REPLICA/verlet_split.h | 2 +- tools/regression-tests/get-quick-list.py | 400 +++++++++++++++-------- 3 files changed, 264 insertions(+), 140 deletions(-) diff --git a/src/REPLICA/verlet_split.cpp b/src/REPLICA/verlet_split.cpp index b270ad445d..acc776efc3 100644 --- a/src/REPLICA/verlet_split.cpp +++ b/src/REPLICA/verlet_split.cpp @@ -237,7 +237,7 @@ void VerletSplit::init() tip4pflag = force->kspace->tip4pflag; // invoke parent Verlet init - + Verlet::init(); } diff --git a/src/REPLICA/verlet_split.h b/src/REPLICA/verlet_split.h index 10835e0792..3528a1fe5f 100644 --- a/src/REPLICA/verlet_split.h +++ b/src/REPLICA/verlet_split.h @@ -40,7 +40,7 @@ class VerletSplit : public Verlet { int ratio; // ratio of Rspace procs to Kspace procs int *qsize, *qdisp, *xsize, *xdisp; // MPI gather/scatter params for block comm MPI_Comm block; // communicator within one block - + int tip4pflag; // 1 if Kspace method sets tip4pflag double **f_kspace; // copy of Kspace forces on Rspace procs diff --git a/tools/regression-tests/get-quick-list.py b/tools/regression-tests/get-quick-list.py index 60bc17f784..7c9f977b74 100644 --- a/tools/regression-tests/get-quick-list.py +++ b/tools/regression-tests/get-quick-list.py @@ -4,151 +4,275 @@ Find all example input files containing commands changed in this branch versus d Companion script to run_tests.py regression tester. """ -import os, re, sys -from glob import glob -import subprocess +import os, re, sys, subprocess +from pathlib import Path -# infer top level lammps dir +if sys.version_info < (3,5): + raise BaseException("Must use at least Python 3.5") + +# infer top level LAMMPS dir LAMMPS_DIR = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..')) -# get list of changed files relative to the develop branch from git -output = None -try: - output = subprocess.run('git diff --diff-filter=MA --name-status develop', - shell=True, capture_output=True) -except: - pass +# ---------------------------------------------------------------------- -# collect header files to check for styles -headers = [] -if output: - for changed in output.stdout.decode().split(): - if (changed == 'A') or (changed == 'M'): continue - if not changed.startswith('src/'): continue - if changed.endswith('.h'): headers.append(changed) - if changed.endswith('.cpp'): headers.append(changed.replace('.cpp','.h')) +def changed_files_from_git(branch='develop'): + """ + Return list of changed file from git. -# now loop over header files, search for XxxxStyle() macros and append style name -command = [] -atom = [] -compute = [] -fix = [] -pair = [] -body = [] -bond = [] -angle = [] -dihedral = [] -improper = [] -kspace = [] -dump = [] -region = [] -integrate = [] -minimize = [] + This function queries git to return the list of changed files on + the current branch relative to a given branch (default is 'develop'). -style_pattern = re.compile(r"(.+)Style\((.+),(.+)\)") -upper = re.compile("[A-Z]+") -gpu = re.compile("(.+)/gpu$") -intel = re.compile("(.+)/intel$") -kokkos = re.compile("(.+)/kk$") -kokkos_skip = re.compile("(.+)/kk/(host|device)$") -omp = re.compile("(.+)/omp$") -opt = re.compile("(.+)/opt$") -removed = re.compile("(.*)Deprecated$") + param branch: branch to compare with + type branch: string + return: path names of files with changes relative to the repository root + rtype: list of strings + """ -for file in headers: - with open(file) as f: - for line in f: - matches = style_pattern.findall(line) - for m in matches: - # skip over internal styles w/o explicit documentation - style = m[1] - if upper.match(style): - continue + # get list of changed files relative to the develop branch from git + output = None + try: + output = subprocess.run('git diff --diff-filter=MA --name-status develop', + shell=True, capture_output=True) + except: + pass - # skip over suffix styles: - suffix = kokkos_skip.match(style) - if suffix: - continue - suffix = gpu.match(style) - if suffix: - continue - suffix = intel.match(style) - if suffix: - continue - suffix = kokkos.match(style) - if suffix: - continue - suffix = omp.match(style) - if suffix: - continue - suffix = opt.match(style) - if suffix: - continue - deprecated = removed.match(m[2]) - if deprecated: - continue + # collect header files to check for styles + # - skip files that don't end in '.h' or '.cpp' + # - skip paths that don't start with 'src/' + # - replace '.cpp' with '.h' w/o checking it exists + headers = [] + # output will have a letter 'A' or 'M' for added or modified files followed by pathname + # append iterms to list and return it + if output: + for changed in output.stdout.decode().split(): + if (changed == 'A') or (changed == 'M'): continue + if not changed.startswith('src/'): continue + if changed.endswith('.h'): headers.append(changed) + if changed.endswith('.cpp'): headers.append(changed.replace('.cpp','.h')) + return headers - # register style and suffix flags - if m[0] == 'Angle': - angle.append(style) - elif m[0] == 'Atom': - atom.append(style) - elif m[0] == 'Body': - register_style(body,style,info) - elif m[0] == 'Bond': - bond.applend(style) - elif m[0] == 'Command': - command.append(style) - elif m[0] == 'Compute': - compute.append(style) - elif m[0] == 'Dihedral': - dihedral.append(style) - elif m[0] == 'Dump': - dump.append(style) - elif m[0] == 'Fix': - fix.append(style) - elif m[0] == 'Improper': - improper.append(style) - elif m[0] == 'Integrate': - integrate.append(style) - elif m[0] == 'KSpace': - kspace.append(style) - elif m[0] == 'Minimize': - minimize.append(style) - elif m[0] == 'Pair': - pair.append(style) - elif m[0] == 'Region': - region.append(style) - else: - pass +# ---------------------------------------------------------------------- -if len(command): - print("Commands: ", '|'.join(command)) -if len(atom): - print("Atom styles: ", '|'.join(atom)) -if len(compute): - print("Compute styles: ", '|'.join(compute)) -if len(fix): - print("Fix styles: ", '|'.join(fix)) -if len(pair): - print("Pair styles: ", '|'.join(pair)) -if len(body): - print("Body styles: ", '|'.join(body)) -if len(bond): - print("Bond styles: ", '|'.join(bond)) -if len(angle): - print("Angle styles: ", '|'.join(angle)) -if len(dihedral): - print("Dihedral styles: ", '|'.join(dihedral)) -if len(improper): - print("Improper styles: ", '|'.join(improper)) -if len(kspace): - print("Kspace styles: ", '|'.join(kspace)) -if len(dump): - print("Dump styles: ", '|'.join(dump)) -if len(region): - print("Region styles: ", '|'.join(region)) -if len(integrate): - print("Integrate styles: ", '|'.join(integrate)) -if len(minimize): - print("Minimize styles: ", '|'.join(minimize)) +def get_command_from_header(headers, topdir="."): + """ + Loop over list of header files and extract style names, if present. + + LAMMPS commands have macros XxxxStyle() that connects a string with a class. + We search the header files for those macros, extract the string and append + it to a list in a dictionary of different types of styles. We skip over known + suffixes and deprecated commands. + + param headers: header files to check for commands + type headers: + return: dictionary with lists of style names + rtype: dict + """ + + styles = {} + styles['command'] = [] + styles['atom'] = [] + styles['compute'] = [] + styles['fix'] = [] + styles['pair'] = [] + styles['body'] = [] + styles['bond'] = [] + styles['angle'] = [] + styles['dihedral'] = [] + styles['improper'] = [] + styles['kspace'] = [] + styles['dump'] = [] + styles['region'] = [] + styles['integrate'] = [] + styles['minimize'] = [] + + # some regex + style_pattern = re.compile(r"(.+)Style\((.+),(.+)\)") + upper = re.compile("[A-Z]+") + gpu = re.compile("(.+)/gpu$") + intel = re.compile("(.+)/intel$") + kokkos = re.compile("(.+)/kk$") + kokkos_skip = re.compile("(.+)/kk/(host|device)$") + omp = re.compile("(.+)/omp$") + opt = re.compile("(.+)/opt$") + removed = re.compile("(.*)Deprecated$") + + for file in headers: + # don't fail if file is not present + try: + with open(os.path.join(topdir,file)) as f: + for line in f: + matches = style_pattern.findall(line) + for m in matches: + # skip over internal styles w/o explicit documentation + style = m[1] + if upper.match(style): + continue + + # skip over suffix styles: + suffix = kokkos_skip.match(style) + if suffix: + continue + suffix = gpu.match(style) + if suffix: + continue + suffix = intel.match(style) + if suffix: + continue + suffix = kokkos.match(style) + if suffix: + continue + suffix = omp.match(style) + if suffix: + continue + suffix = opt.match(style) + if suffix: + continue + deprecated = removed.match(m[2]) + if deprecated: + continue + + # register style and suffix flags + if m[0] == 'Angle': + styles['angle'].append(style) + elif m[0] == 'Atom': + styles['atom'].append(style) + elif m[0] == 'Body': + styles['body'].append(style) + elif m[0] == 'Bond': + styles['bond'].applend(style) + elif m[0] == 'Command': + styles['command'].append(style) + elif m[0] == 'Compute': + styles['compute'].append(style) + elif m[0] == 'Dihedral': + styles['dihedral'].append(style) + elif m[0] == 'Dump': + styles['dump'].append(style) + elif m[0] == 'Fix': + styles['fix'].append(style) + elif m[0] == 'Improper': + styles['improper'].append(style) + elif m[0] == 'Integrate': + styles['integrate'].append(style) + elif m[0] == 'KSpace': + styles['kspace'].append(style) + elif m[0] == 'Minimize': + styles['minimize'].append(style) + elif m[0] == 'Pair': + styles['pair'].append(style) + elif m[0] == 'Region': + styles['region'].append(style) + else: + pass + # header file not found or not readable + except: + pass + return styles + +# ---------------------------------------------------------------------- + +def make_regex(styles): + """Convert dictionary with styles into a regular expression to scan input files with + + This will construct a regular expression matching LAMMPS commands. Ignores continuation + + param styles: dictionary with style names + type styles: dict + return: compiled regular expression + rtype: regex + """ + + restring = "^\\s*(" + if len(styles['command']): + restring += '(' + '|'.join(styles['command']) + ')|' + if len(styles['atom']): + restring += '(atom_style\\s+(' + '|'.join(styles['atom']) + '))|' + if len(styles['compute']): + restring += '(compute\\s+\\S+\\s+\\S+\\s+(' + '|'.join(styles['compute']) + '))|' + if len(styles['fix']): + restring += '(fix\\s+\\S+\\s+\\S+\\s+(' + '|'.join(styles['fix']) + '))|' + if len(styles['pair']): + restring += '(pair_style\\s+(' + '|'.join(styles['pair']) + '))|' + if len(styles['body']): + restring += '(atom_style\\s+body\\s+(' + '|'.join(styles['body']) + '))|' + if len(styles['bond']): + restring += '(bond_style\\s+(' + '|'.join(styles['bond']) + '))|' + if len(styles['angle']): + restring += '(angle_style\\s+(' + '|'.join(styles['angle']) + '))|' + if len(styles['dihedral']): + restring += '(dihedral_style\\s+(' + '|'.join(styles['dihedral']) + '))|' + if len(styles['improper']): + restring += '(improper_style\\s+(' + '|'.join(styles['improper']) + '))|' + if len(styles['kspace']): + restring += '(kspace_style\\s+(' + '|'.join(styles['kspace']) + '))|' + if len(styles['dump']): + restring += '(dump\\s+\\S+\\s+\\S+\\s+(' + '|'.join(styles['dump']) + '))|' + if len(styles['region']): + restring += '(region\\s+(' + '|'.join(styles['region']) + '))|' + if len(styles['integrate']): + restring += '(run_style\\s+(' + '|'.join(styles['integrate']) + '))|' + if len(styles['minimize']): + restring += '(min_style\\s+(' + '|'.join(styles['minimize']) + '))|' + + # replace last (pipe) character with closing parenthesis + length = len(restring) + restring = restring[:length-1] + ')' + # return compiled regex or None + if length > 5: + return re.compile(restring) + else: + return None + +# ---------------------------------------------------------------------- + +def get_examples_using_styles(regex, examples='examples'): + """ + Loop through LAMMPS examples tree and find all files staring with 'in.' + that have at least one line matching the regex. + + param regex: pattern matching LAMMPS commands + type regex: compiled regex + param example: path where to start looking for examples recursively + type example: string + return: list of matching example inputs + rtype: list of strings + """ + + inputs = [] + for filename in Path(examples).rglob('in.*'): + with open(filename) as f: + for line in f: + matches = regex.match(line) + if matches: + inputs.append(filename) + break + return inputs + +# ---------------------------------------------------------------------- +# ---------------------------------------------------------------------- + +if __name__ == "__main__": + headers = changed_files_from_git('develop') + styles = get_command_from_header(headers, LAMMPS_DIR) + inputs = get_examples_using_styles(make_regex(styles), os.path.join(LAMMPS_DIR,'examples')) + + print("Suggested inputs for testing:") + for inp in inputs: + print(inp) + + print("Found changes to the following styles:") + print("Commands: ", styles['command']) + print("Atom styles: ", styles['atom']) + print("Compute styles: ", styles['compute']) + print("Fix styles: ", styles['fix']) + print("Pair styles: ", styles['pair']) + print("Body styles: ", styles['body']) + print("Bond styles: ", styles['bond']) + print("Angle styles: ", styles['angle']) + print("Dihedral styles: ", styles['dihedral']) + print("Improper styles: ", styles['improper']) + print("Kspace styles: ", styles['kspace']) + print("Dump styles: ", styles['dump']) + print("Region styles: ", styles['region']) + print("Integrate styles: ", styles['integrate']) + print("Minimize styles: ", styles['minimize']) From 66d6804d2341d0c8dd533c99ee894a151381bf06 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Aug 2024 15:43:55 -0400 Subject: [PATCH 041/355] apply clang-format to some pair style headers --- src/DIPOLE/pair_lj_sf_dipole_sf.h | 2 +- src/GPU/pair_amoeba_gpu.h | 3 +-- src/GPU/pair_hippo_gpu.h | 3 +-- src/GRANULAR/pair_granular.h | 2 +- src/INTERLAYER/pair_aip_water_2dm.h | 1 - src/INTERLAYER/pair_ilp_graphene_hbn.h | 7 ++++++- src/LEPTON/pair_lepton_coul.h | 4 ++-- src/LEPTON/pair_lepton_sphere.h | 2 +- src/MANYBODY/pair_bop.h | 6 +++--- src/MANYBODY/pair_meam_spline.h | 10 ++-------- src/MANYBODY/pair_meam_sw_spline.h | 10 ++-------- src/MANYBODY/pair_rebomos.h | 2 +- src/MANYBODY/pair_tersoff_mod_c.h | 2 +- src/ML-PACE/pair_pace.h | 2 +- src/ML-QUIP/pair_quip.h | 2 ++ src/OPT/pair_aip_water_2dm_opt.h | 4 ++-- src/OPT/pair_ilp_graphene_hbn_opt.h | 5 ++--- 17 files changed, 29 insertions(+), 38 deletions(-) diff --git a/src/DIPOLE/pair_lj_sf_dipole_sf.h b/src/DIPOLE/pair_lj_sf_dipole_sf.h index 892c227a7a..df01e3dacd 100644 --- a/src/DIPOLE/pair_lj_sf_dipole_sf.h +++ b/src/DIPOLE/pair_lj_sf_dipole_sf.h @@ -26,7 +26,7 @@ namespace LAMMPS_NS { class PairLJSFDipoleSF : public Pair { public: - PairLJSFDipoleSF(class LAMMPS *_lmp) : Pair(_lmp){}; + PairLJSFDipoleSF(class LAMMPS *_lmp) : Pair(_lmp) {}; ~PairLJSFDipoleSF() override; void compute(int, int) override; void settings(int, char **) override; diff --git a/src/GPU/pair_amoeba_gpu.h b/src/GPU/pair_amoeba_gpu.h index c90339585b..3f5f89424c 100644 --- a/src/GPU/pair_amoeba_gpu.h +++ b/src/GPU/pair_amoeba_gpu.h @@ -64,8 +64,7 @@ class PairAmoebaGPU : public PairAmoeba { void udirect2b_cpu(); - template - void compute_force_from_torque(const numtyp*, double**, double*); + template void compute_force_from_torque(const numtyp *, double **, double *); }; } // namespace LAMMPS_NS diff --git a/src/GPU/pair_hippo_gpu.h b/src/GPU/pair_hippo_gpu.h index 5f36d6e71f..d00c490243 100644 --- a/src/GPU/pair_hippo_gpu.h +++ b/src/GPU/pair_hippo_gpu.h @@ -65,8 +65,7 @@ class PairHippoGPU : public PairAmoeba { void udirect2b_cpu(); - template - void compute_force_from_torque(const numtyp*, double**, double*); + template void compute_force_from_torque(const numtyp *, double **, double *); }; } // namespace LAMMPS_NS diff --git a/src/GRANULAR/pair_granular.h b/src/GRANULAR/pair_granular.h index 46c5570543..f94f4f5dff 100644 --- a/src/GRANULAR/pair_granular.h +++ b/src/GRANULAR/pair_granular.h @@ -75,7 +75,7 @@ class PairGranular : public Pair { // granular models int nmodels, maxmodels; - class Granular_NS::GranularModel** models_list; + class Granular_NS::GranularModel **models_list; int **types_indices; // optional user-specified global cutoff, per-type user-specified cutoffs diff --git a/src/INTERLAYER/pair_aip_water_2dm.h b/src/INTERLAYER/pair_aip_water_2dm.h index 295cdfffb9..91f9395214 100644 --- a/src/INTERLAYER/pair_aip_water_2dm.h +++ b/src/INTERLAYER/pair_aip_water_2dm.h @@ -30,7 +30,6 @@ class PairAIPWATER2DM : virtual public PairILPTMD { protected: void settings(int, char **) override; - }; } // namespace LAMMPS_NS diff --git a/src/INTERLAYER/pair_ilp_graphene_hbn.h b/src/INTERLAYER/pair_ilp_graphene_hbn.h index e151ecc801..5d5c1cce54 100644 --- a/src/INTERLAYER/pair_ilp_graphene_hbn.h +++ b/src/INTERLAYER/pair_ilp_graphene_hbn.h @@ -39,7 +39,12 @@ class PairILPGrapheneHBN : public Pair { static constexpr int NPARAMS_PER_LINE = 13; - enum { ILP_GrhBN, ILP_TMD, SAIP_METAL, AIP_WATER_2DM }; // for telling class variants apart in shared code + enum { + ILP_GrhBN, + ILP_TMD, + SAIP_METAL, + AIP_WATER_2DM + }; // for telling class variants apart in shared code protected: int me; diff --git a/src/LEPTON/pair_lepton_coul.h b/src/LEPTON/pair_lepton_coul.h index 8153792bd5..c58177c6cb 100644 --- a/src/LEPTON/pair_lepton_coul.h +++ b/src/LEPTON/pair_lepton_coul.h @@ -27,8 +27,8 @@ namespace LAMMPS_NS { class PairLeptonCoul : public PairLepton { public: - PairLeptonCoul(class LAMMPS *_lmp) : PairLepton(_lmp){}; - ~PairLeptonCoul() override{}; + PairLeptonCoul(class LAMMPS *_lmp) : PairLepton(_lmp) {}; + ~PairLeptonCoul() override {}; void compute(int, int) override; void settings(int, char **) override; void init_style() override; diff --git a/src/LEPTON/pair_lepton_sphere.h b/src/LEPTON/pair_lepton_sphere.h index ab586a309b..9e2642ac50 100644 --- a/src/LEPTON/pair_lepton_sphere.h +++ b/src/LEPTON/pair_lepton_sphere.h @@ -27,7 +27,7 @@ namespace LAMMPS_NS { class PairLeptonSphere : public PairLepton { public: - PairLeptonSphere(class LAMMPS *_lmp) : PairLepton(_lmp){}; + PairLeptonSphere(class LAMMPS *_lmp) : PairLepton(_lmp) {}; void compute(int, int) override; void settings(int, char **) override; diff --git a/src/MANYBODY/pair_bop.h b/src/MANYBODY/pair_bop.h index b210d1cc07..cdc6033f00 100644 --- a/src/MANYBODY/pair_bop.h +++ b/src/MANYBODY/pair_bop.h @@ -57,18 +57,18 @@ class PairBOP : public Pair { struct PairList1 { double r, dis[3]; double betaS, dBetaS, betaP, dBetaP, rep, dRep; - PairList1(){}; + PairList1() {}; }; struct PairList2 { double r, dis[3]; double rep, dRep; - PairList2(){}; + PairList2() {}; }; struct TripleList { double G, dG, cosAng, dCosAngi[3], dCosAngj[3], dCosAngk[3]; - TripleList(){}; + TripleList() {}; }; struct B_SG { diff --git a/src/MANYBODY/pair_meam_spline.h b/src/MANYBODY/pair_meam_spline.h index ee09b045cf..47f3f3d8df 100644 --- a/src/MANYBODY/pair_meam_spline.h +++ b/src/MANYBODY/pair_meam_spline.h @@ -197,16 +197,10 @@ class PairMEAMSpline : public Pair { } /// Returns the number of bytes used by this function object. - double memory_usage() const - { - return sizeof(*this) + sizeof(X[0]) * N * 3; - } + double memory_usage() const { return sizeof(*this) + sizeof(X[0]) * N * 3; } /// Returns the cutoff radius of this function. - double cutoff() const - { - return X[N - 1]; - } + double cutoff() const { return X[N - 1]; } /// Writes a Gnuplot script that plots the spline function. void writeGnuplot(const char *filename, const char *title = nullptr) const; diff --git a/src/MANYBODY/pair_meam_sw_spline.h b/src/MANYBODY/pair_meam_sw_spline.h index 9123f8c560..a5c1b0ffd4 100644 --- a/src/MANYBODY/pair_meam_sw_spline.h +++ b/src/MANYBODY/pair_meam_sw_spline.h @@ -187,16 +187,10 @@ class PairMEAMSWSpline : public Pair { } /// Returns the number of bytes used by this function object. - double memory_usage() const - { - return sizeof(*this) + sizeof(X[0]) * N * 3; - } + double memory_usage() const { return sizeof(*this) + sizeof(X[0]) * N * 3; } /// Returns the cutoff radius of this function. - double cutoff() const - { - return X[N - 1]; - } + double cutoff() const { return X[N - 1]; } /// Writes a Gnuplot script that plots the spline function. void writeGnuplot(const char *filename, const char *title = nullptr) const; diff --git a/src/MANYBODY/pair_rebomos.h b/src/MANYBODY/pair_rebomos.h index 856a52ca81..d36eb41a74 100644 --- a/src/MANYBODY/pair_rebomos.h +++ b/src/MANYBODY/pair_rebomos.h @@ -49,7 +49,7 @@ class PairREBOMoS : public Pair { int *REBO_numneigh; // # of pair neighbors for each atom int **REBO_firstneigh; // ptr to 1st neighbor of each atom - double *nM, *nS; // sum of weighting fns with REBO neighs + double *nM, *nS; // sum of weighting fns with REBO neighs double rcmin[2][2], rcmax[2][2], rcmaxsq[2][2], rcmaxp[2][2]; double Q[2][2], alpha[2][2], A[2][2], BIJc[2][2], Beta[2][2]; diff --git a/src/MANYBODY/pair_tersoff_mod_c.h b/src/MANYBODY/pair_tersoff_mod_c.h index aff1883bbd..8cea97baaf 100644 --- a/src/MANYBODY/pair_tersoff_mod_c.h +++ b/src/MANYBODY/pair_tersoff_mod_c.h @@ -26,7 +26,7 @@ namespace LAMMPS_NS { class PairTersoffMODC : public PairTersoffMOD { public: - PairTersoffMODC(class LAMMPS *lmp) : PairTersoffMOD(lmp){}; + PairTersoffMODC(class LAMMPS *lmp) : PairTersoffMOD(lmp) {}; static constexpr int NPARAMS_PER_LINE = 21; diff --git a/src/ML-PACE/pair_pace.h b/src/ML-PACE/pair_pace.h index 5cff7045fa..a972e857d2 100644 --- a/src/ML-PACE/pair_pace.h +++ b/src/ML-PACE/pair_pace.h @@ -55,7 +55,7 @@ class PairPACE : public Pair { int nmax_corerep; virtual void allocate(); - double *corerep_factor; //per-atom core-rep factor (= 1 - fcut) + double *corerep_factor; //per-atom core-rep factor (= 1 - fcut) int flag_corerep_factor; double **scale; diff --git a/src/ML-QUIP/pair_quip.h b/src/ML-QUIP/pair_quip.h index 2cbbcd4af8..7f23ab4478 100644 --- a/src/ML-QUIP/pair_quip.h +++ b/src/ML-QUIP/pair_quip.h @@ -43,8 +43,10 @@ class PairQUIP : public Pair { double init_one(int, int) override; void allocate(); void *extract(const char *, int &); + protected: double scale; + private: double cutoff; int *quip_potential; diff --git a/src/OPT/pair_aip_water_2dm_opt.h b/src/OPT/pair_aip_water_2dm_opt.h index 50b5043360..18eee58d72 100644 --- a/src/OPT/pair_aip_water_2dm_opt.h +++ b/src/OPT/pair_aip_water_2dm_opt.h @@ -1,4 +1,4 @@ - /* -*- c++ -*- ---------------------------------------------------------- +/* -*- c++ -*- ---------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator https://www.lammps.org/, Sandia National Laboratories LAMMPS development team: developers@lammps.org @@ -20,8 +20,8 @@ PairStyle(aip/water/2dm/opt,PairAIPWATER2DMOpt); #ifndef LMP_PAIR_AIP_WATER_2DM_OPT_H #define LMP_PAIR_AIP_WATER_2DM_OPT_H -#include "pair_ilp_graphene_hbn_opt.h" #include "pair_aip_water_2dm.h" +#include "pair_ilp_graphene_hbn_opt.h" namespace LAMMPS_NS { diff --git a/src/OPT/pair_ilp_graphene_hbn_opt.h b/src/OPT/pair_ilp_graphene_hbn_opt.h index 01b66bb2fa..f2fa30d595 100644 --- a/src/OPT/pair_ilp_graphene_hbn_opt.h +++ b/src/OPT/pair_ilp_graphene_hbn_opt.h @@ -35,8 +35,8 @@ class PairILPGrapheneHBNOpt : virtual public PairILPGrapheneHBN { protected: void update_internal_list(); template - void calc_atom_normal(int i, int itype, int *ILP_neigh, int nneigh, double *normal, double (*dnormdri)[3], - double (*dnormdrk)[3][3]); + void calc_atom_normal(int i, int itype, int *ILP_neigh, int nneigh, double *normal, + double (*dnormdri)[3], double (*dnormdrk)[3][3]); template void eval(); int *layered_neigh; @@ -51,7 +51,6 @@ class PairILPGrapheneHBNOpt : virtual public PairILPGrapheneHBN { SAIP_BNCH, WATER, }; - }; } // namespace LAMMPS_NS From db9a618a7b7bc4befdd36806e8fd32b72162c3d9 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Aug 2024 15:51:15 -0400 Subject: [PATCH 042/355] more fault tolerance --- tools/regression-tests/get-quick-list.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tools/regression-tests/get-quick-list.py b/tools/regression-tests/get-quick-list.py index 7c9f977b74..ff8077b20e 100644 --- a/tools/regression-tests/get-quick-list.py +++ b/tools/regression-tests/get-quick-list.py @@ -242,10 +242,11 @@ def get_examples_using_styles(regex, examples='examples'): for filename in Path(examples).rglob('in.*'): with open(filename) as f: for line in f: - matches = regex.match(line) - if matches: - inputs.append(filename) - break + if line: + matches = regex.match(line) + if matches: + inputs.append(filename) + break return inputs # ---------------------------------------------------------------------- From 27ff1fa5d4efcc5824dadf56c323f5e3ca8622d7 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Aug 2024 15:57:28 -0400 Subject: [PATCH 043/355] pass string instead of compiled regexp --- tools/regression-tests/get-quick-list.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/tools/regression-tests/get-quick-list.py b/tools/regression-tests/get-quick-list.py index ff8077b20e..70dfafa99b 100644 --- a/tools/regression-tests/get-quick-list.py +++ b/tools/regression-tests/get-quick-list.py @@ -10,7 +10,7 @@ from pathlib import Path if sys.version_info < (3,5): raise BaseException("Must use at least Python 3.5") -# infer top level LAMMPS dir +# infer top level LAMMPS dir from filename LAMMPS_DIR = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..')) # ---------------------------------------------------------------------- @@ -178,8 +178,8 @@ def make_regex(styles): param styles: dictionary with style names type styles: dict - return: compiled regular expression - rtype: regex + return: combined regular expression string + rtype: string """ restring = "^\\s*(" @@ -217,9 +217,9 @@ def make_regex(styles): # replace last (pipe) character with closing parenthesis length = len(restring) restring = restring[:length-1] + ')' - # return compiled regex or None + # return combined regex string if length > 5: - return re.compile(restring) + return restring else: return None @@ -230,7 +230,7 @@ def get_examples_using_styles(regex, examples='examples'): Loop through LAMMPS examples tree and find all files staring with 'in.' that have at least one line matching the regex. - param regex: pattern matching LAMMPS commands + param regex: string pattern matching LAMMPS commands type regex: compiled regex param example: path where to start looking for examples recursively type example: string @@ -238,15 +238,14 @@ def get_examples_using_styles(regex, examples='examples'): rtype: list of strings """ + commands = re.compile(regex) inputs = [] for filename in Path(examples).rglob('in.*'): with open(filename) as f: for line in f: - if line: - matches = regex.match(line) - if matches: - inputs.append(filename) - break + if commands.match(line): + inputs.append(filename) + break return inputs # ---------------------------------------------------------------------- From f3df42ec02adb2efb9556d051c2a47cb87a38626 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Aug 2024 16:07:26 -0400 Subject: [PATCH 044/355] include debug info --- tools/regression-tests/get-quick-list.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/regression-tests/get-quick-list.py b/tools/regression-tests/get-quick-list.py index 70dfafa99b..9cabce0c92 100644 --- a/tools/regression-tests/get-quick-list.py +++ b/tools/regression-tests/get-quick-list.py @@ -238,6 +238,7 @@ def get_examples_using_styles(regex, examples='examples'): rtype: list of strings """ + print("type ", type(regex)) commands = re.compile(regex) inputs = [] for filename in Path(examples).rglob('in.*'): @@ -252,14 +253,20 @@ def get_examples_using_styles(regex, examples='examples'): # ---------------------------------------------------------------------- if __name__ == "__main__": + headers = changed_files_from_git('develop') + print("headers\n", headers) styles = get_command_from_header(headers, LAMMPS_DIR) - inputs = get_examples_using_styles(make_regex(styles), os.path.join(LAMMPS_DIR,'examples')) + print("styles\n", styles) + regex = make_regex(styles) + print("regex: ", regex) + inputs = get_examples_using_styles(regex, os.path.join(LAMMPS_DIR,'examples')) print("Suggested inputs for testing:") for inp in inputs: print(inp) + print(type(make_regex(styles))) print("Found changes to the following styles:") print("Commands: ", styles['command']) print("Atom styles: ", styles['atom']) From f1f7eb01c84fa1a5a4cf0cc9e5fe78eee3c0ccb1 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Aug 2024 16:15:06 -0400 Subject: [PATCH 045/355] tweak settings in action --- .github/workflows/quick-regression.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index 4c029758ab..49424ca26f 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -25,6 +25,8 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 2000 + fetch-tags: true + show-progress: false - name: Install extra packages run: | From 50cd510e7930f5c074bcd2229c593a85c915169a Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Aug 2024 16:16:26 -0400 Subject: [PATCH 046/355] no merge commit so we get differences --- .github/workflows/quick-regression.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index 49424ca26f..c1fe0f7fd8 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -24,8 +24,8 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: + ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 2000 - fetch-tags: true show-progress: false - name: Install extra packages From 1c5c3b41a91f71bbf5c04b8fa6d4872792595fc1 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Aug 2024 16:20:27 -0400 Subject: [PATCH 047/355] more debugging --- .github/workflows/quick-regression.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index c1fe0f7fd8..d4e51a31e6 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -69,4 +69,5 @@ jobs: - name: Run Selected Regression Tests shell: bash run: | + git diff develop..HEAD python3 tools/regression-tests/get-quick-list.py From 613e053373ff5e8a461b39ee002336c717b74281 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Aug 2024 16:23:57 -0400 Subject: [PATCH 048/355] check out whole history --- .github/workflows/quick-regression.yml | 2 +- tools/regression-tests/get-quick-list.py | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index d4e51a31e6..8c52db0ebd 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -25,7 +25,7 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} - fetch-depth: 2000 + fetch-depth: 0 show-progress: false - name: Install extra packages diff --git a/tools/regression-tests/get-quick-list.py b/tools/regression-tests/get-quick-list.py index 9cabce0c92..47a3a07b9b 100644 --- a/tools/regression-tests/get-quick-list.py +++ b/tools/regression-tests/get-quick-list.py @@ -238,7 +238,6 @@ def get_examples_using_styles(regex, examples='examples'): rtype: list of strings """ - print("type ", type(regex)) commands = re.compile(regex) inputs = [] for filename in Path(examples).rglob('in.*'): @@ -257,10 +256,9 @@ if __name__ == "__main__": headers = changed_files_from_git('develop') print("headers\n", headers) styles = get_command_from_header(headers, LAMMPS_DIR) - print("styles\n", styles) regex = make_regex(styles) - print("regex: ", regex) - inputs = get_examples_using_styles(regex, os.path.join(LAMMPS_DIR,'examples')) + if regex: + inputs = get_examples_using_styles(regex, os.path.join(LAMMPS_DIR,'examples')) print("Suggested inputs for testing:") for inp in inputs: From a204f8e69f5111bf155eb86aba8a45b44cece1e5 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Aug 2024 16:30:02 -0400 Subject: [PATCH 049/355] refer to branch with origin/develop --- .github/workflows/quick-regression.yml | 1 - tools/regression-tests/get-quick-list.py | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index 8c52db0ebd..b8fe12ba14 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -69,5 +69,4 @@ jobs: - name: Run Selected Regression Tests shell: bash run: | - git diff develop..HEAD python3 tools/regression-tests/get-quick-list.py diff --git a/tools/regression-tests/get-quick-list.py b/tools/regression-tests/get-quick-list.py index 47a3a07b9b..44f6d90770 100644 --- a/tools/regression-tests/get-quick-list.py +++ b/tools/regression-tests/get-quick-list.py @@ -253,8 +253,7 @@ def get_examples_using_styles(regex, examples='examples'): if __name__ == "__main__": - headers = changed_files_from_git('develop') - print("headers\n", headers) + headers = changed_files_from_git('origin/develop') styles = get_command_from_header(headers, LAMMPS_DIR) regex = make_regex(styles) if regex: From dc4e4988a9d62e7a39bab9f66be6e855b7785ace Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Aug 2024 16:37:35 -0400 Subject: [PATCH 050/355] do not fetch specific commit hash --- .github/workflows/quick-regression.yml | 4 +++- tools/regression-tests/get-quick-list.py | 7 +++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index b8fe12ba14..a4bb55dffc 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -24,7 +24,6 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: - ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 show-progress: false @@ -69,4 +68,7 @@ jobs: - name: Run Selected Regression Tests shell: bash run: | + git branch + git branch -r + git diff origin/develop..HEAD python3 tools/regression-tests/get-quick-list.py diff --git a/tools/regression-tests/get-quick-list.py b/tools/regression-tests/get-quick-list.py index 44f6d90770..61ae31a7b3 100644 --- a/tools/regression-tests/get-quick-list.py +++ b/tools/regression-tests/get-quick-list.py @@ -258,10 +258,9 @@ if __name__ == "__main__": regex = make_regex(styles) if regex: inputs = get_examples_using_styles(regex, os.path.join(LAMMPS_DIR,'examples')) - - print("Suggested inputs for testing:") - for inp in inputs: - print(inp) + print("Suggested inputs for testing:") + for inp in inputs: + print(inp) print(type(make_regex(styles))) print("Found changes to the following styles:") From bda862a3a29d6f27b7ad7ddd463c3012dd7d0eb4 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Aug 2024 16:50:03 -0400 Subject: [PATCH 051/355] actually honor the branch argument --- .github/workflows/quick-regression.yml | 3 --- tools/regression-tests/get-quick-list.py | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index a4bb55dffc..bee8835e3f 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -68,7 +68,4 @@ jobs: - name: Run Selected Regression Tests shell: bash run: | - git branch - git branch -r - git diff origin/develop..HEAD python3 tools/regression-tests/get-quick-list.py diff --git a/tools/regression-tests/get-quick-list.py b/tools/regression-tests/get-quick-list.py index 61ae31a7b3..92f26b054b 100644 --- a/tools/regression-tests/get-quick-list.py +++ b/tools/regression-tests/get-quick-list.py @@ -31,7 +31,7 @@ def changed_files_from_git(branch='develop'): # get list of changed files relative to the develop branch from git output = None try: - output = subprocess.run('git diff --diff-filter=MA --name-status develop', + output = subprocess.run('git diff --diff-filter=MA --name-status ' + branch, shell=True, capture_output=True) except: pass From abbfa9470e2344e7db97d16bcfa3937c8da7f131 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Aug 2024 17:07:12 -0400 Subject: [PATCH 052/355] remove debug print() statement --- tools/regression-tests/get-quick-list.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/regression-tests/get-quick-list.py b/tools/regression-tests/get-quick-list.py index 92f26b054b..2fa596168c 100644 --- a/tools/regression-tests/get-quick-list.py +++ b/tools/regression-tests/get-quick-list.py @@ -262,7 +262,6 @@ if __name__ == "__main__": for inp in inputs: print(inp) - print(type(make_regex(styles))) print("Found changes to the following styles:") print("Commands: ", styles['command']) print("Atom styles: ", styles['atom']) From 7475e5a5eff1593b823497e10549f2b8c54edaea Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Fri, 23 Aug 2024 11:49:20 -0500 Subject: [PATCH 053/355] run the regression tester tool with the list of suggested inputs --- .github/workflows/quick-regression.yml | 9 +++++++++ tools/regression-tests/get-quick-list.py | 20 ++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index bee8835e3f..90d3932605 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -46,6 +46,11 @@ jobs: shell: bash run: | ccache -z + python3 -m venv linuxenv + source linuxenv/bin/activate + python3 -m pip install numpy + python3 -m pip install pyyaml + python3 -m pip install junit_xml cmake -S cmake -B build \ -C cmake/presets/gcc.cmake \ -C cmake/presets/most.cmake \ @@ -69,3 +74,7 @@ jobs: shell: bash run: | python3 tools/regression-tests/get-quick-list.py + python3 tools/regression-tests/run_tests.py \ + --lmp-bin=build/lmp \ + --config-file=tools/regression-tests/config.yaml \ + --list-input=folder_list.txt diff --git a/tools/regression-tests/get-quick-list.py b/tools/regression-tests/get-quick-list.py index 2fa596168c..c9ec9e3971 100644 --- a/tools/regression-tests/get-quick-list.py +++ b/tools/regression-tests/get-quick-list.py @@ -258,10 +258,30 @@ if __name__ == "__main__": regex = make_regex(styles) if regex: inputs = get_examples_using_styles(regex, os.path.join(LAMMPS_DIR,'examples')) + + # TODO: modify the regression tester tool to process the raw list of input scripts + folder_list = [] print("Suggested inputs for testing:") for inp in inputs: print(inp) + # get the folder that contains the input script + full_path = str(inp) + folder = full_path.rsplit('/', 1)[0] + # add unique folders in the list + if folder not in folder_list: + folder_list.append(folder) + + # input_list.txt is used for the regression tester tool + # that lists the individual subfolders and the number of input scripts therein + with open('folder_list.txt', 'w') as f: + for folder in folder_list: + cmd_str = f"ls {folder}/in.* | wc -l" + p = subprocess.run(cmd_str, shell=True, text=True, capture_output=True) + num_input = p.stdout.split('\n')[0] + f.write(folder + ' ' + num_input + '\n') + + print("Found changes to the following styles:") print("Commands: ", styles['command']) print("Atom styles: ", styles['atom']) From ebe3bd2f7e6f8e978579da284ffa8dd30633f9e5 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Fri, 23 Aug 2024 11:59:16 -0500 Subject: [PATCH 054/355] activate the env before running the python scripts --- .github/workflows/quick-regression.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index 90d3932605..7ba4e8465b 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -73,6 +73,7 @@ jobs: - name: Run Selected Regression Tests shell: bash run: | + source linuxenv/bin/activate python3 tools/regression-tests/get-quick-list.py python3 tools/regression-tests/run_tests.py \ --lmp-bin=build/lmp \ From b1d40014a6326d7466f97b9d3ad880dff0ecfb95 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Fri, 23 Aug 2024 14:00:13 -0500 Subject: [PATCH 055/355] allow to download artifact from the regression test --- .github/workflows/quick-regression.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index 7ba4e8465b..3aed9384c9 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -71,6 +71,7 @@ jobs: ccache -s - name: Run Selected Regression Tests + uses: actions/download-artifact@v4 shell: bash run: | source linuxenv/bin/activate @@ -79,3 +80,8 @@ jobs: --lmp-bin=build/lmp \ --config-file=tools/regression-tests/config.yaml \ --list-input=folder_list.txt + tar -cvf quick-regression-test.tar run.log progress.yaml + with: + name: quick-regression-test-artifact + path: quick-regression-test.tar + From 0d005789787319443f54a7ff7bf37cb0cd7e397a Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Fri, 23 Aug 2024 14:03:25 -0500 Subject: [PATCH 056/355] move download artifacts to a separate step --- .github/workflows/quick-regression.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index 3aed9384c9..fd7d292c2b 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -71,7 +71,6 @@ jobs: ccache -s - name: Run Selected Regression Tests - uses: actions/download-artifact@v4 shell: bash run: | source linuxenv/bin/activate @@ -81,7 +80,11 @@ jobs: --config-file=tools/regression-tests/config.yaml \ --list-input=folder_list.txt tar -cvf quick-regression-test.tar run.log progress.yaml + + - name: Download artifacts + uses: actions/download-artifact@v4 with: name: quick-regression-test-artifact path: quick-regression-test.tar + retention-days: 5 From e1b324a3e95ac8d960182e154db010c6695bc83c Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Fri, 23 Aug 2024 14:18:40 -0500 Subject: [PATCH 057/355] upload artifacts --- .github/workflows/quick-regression.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index fd7d292c2b..2e44e24275 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -81,10 +81,10 @@ jobs: --list-input=folder_list.txt tar -cvf quick-regression-test.tar run.log progress.yaml - - name: Download artifacts - uses: actions/download-artifact@v4 + - name: Upload artifacts + uses: actions/upload-artifact@v4 with: name: quick-regression-test-artifact path: quick-regression-test.tar - retention-days: 5 + From b69a9847f7c6e0db6dc92dbde7c3d94d4b977c56 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Fri, 23 Aug 2024 14:40:07 -0500 Subject: [PATCH 058/355] add a config file for running regression tests in serial (no mpirun), modify run_tests.py to handle this case --- .github/workflows/quick-regression.yml | 2 +- tools/regression-tests/config_serial.yaml | 46 +++++++++++++++++++++++ tools/regression-tests/run_tests.py | 4 +- 3 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 tools/regression-tests/config_serial.yaml diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index 2e44e24275..2726e70650 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -77,7 +77,7 @@ jobs: python3 tools/regression-tests/get-quick-list.py python3 tools/regression-tests/run_tests.py \ --lmp-bin=build/lmp \ - --config-file=tools/regression-tests/config.yaml \ + --config-file=tools/regression-tests/config_serial.yaml \ --list-input=folder_list.txt tar -cvf quick-regression-test.tar run.log progress.yaml diff --git a/tools/regression-tests/config_serial.yaml b/tools/regression-tests/config_serial.yaml new file mode 100644 index 0000000000..900e3d1736 --- /dev/null +++ b/tools/regression-tests/config_serial.yaml @@ -0,0 +1,46 @@ +--- + lmp_binary: "" + nprocs: "1" + args: "-cite none" + mpiexec: "" + mpiexec_numproc_flag: "" + tolerance: + PotEng: + abs: 1e-4 + rel: 1e-7 + TotEng: + abs: 1e-4 + rel: 1e-7 + Press: + abs: 1e-4 + rel: 1e-7 + Temp: + abs: 1e-4 + rel: 1e-7 + E_vdwl: + abs: 1e-3 + rel: 1e-7 + overrides: + in.rigid.tnr: + Temp: + abs: 1e-3 + rel: 1e-5 + Press: + abs: 1e-2 + rel: 1e-4 + skip: + [ in.rigid.poems3, + in.rigid.poems4, + in.rigid.poems5, + in.peptide, + in.voronoi, + in.voronoi.2d, + in.voronoi.data, + in.*_imd*, + in.bucky-plus-cnt*, + ] + + nugget: 1.0 + epsilon: 1e-16 + + diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index b2144478ec..4d9fdfaa22 100644 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -710,7 +710,9 @@ def get_lammps_build_configuration(lmp_binary): - wrap subprocess with try/catch to handle exceptions ''' def execute(lmp_binary, config, input_file_name, generate_ref_yaml=False): - cmd_str = config['mpiexec'] + " " + config['mpiexec_numproc_flag'] + " " + config['nprocs'] + " " + cmd_str = "" + if config['mpiexec']: + cmd_str += config['mpiexec'] + " " + config['mpiexec_numproc_flag'] + " " + config['nprocs'] + " " cmd_str += lmp_binary + " -in " + input_file_name + " " + config['args'] logger.info(f" Executing: {cmd_str}") p = subprocess.run(cmd_str, shell=True, text=True, capture_output=True) From 380447c6ba8c980747895e7dd89730e71a44be78 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Fri, 23 Aug 2024 14:44:08 -0500 Subject: [PATCH 059/355] updated regression test config files --- tools/regression-tests/config.yaml | 11 ++++------- tools/regression-tests/config_serial.yaml | 10 +++------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/tools/regression-tests/config.yaml b/tools/regression-tests/config.yaml index 24f1ab0d67..372d0db10b 100644 --- a/tools/regression-tests/config.yaml +++ b/tools/regression-tests/config.yaml @@ -29,13 +29,10 @@ abs: 1e-2 rel: 1e-4 skip: - [ in.rigid.poems3, - in.rigid.poems4, - in.rigid.poems5, - in.peptide, - in.voronoi, - in.voronoi.2d, - in.voronoi.data, + [ + in.displ, + in.displ2, + in.dos, in.*_imd*, in.bucky-plus-cnt*, ] diff --git a/tools/regression-tests/config_serial.yaml b/tools/regression-tests/config_serial.yaml index 900e3d1736..1fe3f48353 100644 --- a/tools/regression-tests/config_serial.yaml +++ b/tools/regression-tests/config_serial.yaml @@ -29,13 +29,9 @@ abs: 1e-2 rel: 1e-4 skip: - [ in.rigid.poems3, - in.rigid.poems4, - in.rigid.poems5, - in.peptide, - in.voronoi, - in.voronoi.2d, - in.voronoi.data, + [ in.displ, + in.displ2, + in.dos, in.*_imd*, in.bucky-plus-cnt*, ] From 9e172665f24ef8b25aa564500ffd8d41b443886d Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Fri, 23 Aug 2024 15:45:13 -0500 Subject: [PATCH 060/355] enable full regression tests --- .github/workflows/full-regression.yml | 35 ++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index e3c6835e77..4f18632c99 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -43,6 +43,11 @@ jobs: shell: bash run: | ccache -z + python3 -m venv linuxenv + source linuxenv/bin/activate + python3 -m pip install numpy + python3 -m pip install pyyaml + python3 -m pip install junit_xml cmake -S cmake -B build \ -C cmake/presets/gcc.cmake \ -C cmake/presets/most.cmake \ @@ -62,8 +67,32 @@ jobs: cmake --build build ccache -s - - name: Run Regression Tests + run_regression_tests: + - name: Analyze top-level examples folder, split into 8 seperate subfolder lists shell: bash run: | - echo 'Linux binary is here:' - ls -lh build/lmp + source linuxenv/bin/activate + python3 tools/regression-tests/run_tests.py \ + --lmp-bin=build/lmp \ + --examples-top-level=examples --analyze --num-workers=8 + + - name: Run regression tests with 8 workers each processing a list of subfolders + strategy: + matrix: + idx: [ 0, 1, 2, 3, 4, 5, 6, 7 ] + run: | + source linuxenv/bin/activate + python3 tools/regression-tests/run_tests.py \ + --lmp-bin=build/lmp \ + --config-file=tools/regression-tests/config_serial.yaml \ + --list-input=input-list-${{ matrix.idx }}.txt \ + --output-file=output-${{ matrix.idx }}.xml \ + --progress-file=progress-${{ matrix.idx }}.yaml \ + --log-file=run-${{ matrix.idx }}.log > screen-${{ matrix.idx }}.txt + tar -cvf full-regression-test.tar run*.log progress* output* screen*.txt + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: full-regression-test-artifact + path: full-regression-test.tar \ No newline at end of file From 5a9b742086bd9307ce0ef83123754578ff39c88a Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Fri, 23 Aug 2024 15:52:41 -0500 Subject: [PATCH 061/355] experiment with the matrix feature at the job level --- .github/workflows/full-regression.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index 4f18632c99..1ace372140 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -17,6 +17,9 @@ jobs: runs-on: ubuntu-latest env: CCACHE_DIR: ${{ github.workspace }}/.ccache + strategy: + matrix: + idx: [ 0, 1, 2, 3, 4, 5, 6, 7 ] steps: - name: Checkout repository @@ -67,7 +70,6 @@ jobs: cmake --build build ccache -s - run_regression_tests: - name: Analyze top-level examples folder, split into 8 seperate subfolder lists shell: bash run: | @@ -77,9 +79,6 @@ jobs: --examples-top-level=examples --analyze --num-workers=8 - name: Run regression tests with 8 workers each processing a list of subfolders - strategy: - matrix: - idx: [ 0, 1, 2, 3, 4, 5, 6, 7 ] run: | source linuxenv/bin/activate python3 tools/regression-tests/run_tests.py \ From d09e9d46fa030f2d292fc72b37ff296607285b4e Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Fri, 23 Aug 2024 16:00:15 -0500 Subject: [PATCH 062/355] specify the config file when analyzing the examples folder --- .github/workflows/full-regression.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index 1ace372140..fc7c05a894 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -76,6 +76,7 @@ jobs: source linuxenv/bin/activate python3 tools/regression-tests/run_tests.py \ --lmp-bin=build/lmp \ + --config-file=tools/regression-tests/config_serial.yaml \ --examples-top-level=examples --analyze --num-workers=8 - name: Run regression tests with 8 workers each processing a list of subfolders From afb1e499af1a800aae6551ba6eeda49d195d89ef Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Fri, 23 Aug 2024 16:28:51 -0500 Subject: [PATCH 063/355] debugging the issue with the runs, list index out of range for run 7 --- .github/workflows/full-regression.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index fc7c05a894..71b176e4ca 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -88,11 +88,8 @@ jobs: --list-input=input-list-${{ matrix.idx }}.txt \ --output-file=output-${{ matrix.idx }}.xml \ --progress-file=progress-${{ matrix.idx }}.yaml \ - --log-file=run-${{ matrix.idx }}.log > screen-${{ matrix.idx }}.txt - tar -cvf full-regression-test.tar run*.log progress* output* screen*.txt + --log-file=run-${{ matrix.idx }}.log - name: Upload artifacts uses: actions/upload-artifact@v4 - with: - name: full-regression-test-artifact - path: full-regression-test.tar \ No newline at end of file + From 149ae7463164d51ed84f59bac5ce14c18a36dfd9 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Fri, 23 Aug 2024 17:06:11 -0500 Subject: [PATCH 064/355] debug matrix strategy --- .github/workflows/full-regression.yml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index 71b176e4ca..d550f5b728 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -11,15 +11,12 @@ on: jobs: build: - name: Full Regression Test + name: Build # restrict to official LAMMPS repository if: ${{ github.repository == 'lammps/lammps' }} runs-on: ubuntu-latest env: CCACHE_DIR: ${{ github.workspace }}/.ccache - strategy: - matrix: - idx: [ 0, 1, 2, 3, 4, 5, 6, 7 ] steps: - name: Checkout repository @@ -79,6 +76,19 @@ jobs: --config-file=tools/regression-tests/config_serial.yaml \ --examples-top-level=examples --analyze --num-workers=8 + run_tests: + name: Full Regression Test + # restrict to official LAMMPS repository + if: ${{ github.repository == 'lammps/lammps' }} + runs-on: ubuntu-latest + env: + CCACHE_DIR: ${{ github.workspace }}/.ccache + strategy: + max-parallel: 2 + matrix: + idx: [ 0, 1, 2, 3, 4, 5, 6, 7 ] + + steps: - name: Run regression tests with 8 workers each processing a list of subfolders run: | source linuxenv/bin/activate From ec0cdb8bdcf90a6e5a94aced5c30872177aaf3ab Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 24 Aug 2024 02:11:18 -0400 Subject: [PATCH 065/355] tweak background color settings --- tools/lammps-gui/codeeditor.cpp | 3 +++ tools/lammps-gui/lammpsgui.cpp | 1 + 2 files changed, 4 insertions(+) diff --git a/tools/lammps-gui/codeeditor.cpp b/tools/lammps-gui/codeeditor.cpp index fd86b5199e..f5ecdd0b0f 100644 --- a/tools/lammps-gui/codeeditor.cpp +++ b/tools/lammps-gui/codeeditor.cpp @@ -218,7 +218,10 @@ CodeEditor::CodeEditor(QWidget *parent) : help_index.close(); } + setBackgroundRole(QPalette::Light); lineNumberArea = new LineNumberArea(this); + lineNumberArea->setBackgroundRole(QPalette::Dark); + lineNumberArea->setAutoFillBackground(true); connect(this, &CodeEditor::blockCountChanged, this, &CodeEditor::updateLineNumberAreaWidth); connect(this, &CodeEditor::updateRequest, this, &CodeEditor::updateLineNumberArea); updateLineNumberAreaWidth(0); diff --git a/tools/lammps-gui/lammpsgui.cpp b/tools/lammps-gui/lammpsgui.cpp index 8162507d02..d4705645d2 100644 --- a/tools/lammps-gui/lammpsgui.cpp +++ b/tools/lammps-gui/lammpsgui.cpp @@ -90,6 +90,7 @@ LammpsGui::LammpsGui(QWidget *parent, const char *filename) : // use $HOME if we get dropped to "/" like on macOS if (current_dir == "/") current_dir = QDir::homePath(); inspectList.clear(); + setAutoFillBackground(true); // restore and initialize settings QSettings settings; From 72873b0dca14430af1e7fd1f561c41b4c895558e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 24 Aug 2024 02:56:47 -0400 Subject: [PATCH 066/355] line number area attempts to be dark mode compatible --- tools/lammps-gui/codeeditor.cpp | 2 +- tools/lammps-gui/lammps-gui.appdata.xml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/lammps-gui/codeeditor.cpp b/tools/lammps-gui/codeeditor.cpp index f5ecdd0b0f..8083f1e2c5 100644 --- a/tools/lammps-gui/codeeditor.cpp +++ b/tools/lammps-gui/codeeditor.cpp @@ -672,7 +672,7 @@ void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event) if (block.isVisible() && bottom >= event->rect().top()) { QString number = QString::number(blockNumber + 1) + " "; if ((highlight == NO_HIGHLIGHT) || (blockNumber != std::abs(highlight))) { - painter.setPen(Qt::black); + painter.setPen(palette().color(QPalette::WindowText)); } else { number = QString(">") + QString::number(blockNumber + 1) + "<"; if (highlight < 0) diff --git a/tools/lammps-gui/lammps-gui.appdata.xml b/tools/lammps-gui/lammps-gui.appdata.xml index 95652129a1..46fcc86125 100644 --- a/tools/lammps-gui/lammps-gui.appdata.xml +++ b/tools/lammps-gui/lammps-gui.appdata.xml @@ -56,6 +56,9 @@ + Added search and replace functionality. + Converged command line argument parsing using Qt facilities + Dark mode compatible From 1b3652583b7e2cb6edb38132f1f32a4101b54289 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 24 Aug 2024 11:39:54 -0400 Subject: [PATCH 067/355] implement text search and replace functionality --- tools/lammps-gui/CMakeLists.txt | 2 + tools/lammps-gui/findandreplace.cpp | 148 ++++++++++++++++++++++++ tools/lammps-gui/findandreplace.h | 46 ++++++++ tools/lammps-gui/icons/search.png | Bin 0 -> 4821 bytes tools/lammps-gui/imageviewer.h | 3 +- tools/lammps-gui/lammps-gui.appdata.xml | 2 +- tools/lammps-gui/lammpsgui.cpp | 10 ++ tools/lammps-gui/lammpsgui.h | 1 + tools/lammps-gui/lammpsgui.qrc | 1 + tools/lammps-gui/lammpsgui.ui | 15 ++- 10 files changed, 225 insertions(+), 3 deletions(-) create mode 100644 tools/lammps-gui/findandreplace.cpp create mode 100644 tools/lammps-gui/findandreplace.h create mode 100644 tools/lammps-gui/icons/search.png diff --git a/tools/lammps-gui/CMakeLists.txt b/tools/lammps-gui/CMakeLists.txt index 73e945820b..1732169bba 100644 --- a/tools/lammps-gui/CMakeLists.txt +++ b/tools/lammps-gui/CMakeLists.txt @@ -130,6 +130,8 @@ set(PROJECT_SOURCES chartviewer.h codeeditor.cpp codeeditor.h + findandreplace.cpp + findandreplace.h helpers.cpp highlighter.cpp highlighter.h diff --git a/tools/lammps-gui/findandreplace.cpp b/tools/lammps-gui/findandreplace.cpp new file mode 100644 index 0000000000..d3ef6409b5 --- /dev/null +++ b/tools/lammps-gui/findandreplace.cpp @@ -0,0 +1,148 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "findandreplace.h" + +#include "codeeditor.h" +#include "lammpsgui.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* ---------------------------------------------------------------------- */ + +FindAndReplace::FindAndReplace(CodeEditor *_editor, QWidget *parent) : + QDialog(parent), editor(_editor), search(nullptr), replace(nullptr), withcase(nullptr), + wrap(nullptr), whole(nullptr) +{ + auto *layout = new QGridLayout; + search = new QLineEdit; + replace = new QLineEdit; + withcase = new QCheckBox("Match case"); + wrap = new QCheckBox("Wrap around"); + whole = new QCheckBox("Whole word"); + auto *next = new QPushButton("Next"); + auto *replone = new QPushButton("Replace"); + auto *replall = new QPushButton("Replace All"); + auto *done = new QPushButton("Done"); + + layout->addWidget(new QLabel("Find:"), 0, 0, Qt::AlignRight); + layout->addWidget(search, 0, 1, 1, 2, Qt::AlignLeft); + layout->addWidget(new QLabel("Replace with:"), 1, 0, Qt::AlignRight); + layout->addWidget(replace, 1, 1, 1, 2, Qt::AlignLeft); + layout->addWidget(withcase, 2, 0, Qt::AlignLeft); + layout->addWidget(wrap, 2, 1, Qt::AlignLeft); + layout->addWidget(whole, 2, 2, Qt::AlignLeft); + wrap->setChecked(true); + + auto *buttons = new QHBoxLayout; + buttons->addWidget(next); + buttons->addWidget(replone); + buttons->addWidget(replall); + buttons->addWidget(done); + layout->addLayout(buttons, 3, 0, 1, 3, Qt::AlignHCenter); + + connect(next, &QPushButton::released, this, &FindAndReplace::find_next); + connect(replone, &QPushButton::released, this, &FindAndReplace::replace_next); + connect(replall, &QPushButton::released, this, &FindAndReplace::replace_all); + connect(done, &QPushButton::released, this, &QDialog::accept); + + auto action = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q), this); + connect(action, &QShortcut::activated, this, &FindAndReplace::quit); + + setLayout(layout); + setWindowIcon(QIcon(":/icons/lammps-icon-128x128.png")); + setWindowTitle("LAMMPS-GUI - Find and Replace"); +} + +/* ---------------------------------------------------------------------- */ + +void FindAndReplace::find_next() +{ + auto text = search->text(); + + int find_flags = 0; + if (withcase->isChecked()) find_flags |= QTextDocument::FindCaseSensitively; + if (whole->isChecked()) find_flags |= QTextDocument::FindWholeWords; + + if (!text.isEmpty()) { + if (!editor->find(text, (QTextDocument::FindFlag)find_flags) && wrap->isChecked()) { + // nothing found from the current position to the end, reposition cursor and beginning + editor->moveCursor(QTextCursor::Start, QTextCursor::MoveAnchor); + editor->find(text, (QTextDocument::FindFlag)find_flags); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void FindAndReplace::replace_next() +{ + auto text = search->text(); + if (text.isEmpty()) return; + + auto cursor = editor->textCursor(); + auto flag = withcase->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive; + + // if selected text at cursor location matches search text, replace + if (QString::compare(cursor.selectedText(), search->text(), flag) == 0) + cursor.insertText(replace->text()); + + find_next(); +} + +/* ---------------------------------------------------------------------- */ + +void FindAndReplace::replace_all() +{ + auto text = search->text(); + if (text.isEmpty()) return; + + // drop selection if we have one + auto cursor = editor->textCursor(); + if (cursor.hasSelection()) cursor.movePosition(QTextCursor::Left); + + find_next(); + cursor = editor->textCursor(); + + // keep replacing until find_next() does not find anything anymore + while (cursor.hasSelection()) { + cursor.insertText(replace->text()); + find_next(); + cursor = editor->textCursor(); + } +} + +/* ---------------------------------------------------------------------- */ + +void FindAndReplace::quit() +{ + LammpsGui *main = nullptr; + for (QWidget *widget : QApplication::topLevelWidgets()) + if (widget->objectName() == "LammpsGui") main = dynamic_cast(widget); + if (main) main->quit(); +} + +// Local Variables: +// c-basic-offset: 4 +// End: diff --git a/tools/lammps-gui/findandreplace.h b/tools/lammps-gui/findandreplace.h new file mode 100644 index 0000000000..7c34c50543 --- /dev/null +++ b/tools/lammps-gui/findandreplace.h @@ -0,0 +1,46 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef FIND_AND_REPLACE_H +#define FIND_AND_REPLACE_H + +#include "codeeditor.h" +#include + +class QLineEdit; +class QCheckBox; + +class FindAndReplace : public QDialog { + Q_OBJECT + +public: + explicit FindAndReplace(CodeEditor *_editor, QWidget *parent = nullptr); + ~FindAndReplace() = default; + +private slots: + void find_next(); + void replace_next(); + void replace_all(); + void quit(); + +private: + CodeEditor *editor; + QLineEdit *search, *replace; + QCheckBox *withcase, *wrap, *whole; +}; + +#endif + +// Local Variables: +// c-basic-offset: 4 +// End: diff --git a/tools/lammps-gui/icons/search.png b/tools/lammps-gui/icons/search.png new file mode 100644 index 0000000000000000000000000000000000000000..1790200d49d94c182df7ce9574617faccdb9edb7 GIT binary patch literal 4821 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 KSPj(b>3;#qPV4Ic literal 0 HcmV?d00001 diff --git a/tools/lammps-gui/imageviewer.h b/tools/lammps-gui/imageviewer.h index 8e72cea7bf..94632bde89 100644 --- a/tools/lammps-gui/imageviewer.h +++ b/tools/lammps-gui/imageviewer.h @@ -34,7 +34,8 @@ class ImageViewer : public QDialog { Q_OBJECT public: - explicit ImageViewer(const QString &fileName, LammpsWrapper *_lammps, QWidget *parent = nullptr); + explicit ImageViewer(const QString &fileName, LammpsWrapper *_lammps, + QWidget *parent = nullptr); private slots: void saveAs(); diff --git a/tools/lammps-gui/lammps-gui.appdata.xml b/tools/lammps-gui/lammps-gui.appdata.xml index 46fcc86125..a6a384af1b 100644 --- a/tools/lammps-gui/lammps-gui.appdata.xml +++ b/tools/lammps-gui/lammps-gui.appdata.xml @@ -58,7 +58,7 @@ Added search and replace functionality. Converged command line argument parsing using Qt facilities - Dark mode compatible + Added dark mode adjustments diff --git a/tools/lammps-gui/lammpsgui.cpp b/tools/lammps-gui/lammpsgui.cpp index d4705645d2..362a8f00cb 100644 --- a/tools/lammps-gui/lammpsgui.cpp +++ b/tools/lammps-gui/lammpsgui.cpp @@ -15,6 +15,7 @@ #include "chartviewer.h" #include "fileviewer.h" +#include "findandreplace.h" #include "helpers.h" #include "highlighter.h" #include "imageviewer.h" @@ -206,6 +207,7 @@ LammpsGui::LammpsGui(QWidget *parent, const char *filename) : connect(ui->actionPaste, &QAction::triggered, this, &LammpsGui::paste); connect(ui->actionUndo, &QAction::triggered, this, &LammpsGui::undo); connect(ui->actionRedo, &QAction::triggered, this, &LammpsGui::redo); + connect(ui->actionSearchAndReplace, &QAction::triggered, this, &LammpsGui::findandreplace); connect(ui->actionRun_Buffer, &QAction::triggered, this, &LammpsGui::run_buffer); connect(ui->actionRun_File, &QAction::triggered, this, &LammpsGui::run_file); connect(ui->actionStop_LAMMPS, &QAction::triggered, this, &LammpsGui::stop_run); @@ -1863,6 +1865,14 @@ void LammpsGui::edit_variables() } } +void LammpsGui::findandreplace() +{ + FindAndReplace find(ui->textEdit, this); + find.setFont(font()); + find.setObjectName("find"); + find.exec(); +} + void LammpsGui::preferences() { QSettings settings; diff --git a/tools/lammps-gui/lammpsgui.h b/tools/lammps-gui/lammpsgui.h index 0cf6677149..9185b7a535 100644 --- a/tools/lammps-gui/lammpsgui.h +++ b/tools/lammps-gui/lammpsgui.h @@ -115,6 +115,7 @@ private slots: void paste(); void undo(); void redo(); + void findandreplace(); void run_buffer() { do_run(true); } void run_file() { do_run(false); } diff --git a/tools/lammps-gui/lammpsgui.qrc b/tools/lammps-gui/lammpsgui.qrc index cf9dd20dda..8111edd44b 100644 --- a/tools/lammps-gui/lammpsgui.qrc +++ b/tools/lammps-gui/lammpsgui.qrc @@ -58,6 +58,7 @@ icons/preferences-desktop.png icons/process-stop.png icons/run-file.png + icons/search.png icons/system-box.png icons/system-help.png icons/system-run.png diff --git a/tools/lammps-gui/lammpsgui.ui b/tools/lammps-gui/lammpsgui.ui index 1517168327..045e0f84a8 100644 --- a/tools/lammps-gui/lammpsgui.ui +++ b/tools/lammps-gui/lammpsgui.ui @@ -62,6 +62,8 @@ + + @@ -312,12 +314,23 @@ Ctrl+Shift+H + + + + + + &Find and Replace... + + + Ctrl+F + + - Pre&ferences... + P&references... Ctrl+P From d98a3d61dac278d777e3df6dc09fd7c18b6492fa Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 24 Aug 2024 11:40:15 -0400 Subject: [PATCH 068/355] reduce outlier margin from 5 sigma to 4 sigma --- tools/lammps-gui/chartviewer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/lammps-gui/chartviewer.cpp b/tools/lammps-gui/chartviewer.cpp index 71ba07de4c..4ea5449f3f 100644 --- a/tools/lammps-gui/chartviewer.cpp +++ b/tools/lammps-gui/chartviewer.cpp @@ -424,7 +424,7 @@ void ChartViewer::add_data(int step, double data) if (last_step < step) { last_step = step; - // do not add data that deviates by more than 5 sigma from the average + // do not add data that deviates by more than 4 sigma from the average // over the last 5 to 20 data items. this is a hack to work around // getting corrupted data from lammps_get_last_thermo() const auto &points = series->points(); @@ -442,7 +442,7 @@ void ChartViewer::add_data(int step, double data) const double num = count - first; const double avg = ysum / num; const double avgsq = ysumsq / num; - if (fabs(data - avg) > (5.0 * sqrt(avgsq - (avg * avg)))) return; + if (fabs(data - avg) > (4.0 * sqrt(avgsq - (avg * avg)))) return; } series->append(step, data); From 35d8a3d68ec218207633abac1bb3194810f05371 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 24 Aug 2024 14:32:01 -0400 Subject: [PATCH 069/355] Document 'Find and Replace' dialog --- cmake/packaging/LAMMPS_DMG_Background.xcf | Bin 0 -> 103808 bytes doc/src/JPG/lammps-gui-find.png | Bin 0 -> 115375 bytes tools/lammps-gui/findandreplace.cpp | 8 ++++---- 3 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 cmake/packaging/LAMMPS_DMG_Background.xcf create mode 100644 doc/src/JPG/lammps-gui-find.png diff --git a/cmake/packaging/LAMMPS_DMG_Background.xcf b/cmake/packaging/LAMMPS_DMG_Background.xcf new file mode 100644 index 0000000000000000000000000000000000000000..cff3222717940ca4bbec0900386e34daf04e50ad GIT binary patch literal 103808 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

|iRWL6*WV%oZvH-PAM23Bzj8*d^Z63~kbLNs;) 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 O{IP$b_=BfoLjE7ff$wGj literal 0 HcmV?d00001 diff --git a/doc/src/JPG/lammps-gui-find.png b/doc/src/JPG/lammps-gui-find.png new file mode 100644 index 0000000000000000000000000000000000000000..452c5818eb9666418730299877088ce268dd5a3d GIT binary patch literal 115375 zcmafb1yGe?*DWf-5fKg{A`Q|lA{}z1yZeB2cQ?`@q97nG-5}i{(hbrax;v!%KKT9L zoqK2Q%zcI#Iq|+cv16^Z_iL!Utk^R&0yHEfq-PT1!iq>pk3JzGJy?M}1n*pa;n)WM zAe#uu2q7VrMn1jPdkkJvIEtt{D%lu2y6D*(A*rj9IUCtKnAup9y=7))W@TbyWn*V$ zW93MGjADy~M1~|GETrt3v@`4CsiZQGbl_m)oWO2X_a}Kz=H+uJT9)M{YZ%3QqJ;u< z)FtE=v11`m61yM1Aryp05I;QRo~TAFNi9iEtGdJrv+`(<{i{y1_u{BlW@%gdSBL(! z%bz?P60hy|xYw~abPf*>Az*$;NWQ__eerLq!UIm8W5}KDBF<>d!NlRPjA7gtFV@%A zQn($mtEyu4zBhSYooA$7)B zF_(8pNC(w#^?oBGB!pA0pk-(}v}jluT?oibfAxa^N7htlB?*_)(p5 z?dU>Aki1^Po+yXIlQ!`aHNwihu_;qoOo={=H|kF8>*E&fBU>wct8nGDJFv_sPYVvEB7J^t5L& zIcyI7s11j6TgskEShrhG(|;%(jd_0E+rQ4MGDRU4H)UDjv@ZAZ;^IY$j~!9)i_8K+ za{Gkz;%mjN+QR&EDve(y>~M@-v{`6k|L0GigdwYIYg>*y0rG~1l!^V!%*@4kd2W;{ z?kmqt^H1oVTG8&PX3nM!O4Xa1nlLah81bYE#@1F>opzKtnQ{$gX8BV*|8^g(59&5~ zy4_r!jO55ghKAbN+nYO^1nH59KSpvi5sfY_F8)mRL^va1ngBKJy|{RY|IkNDiLmTG zY6l|~BL#)8Q=ZS zGrQQ?2|>Unf~iks!5}eladF?iEyQ?xds|yu*Sv>0J3FH~msfEYDChtEYkw`%YFysf z&_Id(B)C-%zeB%tbZl(4)*&aRI<<4T+mNkPy>He2Wl$Pzk12cXw{NEG3E9DY#^rWW z4njg7C9~@GxocSvtt}OrR;sG8v)1GL3gPX3^ZE@Pb@)U?j9P!g%B(Fn$)i za~X1vsnZK2C5r3}4+*L9zHy&;ZZ~f+I5svmJgks2Vq|2b!AtM7aKGc552{Kd>Ee1L zA|q)e*j~LdKvY@zqdX(vbuJX9FsqV>C_X@%#cO4CPbTrX%o{W3RI3g-fDjcgNE}1_ zZ0+r_!`r90rz$Hefw@yhcbl>=xyh1P+4EXnUs%`o=nvEO&c)R{HKwLGCB|-GYBc`t+DvxDal{KmLlBp|nZAlc z{C;}(t2aNInVG>wdn}?rgn=4d?#eJw1g}?7Q(IqMJ-oQ?>Jn#RVTq!&qa?t^U0GSl zl&9_K>6x^w5~i?obhKOz0db}E_iyniMkc1B!omu5#)lt&_4h|mN+6aJNXndK7}hhH zEHQJKu9R>CpVm3=YL0mFX#d;Lv*Ak#*Y7#GQf?j|4qQZ8ilNXYTP|S0Dk>`QY4Mpq zmBGATTPN<@sn?Hm2y1>AWB#nFs?u8+g`f7Sf-vo1XUA=~)b{n2SO9D>S0Q7^8w&e^ zhP1K9h!;Mkzh=wDll+-wAvh=i8(EJP-||eaR6T`FE~DjpV(PXux5Ffne~WKDx0Cf> zUteGT=AFaSzk7&k7FmyzX%(YN`@Zk5`>Ct_{YO2l$4A)3mUy(3>ZNE&%s7aA_B7xU zPa4lAAbz--eMZ{e--SFRXi4%__Hnr7ij}-=UdvVVw!~J(XKo6B^P`kV=r>NtcH>J? z-qN>v)}AL#jT6`FU@4s^{LHq9?msdnr_7Fs&N72zzBlX_g4 z9<$;pAV^Cq&WQICy(26Pt=|6X)az60qM^yUvWm*d)|e74E=2&}zrl#t+a4YV4m35j z1lUDf?^^;H`k1mB^^aHf3l&sUSrlLW#VLCRS#k>tEp7U(7(T$2X(fdzP)pSE|V6dRlF{^4*HSYqB z-q;A&&eUdBE17tKi#t9x=JaC*IQ6&7bJyU&z*6_El5bhEnMoaz{c%-snc zN5=}`r4XpS-Tk0rDe3C%EwAU$CV`x9Z-+(rLzO7Zaa->jeH zX&r5CbMx{J&(3I|9|flpsPFV{_*=IRcj9LVP>b1gO4ucFER zUOzI>*I%)(RV^qgF1Fr;5^R$-+ARmYBik6x5OZ~%1jz6Zqw0B8N@{v~`qI+U!oq?t z5_e>0yGcpRawS7)7J=qbNq&A$ceh^o6kEJ>mZI~I8P&N53E6}Oj6vzN;q9L$DprrH z>;o&dr|Z3M1`iHwF)<@S_|vMke%p9&MOV*9`yQ}`kVEyqcN{9YKEMhwrHwH?XD}k* z%=qPtlq~DAH&grV8!kyCr|A$G;_&vLCB1!USF2s#-p7ibI>?ESK7N4fGoggnW6DPG zOtidRT7%ZV<=65dtyC+cf=u)X`_1u)9D-`T)*z=&b~bk9$&)9|I~S|hwMW3)^78VK zh0yU?e>5~ST>RM|>-$9)B*Z6FG4;JupLh5Z59^n_5)*5adMwJg>}?Ip7vZ&^`cK{v z9@R8JGXz>;Cw2S3=n}GbPq9LU^-J+xXe!kiK>&uO$ur_jjE`%cm8vtM1yZoGvQkqA z^&5kfut-f?&r@y3sg0T@A9D3bSy_3ZsJ6DYX0v^EmbkNjb#=AcdR8C5V{EKaERuxF z4L}7=%><`9n?@Zu1>C*Ppb21r2ygH4_Z>%!?EFt;2A^2NtH5C>nHMp^a&vQOIXTtn zW0tg{0|NtbML;%3-5+wV9T!nkQ}diF>;_SQn3&jV9ybv75pkMe?$N(XFR!VYKe8!J ztt2droU&G(MfwEfVibR&B2^C$k5>U<;o)Wp(5mpEu#18oa z&jNYyz`y_r2}u<>Suv}CAbRV{`uYevTv>T#adB~TGd?j9;cns^E1Lj+Rb) zd9c5~vWdlt?ySk?V@U~%L=>m>Oh`mTBS>$ywm)TaTCqH}WNjGKAQDl#b*F`LM3o*B zdmi~<=2~>QQ;*tQ?g$Cw6k5gJe%u>Kp8o0g0cx&J4MoGm95Js1rY_)m@T0!oLs|J- zWfXer!sR2Rcbsh$8jMlxWI?Tq^0*>O5vgj(`lcO*9Elxm&O{aL5hW z31aAV5ys>vDa4-KJmKZAh<5`y2M&%E4m78u%A=^e}tT z#CCXC_|vCP4`0&JGZ*I%-RWeJFP9lmezObUALNhPZQubv(D_+jeyU~l@{{N`Fmk-;{t%+< zLpF>rz5I<0$evA3-XECb5EWckNrY+y5u$v5vo~M1t_PEakiQR7=fpy8S+hkKHIPC@ zRJ6sKn|fUxpE=){dDNbHJ+(jj3x(Ariov~ZDh2N>CpN8C(a1Flo#7r#H&15Hl&ihJ za(eil%c6WFSW7Jv&5IaOd^>YY14%E1SEdcW<)NUCf1TLh5za_Lx^Fy6xR-a@IM$jFJK3QW2K{4J zS;PA#6asQB76w@|XKTyg=ESPds?ix{ z9E3>vp;U7W#Q}Bpq1YmS{1CfU+RW_idl-y1jK^oMK{O-uQ`qs*QThDQ-1VU(MzFsc z2l~eFW+HXVdT@GfhS=w-*P>>c`V4O?n!GHdj1Y@yyQ@`)OT(jMr2O_3kM5C1fZ^AB zHy6r3>8m(Yc9`55e`@aT`z+(?NgMkyFBO4_gi+zFl7fLtQT=QcL^#S&T3XsoL&Ms7 z4`iz7=;$I&#UE79k%p4(xQP77fTMI9md(}vCe2~QdwNIRnHoVUR5oP^rUo$_B218x zeNzFxAocmoQp!GH2&f=j+5-oAt&t@B?hC&@9xiuQE7k#87LvZ-I2)vQv^RtGnN(|IQ70~Clb4;`;v&p2nmm%mv+oDLO&ZGNs;!)L#Jp(LRTrNT z5k{weDG|4Q+oheL=eaFOB+bz`5RyANR78zMMU7GBf{jfPGdw@FMhGxK63WdL&r*De zj*{m8g$x{&A5EH7-5U|5```$Y_utb$rf<;#e_L`acXcHAl%`M4w!-OS!id`mB02`= zFiT~ZClpU^bffXe_$sIIDaV6KZM?@tgLFv zPViNn67$RY`g-r96<2fCEJ?FQq9QZW064YlSF$IFb^));F2k~=C4JAqy3+2XY*V!6 zTIUI?)+sW1gR3Nw4YF_DhB9)><-rTPyG8P}$PYg%(;MMu=hoz!Mg0x?dwJ8kuaKw3jM`OaWb7AlsSE84O+S98BhYK|nx& zgEJy8_m3VvC#eH5pW>@{&ImUb7a2;?rLl^L(PN1(xFT+D?%=J4gWpdHA^=d4i>Coz zkujMlBTM#h{_>UOnh3cBHs`mhl%#%RQZ8FxvXIvqowk!nE&eE}0uDS>RI9pomoG#B zx#ShX zjkQr5_!(wLC;XV6VgDtCl8(;a{{H^*a&%nW#Gf5;4uc@g#NNEA*}`yV zbd>kDwycqzK%W5kuB`0u=`O;xW5hTnCg$d_leDF!1u%Saa&q+6@BM2)|0yeDOKOIt zkx8(Tk^S(wyFCY#0~g>5HjcR@t43fTJ9PZ62V}5~>x(0Pk5hCADDv$y`TA=i|qZDE{P}oSeM82@~`5aUS6o^_&=2C85kJ6JUw>?9Lj^`RaNl_2^qeM1IfVH z*f@Il=9z?SssJ$o0ey7$_03I$r~v>x#>Q)2av@fsMH2@=$OP+3NlAGhj8RllGT7DS z`o$ix-IViUA~TC26AmoI=b;t%m=#?Y-Y_&0TX?yPHoX+Z1tEtu!ukd6?5XKYAlaAT zU`8;Qn3$-jsL05WTw(*sK(w#|DK)&E0v8R;&U(ISm(mV~{KveKQfTRQJpe$jU%zII z0@|G%G(elV^I7V7bdi~T6s6DA2`7RAzm4o+B~|T=u7FVwt_6(6ri4x@8;aaA^eJ6< zn7x*m0_dZ+*B4a7j`!KK*^aG9#j+I(2CF#qTU#!w2PiT>leq2Ez z$Uw(To-HSbH3dvHR${d=x}dK%LC1ONhV%4XZg?-z(Ku5J<4!K zdV1_o;m`VFew08`0H`j~E_4@bdv(=_6(5i{D;rV%>6NNU2zig^Acrog+p$4&7Mx&j z>qbVVUF_3Q`Q_y+A{~4g3)B1{hLDMgT_<~=g1TcX%)8`I&QUp?sLtF-2a$h|k2jqy ztmhis%2y?7ezlH4Gdbqk#UeOQ3FGh)jRayhoCCIftRe5Zq}G-wPZa5;fNssJ-v+^- z#YEZ|zz=Hq_(PcD3FcE5Cp#u429=4od`>_(m5a|wO(peq4_C~qNW3^4rmhxg>+BqK z-=wOqq<#ZeP>>{h(7N%&ualM9@57rAA#@q2WzcIZE(LU2Y}t_z5p z^EcIsd5yNG-~Iht(?!C0GBkl`UI~P~P+`E-JOxO}Ge@>f!>A>q`i&iGwG*-d{~R-~ zoP8w+RPBJFaF(XYKE6B%d6Y141PFORlBR&C$#t`;aT_v{uUPLTvY?+S5kVmSLo9TE zh+hkfLMu_es{nn_&#H{{dR}oVzor{hc%$cDM+6~o;>gRNNJtB}$L@`qMR?)uu(U4# zUj%w4WXn)1dEJjj)dH)=qdyrkbJsXI;Hx(N?;wNXVqXkfac@64bqV_8r@vMfiAY8| z_<0+&coz|wr4Na6gmNFcHUKXT3?({4qotv_N{LlAmAS9pAR$FUg`ndS52nt2(A#qVy|eI@LgLRQ!6gtW!0@&H zSn-nIS4(kUz$uGq&gbr?++>gg-fV^7i0(fA{gkCpyOxcb+PFK4iXdVQoXtk2c?M7b z;}ZB>kPtuh-oJb2JKGaYBj0k znu^+88!_j3BvYx`2n5mk6STmkDU(#Mo0ACx1B0UVJMvLfa(1`BD?mv#=Cqn?m>L`` z8Ih8fmG%DHO_S<%I;DC#RqwLTIq&IkyfHk$RF|8(aj~B2;^2TNH3;67Ym{vs1%B)6 zLmlO0BSoLmm0&98kq1Fp7h25{U& zG2CPRY+zuf)*+ww+8}(tnr7N4Lk-<>LY~a3a;1DA zSO7hKc5ZL3TxuSIXio7J(!2)_Vt=kNM=sTS zV<>GLamv#UDpbvvTLrJgKKG^YBb;vlq(sFa+Su3#fo^Rb*>p}Dh9i`2Zf+3gd&;V+ z`D0toF(L}Mpt|+eTsheM@&|yy_A6a0-O()CHD3}Ern7{$9_ge5OZugYD*+PHFFNeS z`Wtk8=+Q!J4jLL7HV%%>T!UG4N_uVtA*XCSn^9W`)*#CYd=3b-dH6S|Rv zoXm7|W}APq06(lV1J-8Kc!CF`ef_!=u(c66L1JEK%N*RT)m5sm;2m~Na8nA9ACM$5cgoFeRjjVR1`Ir|e#5j0xoraQ| z+NqrPt}~MKuKDh&8Bknlpy93d#E52KkykiwDk4-5)>AyJm)b(d+q0zNn6+yTT4cN7 zye@kc$t5`eZoYnf2++htiEb*J5gN>Ny25O~`A%T&IQ8z@1RTpN(NC_6fiDVOwvLXD zaBy%~bQ-uE*7`uHaOrfSNUM6O2*@2?n^|#DU_?j1VA0Xm)SRxs06JDl^X(-twCJcP z9gp2gKyg14_r@@&DbuUy>n|NFwhkt71I5j%I*QWmV4=lqBs&5i?tvuksi`T~#wkmk zdgonmI>DHvAXk4Q^*(R8^sUa!%uMEW(R4W9DIYwriIi>rk6oeRNoewpeyyRZOrxvp zMUgMkC^rU1`Nm`b_`)){%S53@IRI3jjf_gwrO(Q|x9@zpj6%8VYNVrYY%%SrV%XD6^rsXBJQ@z`%% z-`~IM=bQ(-Kut?)+T!>4Uf@HRFWEBTS)FZdu5NBw+1aOivxI7E;Pf-ZB8^Mcojm|r)$hz0HF1}BjfTowNy|T2KD%i^mHf;m|TPH;%{IX07UynGH`HIZNhJV{P;oYbFDxf zy_l*E0`Uq!6Xq5c$w^6tSKne{HXZBk58~?TRlvQcw6zIi1dq(2S+FO76vnLn6G@j3 z6wxGQWGZ*xN#1&7HXwy$5m%J`3>#w8_L*5l*j*LYt1<@lU{hJjJlgcmAI zZ2IRFL>~l2V1Dk)5uC1kbAeA^jG8-*U~%Aztgfs~I!8xDL>S>ygZjqM47^QWXF{R0wNKg z>(7RUndt-_H8m5$%u6t?u6(W!JHcl=z_~ybceZ^*6a+?-@~KF{TP|kPm9DS*O;^@N zMp;g_-+_AhSsn`pNQx^x5?;87!na-Nr{J%ExhvA*V%||c0vk7*YL%fC-p^fhbb8uR zB&k~U=I>;S*ZX<5b>i(&wHhfSD=RBhKV@Cr`L3?6?CleG{#Mo+FN|`p*-+I&^^b^8 zppdRstj+CndtEhn*0y|T`GS|@{Vqtz)s|ECr#n+=?}LGO!OqB7Xx8TMA!IpQchX7f zQvugiQu5zF&&kP=OJJvCV%p7$)4_%yb0sv}EqA;(6}6K5$Bid-VEU!%IU`fd95PNo z0Rd^L>113B2<5P}17MIg%{NuE0z??=+a=9&AYy-fgxYj>>*3;ZHdSu2?A-6n4fIyo zoboHZ*o1`3zBhvl6IDei=Wq^!i1VImjPi1lkli$pw~>DJc(dJMEF4`}MfTbLtZ_Zm zuMdspGjIybU0?@YMcG6|L=~D$t*qlW0B`SK5iSF=+30ab^2Qil2ZY}KJ?gLG#{CJv zzZnBz_e=*I|K$8UC=BNAiYWkbIVPskb`gdkeFVZdNH0|=c~vh`L^E1mPzoS7nDQ6L zdJ9xK+Jklb`d~ygT&a)ZB?|K1FE6-Npey8cU3%)4_sJtu`}W_=TspxOmrn%urPw|G3$xNsb-*Ni^d`r(w6=f3a$1 zXh=RR9YjeZcxp-t(D3Kn*Lad;X>m1QC@Co=a>&NT#AuN6EiNw~ttHw-QQm9$#Ti_V z8(+f1dqc2Z{kHr>%&VFvufDCVt!?>C^yn|pZtZLNXlX;*^^FV-)ysRjx@>K2e?@vj zLxrnk^0}Ua?KqGVg)&BYh6v@$(-Hx33-A(9F;vje@dm9G)pPDoBiZ+TK^T>iS*Q&T z!XS1B77hxPKow8*zRa7l@nVkaT|O+Mk>=RPTXd_hfH>=y0!)s&eVi*Jlgx86nx_PEb2KcitH1wdC0b6$=f;U| zMuy@mGAio+Y<)GrBd+TJ27=rKM1yzqG0yt@pi*y|aC>t$ZUyOH1%q0xR6i_I4G?_K6?7Co)`vKl;Ykn}2frPgO1{#28)Nb~?&~aS^TqOB55Tc3z zhytE+b9I&q5)&$^r?1JwvbR>#6#ztmkE{SHsk(X0YrNJMe-9!5_Odiz|CPcEKbiB` zzsFDj>Hy;cpd~Lq-+bRN*NPkpLBS;PJU>`eC6Qz|9s1E8{^IU@8K7QkT(m#{TF2)q zYP7aMe^a55IS^)@_viS{{$vgmnSf3gfIg~#%>v&9y;>JX>&-yO<2pcl_H1{eWZq@o z>jfd<)n-mAvu@Khzzbeyvo1ha`tV`lPnMLG*z{YA%i~SJ6!#mBJ_AA?@B}Bt`*O3a ztgNKzvZpFgfP=$%@pmAI-vGkm2tgk_c+eZmWHHgI37Wt(;qciVN^}qzQNiqYD<^0{ zfj7*3r?eA~SvyxT4+ew9GHC_98V0}yq;^0R%d)dOW@m3|*HaQXtO#TUKtU56BS3p9 zjCgY2S@l3N0S5Fdk>`I-hfVzVG_^308nj5+@>B~ZysO6zcdo6!fs+HHBA|CUh0FV) z+=%1xYT2RbA*joS$1$s^ew*hE2OJ=lBbm!Cy_{ht+nyA*Ih*$Itf=*Uu5a?5 zYHINi@tg~|%>rreZ1olCnA+c0n2$-^9@j|_TRK%=ItKduetl1ZXNOwxx&A|!-_^eZ zJt6m_j`&_y1>;@yh;-^GShtu0&i!ko1I*R!b&Klkc(Uh%QkehV`qes|P4--ndoyix zo#b5O>%TV_Y{(cG!Uxwmy-c}vDvAy9MT=B9Wp2Nu{LkOIH3L;<+OTx91U>xgl9NEE zL!fi^X9s>%`gi#c#77|8-491z+j`P}zwx01*%Csot9$iYvET2%AJ>BH@%uMyhHz|f z5Txq&|6PHq(EWJe9f45V=Pgo-L(T)u2x;=UW9G@Soy&)pC)*Rxv;N)JcW;mb){ge{ zsab;cO_LA!CMaS7FQLSqNk=>v`OkNn1AKjvYPIKfaFvTPx1z|#`tQdx@^4?irVX3c zrT?cL^MDgYN&er)`C5Y{|L@YF-CrBt|M^Wt95m)F?&Z|T(F0+^M{6?{O$bOBU~FJl z8Du69bG5U6(kZdwU_#FO_x6vYvC+{aL7%y4RdXxpN_bm2%&vAOyPF(zq1}(&ksLm<@N-X3%g!4-)9-QYZN z6RgFQ9SF3VFAlfAf5*>Lo_^l`pCvTMGe1#${7?v3y4ni*zwh5Z`62f|Gj_gjg}WcK z82_E^|8Z#d#d|P(t=IR7GO73FVr`;)ymybp;8^!>BMk|_*&eF^90?MdY)*!mCN1ve z#YJsRjiZPVv64mMtfK`E%v7`uohD@@qgXb)BSNayO(|4YOqh$5k84q3;@^HRkb~L3 zrJ?Br^lxx5lCm2Z>hbaM&!4RE(&NvKD)oKWk>pSZS=U+XPbxd6ONxC0p^_NVdjmq| zhMC_w4Ps)zMz|2~!0eJlpK0fbIOV)r+HC1zD^Pr9wF_DCjCNH-^F5Zw+g zB3E0k+L_LhS`~fu9G^Cx`;C3iH)zt<>2AVU z!N~1(d49g6T$HeB?#Ry6u!O|l)0BUk;@cd}OnPsXss+zOh2dW>a?}J)aIWHMN4}=N zn$Ps|SHnieS|@q2PO|>X`6pstWq|;^TE_N&VW6#lkmAWfTHCW@|+dUsi%zzilB_tGusJ6gk;{aXF?Ymz~2vdOq{-9IgYMB&7100%3Y=@t|L*j^YSx zehLwr+tjMjHrqIyu{lJo3^Kl#IqT|pce1rzb%l9I!q(;?t_LT#eh-iNiN^Uuf3@zf zU){riq0?ame_#qEO<(49WV?l0%*^a#M0I3rcR)=HF8T3loR4|sn%(llrdxMb32ZvL z$0d!<_9t5vFLXaG*>;6MHg9j1(=!NBVr5dj&v&NE-UsigPyA<=kDRQ&XliL`$;*?) z^-^I=sI8E$V+BKhS1+rej6GUg`|V=4!ZRMfmBMwDqM6Dwda-TXLGQ8wU(ExY1w|(N^@f znO5*kru}D~l};*O%vtgO{P{zPEn#YE+G+pu8NWKK%^$bzs+vwT?a!NX%vr$PTo@Vt*k)60xElJYewCr zHSWgN7EnS9B=(0VGWKu%Zt_I(2D1ec;fb@S0l;${V0I75zbPg9L#Lzb*I^fr1BSNw zbxBv=LdaLhk8M}$RSEM=Na9~E;li+2w#W;NU@!ZJ$|DD3|7NBQqRDM>wY- zdDt{fZ38Pib~L)XvkXLni z7Wb7hZA1eb=L{J|5(-09MJyd@V5&3RFUEJT@JLIME)0~%fQSULay$oqwijn%PLQk@ z{W%?$*tdcK_>hSZ0%xt@s6<((R?$twVt6TAzCpdnCImG472-EgbWn9jCzhzd>lNMF zKfERjP^a9kJy1xiilhFg zP5-jd##+ev`8go2D5$8q4UV;C>DQ}jIs%}1Avn0RwInPuavh{9PKIa%3_{HB?0J$O0~d%F{C5@7?&JUF?EhZ| zKRwG|A~AM$;%&B0u)>W7xwHd7qG zhj5hzhfT4x_)s;Kl@5tXH}>XO0Us)=`;zc3@BXNL?ZJ))_~Sq7H=@W?GKRT$czO)k zq9_Xr3Tyx!6kImDki zU=S_~Qcu#=JX-Y&?(6IQlJY zlW-3Qeh}ADibsTnl{JC<3PcV%AnK@nneJ>H1aTRXHpG-heW@o{F2K#o&CPm2{sN6h zlrcLx%5hhw(jh0eA*s^MK}7xgOY%&`((xh(&l{)8M8dJRWA{7z?#fZ(i+OPGD(H_G z=y%xduSDG^V$5gB=bWfNfMYk{%!PXl#;<>Z5}Y6X!nS%cX9d>gb#Y}ob1}A1;=I1+ zebt8;mkzpSLvB7_4i#Ma461RN^EB|h20yiq+RnvyQ$6oqs_N+0Hrx8dSM{kZ@PO2f zXq-RAb*BiR4Z0M#xq@@ohi!)w*M9bIKy5^*Z0QAwy*J4k08*=B|uID9(1nlo}KE2zld?E=D} z1pJyH4}LZo{2Luzxy#20R*d8r%8U;DG@TX{jPk3hI9OSKI=q>8v`{K4SU!hP)zW?zfNx(3M;iPO~FXH*z$}~pjwr9MD-CueqB=V>+JylfcQ%rdK-!WeQkYL zMiN-a;M%ffYjP_VjA}ZH%_{z8k#+Rbt2fNwbl*X^ZHbgo#!_bZC2l3S#Nn>ai-Wa- z4&t&k>l5C-!{qKzA`}x=$eAj(#aPA8f%5%!5xZ_1=5&BCXLkIZQ zMeM>2VH&D3<4@W{IZ*Q4+>#D#1*1a`&Z-;!hPnSWl+jXrj%#ZhkUxUuDS(4-+QLA( z0KEsML-BR=;$xeRp!u((#NW>kXumw1caOdBKK&)7GJJ{!i(_CGbUMge4qF!O=`uJR zN7wL&Cis}&HeGx9H=_EyJ!i1UC!c#CV6O>gckSP6LTxdu|?cUkBJyBfr`~O<_>d^(zTr+9&>*{y} z1yg%ht6Y42wl8bWraUbigy0|N(mCZ(X8vg8l^j36aMq`*8u*qX1QDqhmcvZ^4AsXf zOcQv2nC)P+4ux6z-E^mvS=rA81q>3-vQlFaTZBp&vMr@~OgLkK?+a~brYxb_28AvBs&X+Y zW$=>9cc`5xdw|NmsxFq$Iy#N7t~9p#N$Ki#{ddl~@o-$zpeXUz?t}0J`}j8D*`Q~i zD=RbK?pcOryn^e>NoI|=349>FpUaR2NyjfbF|kh2-|gQ*hKq(sj1?(^ zMle>iJFjMIC8B>5>WQ|-FD3uYovy5va4w~vqgwk?R+}b3zBIdLllqC8AM~&N0ngcJ zEqGW>S4}sv$xG}w3x7*;HKu(XZ;+RJzc2HRYxJAJT1DN91rb*Ck5KboxwK#J9CYSp z!@(g%K+zzqg0BCW4INK24oNF3y2BvXc0vMyA6bpIHL88<(RbrqHcK^t{SK1PQi}QA z80S{QdS!5yO6A9bwqiLdD=MiTHKI@i{E68_SleX0OfcS1%E|$+AZfQ{w~#bNtIf|l z#I7cfj3%S&QC%J(RjDHvq>44u_@^>X#41U*ddh+8Q|P&kuAJ5JrFz}if6xje*u4nQ z+lDjzfFZ*DH>S$3iFt*MjLxn}q{P9K3ASbBgrbtfJS)HS(>^}hMXcTX-$AaUw^Y7t zPQBYvc71*QJF*Y>fG+1N)62=pan!#udiLenWOTKO%yfbvt`B6=@9^Vt>Qf)dpmLK3q-QGBlrMLAy}A#Yji`g+$-$g%5Pt28VzB0TG2HV3H|h_Cp$a7X;E07qUMC$L(Kaj0QS4(|b$9ed zfXH16!JFe@an>ppop}p>4dMhXHrgZ_qBD>`24)0n%Le9 zog(ezytkH7HBj}I{80n9C)C{C=bm}e$c-0jt)FidSRbuV#D4qfb1uYZrO7WHbuY3w zk|vX8+Sh`oKETc$&g4G)NJdH7*4tZPP|M_PSsKrgO2_L@sN_+50)z0sxJ{bQz2!bz zSK(BBEamq?u<9Iu^eL(&Y#u757`My~@nW7hV^h-M+0Dn0djO_?F}4ckJds~a9)OPb z2$rjG;}=w)X9=mF+puDUnrU_I@y!IM}mSS<7)M2+QC;CJ~@ND<3$1#sf~BjSM8~~ z*ZVzpj(BiigI`a-vdnPGiHY&h_W(LV5fer!flCZD#Jr+G;pgF-01})pJlagOsY44q zJ{p{8zOvr+C#JafRvsKIQc^)6o&9gAtNFU8cYQGV?BKxlTxIjvEdTy%NWU_oVq%_? zl7dconGw+V?Nh{37!ID7;7Q&)d=Qz+<*h%Os{l#M==@xwQ;AI-^bdahzqJZXgOq!z zW?|oOnx%oc$L$KIgSqFWZ!?jp#Q!K91A^@0{2KO(O%lxWl+_-w|BwJXb8XSBTfxlR3ot)7QQ;*a? zG4HOiOBo(+^13{4I8XOb#p~p-QX`^5J8-0QqT zbE%V51V%oaBlo6p#(H*%M35OBgQItKEn7?1XZGaRgGIl`mB7HHrIz{c8pH^F%u@J! zeb!!LhEd=#|H4O&XwBcj-ae{#wcnb@X=@Zz(n;MTkdTN+ z10Usp2Y-RyWW=TcB&&&-Kd-<3>9WB|bo)+^RJ3rUQSU-S9+2X}3`!q=#Nf$S4$j#& zGqlta6?*>5wGN*3MH!--QBjVNv^X96-o6H!oq?_e5?<=_K_6u~L3kuIN^%pl7Rkvx zU%JscTMYiwIeT6JMQM^3P4^P8pCYoA+wxu*vB6?PfwSrl1{1mws4oY-n=TZuOL zwc5t#wqC~6CaaP;r3U-DOFp+5QN4uv#S+o%?)-q&le`U0+FDxobMu zmn-(fu71UAC?)T$hyPd6e=c>{qal1uY(&Tp8 z--X2eHTl2209Icg`H*~99Ez`wxa~mOMguW|6n>ezewrdj>}l?^csqyiz?^$?uG-HO zFf=jIjDa9fHOVjE^U1gj@{GQ=9oE}U*qPpWazzth{e<|kcj&zf#*}f>Ylr18xEX!cvClO99dM(kr{!@vBVaSvT~_AN^pgdi?|M2ly0Jl;k?x(us&_XixX#hvVwy=U zCqNr#Lm=Q;qBu1^%@Ixpo%);V%HWfbBl`pj%MjyK_8uZ@^nc{9R>jPl7#-aj|0bt+ zs*g9x3Owj04Dfs&%;AszeZQ2mmq4? zc%>X5FaJe@>_dJ5;a?C#`nng}+Fyot@LnGtx`#i8H+1>@YPGzfD5d&3q|Iz#ZRxz9 z-Z}%?`@6orDhdZJ0^q5EzP`S;a8MEe-6l=-;2GHNXc`(Ss>Zw9EAz_fx+BoR1bUOe zNSxav<+e8j4Mw0Hth~G&htQ1uS@VxRhXFti=Nk#ug zu1!$PPCGHD&3koj*>G2xZ()<_M!0`*l|0XBX#KOlUu%UJ2(DYW(-mYmR_>QWBZ5YE znQZw)To{0dJRM+G_i!lM6#&{(wQ_ZX$Q&SaE&~l2Z22VDNO$DNcsFI8aqyysOJLJ# z)P;^q++GgLgaJd}hUoC7{{rMR&hN6m=e$~varDv{el*-334*f*rzQNS(m}6JLy}5# z;Dk-r%)xM0Bw$lhT>8ruKg}%uj5F3ZpxbP151);Zl-yojHy_6Q6fn5AOF*>_HBO&} zF=A*en#M>a;_ z=6G==N2*fuO`OzRjY6YQFH=2{X@c9$iBcT13Ni|h^}N7zdsb29uJ*-+B`WIlY;u`8 zB;zZY#LB+Sz+m%TM4VQ24QQ-2?1(r*$2i@e7z8~IRE`E*6N{DzSP6}}c^3{1G9hPJ zFveJYEr1Sx?W=U;kWmusP$dK8Km2%1t}Oq{?0xK6jaxJW-iY&8hS<2y;qB1x--9^o z!ZAh+VMG`P@U}zRy?sPz@YJ_hA+`uXYt#0$)SG%C! z6V#pC8A=*pQJF16p2Ab@Gjlt-qD&I*Qns=J?2?N}4~nyd zYj3m_vy2nz(B&bJof9aFNUrcWGzd?`t6;Iw4Wjp#vd8=+XzMk7qUO;M2EDxOm^fK; z-xgp))#y3@#xLQaj@&8`UM{ zUe8pZ%bVfsZ+_o$U@7|1F3r0sXl~}ReM!js0dGSslH3M>>c4n0pW`DqX>kyIBS5tB z)>LPwVN-9U+Q@3;JE5kS&i;jauEPdl&b+uM zM08wlmt6cW#@;fjt2Ju(UW9-kAs|R2A}Ao;Eg~f#QqtYsodN>V!k>`t2I=mS?rxCo zZg_9q&oj~;b^T`H!64&i%*|?fD3u2eE))z99Bf_n`e{tq zwl;s0`~Ha3_d#|g3h`BvoE)ETQZCB+CXDh{X^GjT++w!!J9W}fkd$m|qZ4Q5j(da$7mFks9?ItCdF5wE zT?0i$V=RM41%3`4+SfO#rDX@_J>cdI!pbYA<~d@F2cjA$O^@Yotb;N6<%vSrL{a|V zTl8z!YJWV(;QV|8Fde-A_N2yU?Yn)XJPoW{kK!|nPbY}N>oF4~4+4`^sfeB~kqt1F znwgo&%F5c=*@06OSO&;p60=HVKLpV{^ZDW}Z_l#!Z*0%r3tcWq)A@#n;h0)sdA2?3 zFA}iFKX@*{okK69lH}Y6mo00u6^5Cgy!&RCVWWYkFCuN&uxsps7S+o$0ZbxoZEff0 z=Gt|vv;RbIMotZ;lX;M=6Mw_vmUBURq4(>#2LI8qZSLS({;KC;p0%It5ne6*1WtWdp@`C?S@s`tD#bPoVIoy|%OTaplX&&#f?W-zXRT zY~ob`?|_%B4=S3Qbm$yoS$@)Wx%GjwqG7lsxoi{C7;$Ft>*RrU`2lGpa5CRYKe}H z+VusPH~`;bvf@c=Yc0^eg^bR3pZ}ob<@4+Aeut7;X4s1*q@ZxUyQ|_mL}YpTquUn+ z1aewVx3nv{Y}p`Ii-Y4f*&jw#W}A%tjpW`h#YL#-Yz?=is*2HfIW3_i2L8Y`Zq11_B;g@f;lUJcLu47(^ zQ=no#&b@ORsEYN>aQ=Jsvv<~|{%FFl3c{i3ox9a#blhiO1uOQh)YLRS4cI#99J}w^ zCg1#d1M9(qxhtmPb#E_RyE4EyA!^dc&|JS5WL1^ZSjppu%E5wt|F9S}Ce$%3`eRKy z0n2ckVdDd~e%&V$_@l7&y%+;u5cz%$QjnuYtWe6h37la1Fi7XjA-n7U945MX5)efJ zVTnD0&rQ*;6@ClnWuwdm#SU*vC=b1TVwNMJG5(7@vRyCyIW|oAb@3N!J-2y$R;nQT zy@!z7VmM?@vxQT97yqS&iH5h&@kEn|m3Nyir$%+**5S_0ZocVr;fCJN1enRcDMs`n z)JKP`exFrUNoNlHc2UQ)Uy5~fZsVC-d<%9MF!jM{{_5MyfK z+lw9iJ03eD8g?u{A(_{#JCg$Y^Rqt%8BZU2%Z=R{7{KZF?4^j<6S_HVg=Z z_;rV@iRqD08IhgP+bP zk|H5_XJ-mpu*tC{AD^}9=?=(h3!8)}#XDqTVx{SRW^77{2uJRy@?iaFweM_)YiB|M z5pfK&Ch;4WgPD{A`&Eo)F#cQStWo??Zf&5>lgk|o!e`i@sR$BE1&Mzx;I&4w$jOPG#HxH-2Z8&>QW()%*Ij+*)+w%T^?KZzI;e^Kf8nE`LYIY5Y(HhDw#3%SN5!r@0x_+yyv$hnj$Lyk_V!F0xD@&G2hHj{l|Tu2 zs8jy^q&pTF5}fE|>s1iwrn{3OEgKtaibPkdp1FE7W4#gPrNM-h^rG(rGwI{gP@$on$*=!^}e3G;Go__*6z0EB6>BY%tii{%_*d&>CjM< zKtxSN5k|HSQnu8b08Txz6IF3DGx^vJAJRfRt7`7Z1>`S5+DnW~6+dG{&(9Q;lyZz& z=TA1y_yuA|Ga4G+{S-`X{vdp)&wN{=3y+jua$D;b$L`tK{q>PT$R8UX^hhyZg?yUy z^n65kA%BdYtwL#vXciU{kkJ|=y07p^O&#sQVs?2q<07)R;1Js-M6gdZLzpAK4Xkw3 zpOVyoJO21jH>lie>gp~I78k=y%}nkXaO1xsL#e`OPzCB+==tzrCL5u8W*)CsJO3*9S)$?x6E?`#B;2QMtVYHGM3kVNU8n5ZoLiNc5pd9}9+ zo9UB5G;6T<5e{u6c1#>hvy%{^^w%*k&DZr>S{3i`-a%HY;~{3+>`=R2Cj^Cvoh;v! zD-1q8&@JyZiV!+&3n6<>*(o^d9kQ>J^c+f=$Q_j!<(1-vYmPE7k>+T+->&7KJ^M1* z?NnMTTI8q_cQXP zSx)Ef?o@R26KJZDPcD79!TBYZ^DzO0_()kR=w@Jm7*cAw|12Je$iud@)bjP)+mS6{ zS62tSax$nu^+JZ6@$_P$?aU5&W>bjmBlb6HgEJ4>%xKr z(tF1nhlUi-LoXfO^iDM{fohfD3v{{M+n{a&)5O6q2oNb|OUrTo7vpQ*z(6~Ed{~Y1 zNm+(s+v(PA8pX){T_&%_>aF8}Z1$&(Pw`Yqyfr15f;{LrK5fIbHM!59m`esfO)fsV zh^exPl^QbAd4)Z<+NCG?qtYYxz^8W&ByG00Yn9!P|A^DlnxXi#gm?ekei73P+-($o zLcq<;_Dh1a=uUYGaBeOtpc22ih6^iuouazV)@Lw2Zj&=K4k+sq0{L+ zDGn1Vx$`*PK#1&1ESuoKCW<#OjCJ@AffIBpxo!!(lcqi893OV_U0GW+J0N1=) zrh>w6T1YEu^feT47*KtBEL+LQb7+(zlrEFA^HM&hx2LJ@T)pkt@^D~!Ndu3nizUH6 zCoeq*y=p9p19z~vj?PV{fyC~vD4{y@ZZ9aXPq?DWNxlj1inu|yE`E#eS-vM-Z zv^);11U!{T3%M`e@9OH>lJUM)E&a1!Y5t-xRvz*)oit)5pr`gngeHqegG1ObFlab9 zo~_?F-EpHsJv{-;1pBwn+>qT~-qHzMQc@4UyPoNURe&%_Vv%aa_to{R>)o>T4=iy7 zH|Mk4{qZ53#31Us(uuAjfoi|j!(H5hSyX9!eOmJ5NmN}O?UtRo3-{IUr&;mwd5#C% zXli;HPGhf64w^RHuk`m|wB@2%`lC zvXLPkd3kp9_v%&q=4P$Qta0qZ!U7`v3y}B)HrK&q0ghX**Kv~Hlv7Mi zO)FFi1<4@@I}k_-X@jeqn@+%d3IaFIE-ouaPT&K~4&*^>0ZiS&B+O4pTtY%NS0049 zkt*{iwiPkgIu|2j84u-Z%Zm6Snx2|MDM}eNXOc%!99gmmAB4^z$Da+4ty|)1VacSS zQ4tHLj|@LNtC)!-7!l;@8Szdi#+0Ap0uZrew^(8`zPqj#wep}DQm*J1{p@yt@Oy7M|&}J}Ku8eKk z^1eq_6i{<7*-TS<;jqpr>LeWOKyY`2biKI353W?i$A2XXnN1=|J- zh3Vp=@2|_ul$RD7xWdR{W@m}+&Q+mEWy_7{Osz+2y+3ukk{aEV<>cklWyYqXTU&SE z;DTOy%h_3oDsrqWd;IGo^)s})23J)Q&LPjyEMNmfg-Pse8S{GF){$u8F`Sd}-(+J1?SDznR2;9=FWxnTq4eBf=EDN5~o|Jd28vbS`sX!wdDr0QgY4?g+kUk3$Z)EXq} zoFeEWmkSD5pFU-ah|qB_ry?Wks5I9%owUi8T2)ms@px!prt3?w9~TY#4IOroC439C zw$^WO=}zK*B~E!+&+FFq`1D*cf%lD3llI6~?U&+!jlt1zlZ5zqo37AOkGt)VuVlP^ zRQ#0u{NKmqwt zE&7|-goJvX4%E9|Cf!cRtFG>5qEPiL5aht-O^AD{X2b}HCy)XTL#Khne6Bi5{m8%qjZ2JAU=BB2B zzP@P0^x5=<4QSL67tx@OO1B-$GR72 zM;5HyQX(HZeM*sIf*6X?HEv4kd<4ls{l5~qr1@I58_`A}-Xrc=9DJ^qFZI=-dzO>b zmc{VhzfYy4=?<`c`CPbDZ?Jn=dYP<_2mJWng5>;<@BJuX5-Iyn=C4Xzy%}-Y)cwUl zm&21d9blif3?;YE@}|{*k7;Lhpv@{z4|5=0Jb%%R<#JS0y)o z|6MVh%m!f^Z$Xd|LedkyrM^7-lA4%ESVziLsz}v#cSUX60v{15PDIQa_TR@Zit=1% z=omgO&qAsZX}oUPPU>>yKCs2$CFJCBb_gs-tUJrOjZiO0R4L@_4e51PyWo8M5jbCC)t_;Vkjvf$u}9ZN!HCz zT!)*GYyHdGU=yDJ6$?_Y=YB^`)DK*$!0Gukox?fD|xp&dZwo4>~~0Ywwi8j9LMvJ{rtww*fTSP zYt2>H9T(NxETj^*L2(?XQm9_GuC1Tx))i_71$^Juu^hCh(gwj5@PHYy%zVfING_mI z02m;kptsCBnZRwPsH?smIz@Lc`s z7B#4yCI+UfV(XH<)rCY7zs`mZN#;<2-}10;PMr_!fi5G@v+02cqC-=QX64;7xa=>X z*Bw_m%Q}@p!Vt~C+Pg8+9Jowqaqxj7PbGhc%uTYX2qw!2^D;N7VO_k9Y{z(EJ+u+g zy8{d8kQ1?o`XM(`pmpQn?ZsqMOb#XJdT{2b%k8KH*hu*^5(EYVf^j3e`z;e%u>x@c zATv6W#p6(m2NDx|J+zhb>zB*JJujrx$O|Ic!$(KSaLwcS#Xlk!0dYA@wTeErg*;I} z^YZRu@)fp%On8Uy5c}g0$L7mB;Kq%S*`T zk;6Xd6z-MaAy?x`t!9fT6KG%CX;}R-+aANCtE;QEaXl|b^s;O`;&d#Mp_Gk^5|fpC z#7**?AfhYS8V*P$bPe^ArKL2Mi}a5%DiI+fqKUK*>gj2+;KS(fjQ3_pt3A*E{JRa0 z$kOINCA2PEQBhHWa1uCrDY3f`zfxqPK-`Qxtcm@>788e=BH6j0$F(eSofVwXEywdr zIwXz`%i0#tx8zNtq^iCaHX$_6kJO=$zA_1fdk+UO0#{mXcn)Pnp33eFIJd1huh${1 zl=RZJpVp8ua%jV ztg>Zm&!;?y#NO}p#KwNGwU~-Jw?i%18xV)?VR@JpxS?^cofo55>rm$vJ%9rifGdwpUy$@0P3S??EMy$x=|~zezF5zp=H)*MYxDFqh2zG$wz_a$qH6?lA$wB} z(N8`?Q%N=I>7(@#^;}oGln|maqITv$Bd6nq5L&iMP0;#6X(`dpxe^y6Hd> z$~oz{|8?wN!zO;3z; z-){cjJ*H3atLq!uC%6SvRAbhw^gwyZl}Cp(-Pe$y%F2SH)%lgx6FhoJ^@i(r_7#K` zw3Pqr1;B=cgbJAa$4Z%}Tm2j%piT}veM$--0KdDR(kq3RnyZTOggyILXbBVjy}W=E ztWyGF?#aB)vhwo66cNA-0+Qh>DwuGh0ekp7s1l`~A~AGy12qrWXUm8z1h2z?*xm z^!P^qVw85IToBLBk;rIKSKFhE>1*yO0pC%LtI|%y| zFVApm&Fv+xi!hRx!2Oc2$nlxy^t2vAkmG)zYV|LG*VSScU+t_B5pif8iJ{+P2X~&T zYP5V_=`StG#O=(<6x$7|AYoK+WVQ$HJMmf!Q+PsRl62hGE6Q~t*#BL;cyBZejC(Nu z2PxtGta)16XTs89l2YPqAY-3mMl{Mu1^riIBd(B10ki<%k&2`bn^a5@d;-a6&s5Ji z80-~H0-+9Bqt2z!^Wh${y6PE!8_#j)PLzFL_M!X3_bVlmN6e_GRnx1wTDmXJOV>+#S~~tuC4Z0p_wRz= zbo%Tfx$kQHPl11%ZxqA%zZE`;u<2#k_c5Yk%c+i;>5iG)itVs@e8e>O&#zfQMF@U% zcOT3z`~TNCh`0v+eK!B`9c(}$2$HOTYf~xr9;N;7`)py(WpSS8D-ZZQu2$kU+ICW{ zU7fwg`a^x9vyz7A%zuLC9~>Q*n&tM!K>Q*LX!z2x;bYQgRY`_}Vyj*8HFdL>SIP{U z0}3>%4k#x)HCEpI77205$rbteQepvX>-{3AAKWXrN!r_=KSENeshxR3W^um+m)YI3 z@J{^ka9dmFz(8ef?Y`?}Gh8$Xc&7_!YWnD$G&sGxy)s;dL!cujHuL#fK)BE|NG+Av zbFeVq)6qf2IApTGi{tGz#Mp)mjMsMs;*pXK4LnoR)1*9|W=hN*rpzrOCwGz=@nAuR)ekvuuNMDaz4yERr@wZO2I^QT`u$eFCOVjs zcfX8;Bj`fpNpBmV!CvKlsqB%6Fb|uukUY8VsmK>=T#ADX$&r^tbOt|^}4BiMQe(e z9sK+~i9E_URI!Ny51kX0k7!vw#@KZ0foHE6#q~7Yd|t&(%#t#(2pqr z^PpvYUl%ZzmPU~%y|KFbo{GpNV`wqhzeH40tgmbT(aJ#zOR??9VRRf+7&ie4zLF7e zkEk_Kz=GP_^@>!^mfW2Kgw-Vbnywh9+Wh0-;a<=ejg2cjf`ndZZ%Chq34FhD^TC*R zCu1@EPS`lMrFEbYEfc`OK9M*6%RYZN<=u$$i7>#fE<-zZ0qG{lbww$ zxb>QxBiXG|%3)=W+wzaSAug^zA`}YrT^*gJjhAUi@ogS=dV|S0Rc1`BE>qXjrs!aQ z!~>DaJ8~>@A#f$Y0tQ>#lL^|f$Kp5^jWt)W*)OaVdxP1Y-(fl*zpaHF5tB}SJXnK&fhdNEW7hG!vd>)MJB?=J!SQ}C$rQ%aJQ!Bt3`+Cj2t=Cx2^9&I ztIvAk_Ig{I zSWJOsmB@2qIB?a0hafrFIm5%lgA+(LHIY1cAC;@0)S1Lfco4Yx z*x~o+X8Z7!!j&2GZ2#0=+~qX5&1+icsy(a$)9`jV;SU%0(C5cvCm%M%FPmm24 zdg&A9tmmFn)%T`jc_t5T=Nm0>$hnByx3eeI8b6)v+ZA>nzsP4c1r;%MC{Bo29c?ww z&IBD&#PDF2X4?vAHgtZ(_QJ;;8Q;2QUa)e9WCv1L(hIE21vHXeOCPY3K!lC+k-StP zc7#_sx5L3iEDQUCrRDmrHs@}8_x4em@`ZWl$GyGaXEXSar}mO8k+W7(g+05M*fi^` z+mIT36S1tk-AU${kWkHG{KrMRs?E^pEf!i9&2hr*6#&^t?~fm4YRi_$1f#u>eA+70 zFJ(-+Y8MBCgM9^V=aRddMj0|j^qTZYUZ2ww;tM1Dscx>~w(S6ZX8PtB9Ncz$CY@U0 zpq<_5uLBbMbBLH*{+Gkvdx$*p$o2fsbAr1L{lm+n>@q`Y1g{`8QToY@jHrxE7!~E| z!OR&$*Ben0jqK6DMDC*`0sNSl-UX8@hQGtw&d&8#KQxz!p9u+or^sg{b8rdLb1pYBGF$YN*}M1Q-BvbS`$xJx3k?NBG6V*iZubc&5SULTVr0nj`lMD=eVMO* zCzsz6eozzpoINI=fL*fG`sIeZbGwe4EH;=unz#`;Q)*j43>sK53>!b=OVOYL3X`T4CwoJF}0#JSlJ}2>&1Q|pKE1eqPm*e9FWxkAg^!r=+|GE5_Aao zN$fPtcAG=t;o%?#4RBinz*N=Mg$!-l)$+Fi&>amx4h<&t8;j)n&l-UD%~ZaRmLQ`Me3 zP)Q=4354sabp~4#*sh+16f?Nb=cGmsN*kb+bvqsBzQ~@d*^lkF*S+qvwpcwCjsMBg zH~56_rGmISw%Z>$6e-~k1xzM z$ni*rY-9Un`cC^1U~ez~j(iCe5m6-4BaRbfFVbi}(AKH4R2DtfK!6^l!3U}^r=L98 z#q!tc)k2`oE|A2-iG+iem!)YH5y5F*U!RwjMhG3AvhGg}Q?isDkL7NZ8C}xGaM5FuMTW% zq@v z-y6f$f6h)vXVw);qEj*j_KJWpKF&N?B|%L5^X+TARUm&V6TF~or{PqpG*{*N_fTvO zx8pIJ>+ zMV-jqAwy%myligpB#Eu}&#p2O{S(W36`h%v>$Xr+nr2y4E z>W$o2AD@!!TmaIGmh|ffJW#k4)5#(R%|=Nvv1Z|IV9MUIy9TcvfIEbSjLHE=iP!BC zYsW2yUR@-Nw83JY+tu|3kQg5!BUj~s>4@h0SxchU31%_MAMx=-F};5=k|ia%IXU6I z=qlwd@mM=plBw`sw3E-Ee;T9O+pu~(t{29wgKq?MZ<0rPN14iqAu#9cr(`l$V*e&O z)1+=g1i2_**lg4yKn8-aT>Y>fvaJ)lb$hCn0Ptc7Fhhi3%(xZJGFDzE>#n+!4rV+U zev#^NAq{RCcW&$baSfptHj4-QrGsmsOVWM?X`hdh6#1C^W~eeyWr1dd;{OyKhltyw zKI{)iH;{HDQ+0g6R22Q@H=LJG1cINSvWMlBUmDu05$v`b+lwN>!U=E*^~5<){UFG~ zV($LV{8ycBXEl6tuG}Ep((W`q#Ap=$HgSRdQ>=%F0i>lXck!GGx5u9QcmFf^=4$IA zJMQ7p(Q45!7i#aX@Si{U5kmtB&ivX!BfgKtfq;%6C?c|2Wi~*p2^RWIe_z7M^z0-N&&ooAz}hIFqxim& zhnn%QKz)7|M=kXeXwOUl^9_?=1Jh5%P#|#mj{pS*-rI{WY+DX2zkq)0G1F8$;wd-jZ5A^f?ep`=_|6okN9RSu@+Hkkrq_9NgSr-#5(wNz;lG z7jK88x1jAC7oBRfqP%?MPnv3*j-d(EuJcp(wuqi)xW%9*OLX9RC7?sgl(UVZe9h`2pk?T4CMh3?ugSDw*UcXp~kmrK(u zzTcXdIf*gK!YY3NGTSad0|PWT;9<8dOa5dv{*CqC@#U}SvoOP{PpM**fRKnCi0B&l zE`1mUQOMlvIkS=)G}A}I5})uGcjvH70uA4KXevjjnV_Dj9r|@qE~z-t9v|cyqwW8G zz1n)d@^oQ@v1D0vY_V^#DY;1`yGGODVIwLTj2#iyIcHhb=pRAC6d$BQw#^fJw6Ksd zD|uW_jqU8d%jZ>=XtN#Pw^L?`jIxJG>M(n?2AYHBL$&v)_0#KiK0N#mX{ZThhD^KUccIM@PgCfM_bGL7(BH31V=BQ%h;=slUZEsTtfUDMS`$n%`tE8erD1!^otM|3FwS-W$ z8vVMiSDZYP9&?;Xy76lf%_ffq(@A>3#cIE8e1^<-?Kl&Xq)XtXsVXB9Mal zd$wspHT4=OJoXYB%FBnLhvnJWvUF0OMZevW$i&0x%FX@igM6a8SOn526%}V7yaM(S zp=<6OjDNged6b)yB5nVQiWvnF1(kWMDd!{phsh#)PxOmx*6+riH|)=#OE)&iZZ~Ji zsyr~ze$klX>HPqS2`GBevQpo_5)DH=^#tbuJZPS$FBT_15>ya69#g(#mG4}u^wvMx z^Zv8`BR}sjE6bj&S8+gN6c|v|)#N2!wRe_YpTj=q)=u=1W*z#rx*(hY>g`NMGUWBR zJxiguuy7ARq$^a+KYY;Lh`u;ldgpFd4RpSr@$tIoU;|fXcJ@xg61Y7OAV6N30->Ti zP_A=xa~Eke0H=)xfB?36i2RcTvA^a4(0yE792XZCfTRHuV|DdZU!UZG+o(%tbtqS? z=b`i2$qU18v7`M8Dhht}@DP|`!F!>E1$<;+&_^pn2t%S_MfIy)xX4#iyq~W)fKpVH zFUUOXR*WFV?0Czq2L0;Q!|J(eGi`^dEk>$9<@7u_ueb%4v$`y-Lhk@-`kJ~7MTL(o zaufV2rv2rebAAYY2)~_<*H8Ne?C#%@KkJi9`uL)JH+^Nmz6ngc9(L|0Bu!DhzwV`s zY_h)kf!%N;np=*m8dasEvJ4sam*02Z#j8)(>6s`8*at|y{75HxJGXTrvvYm6wvRWD zDZo`vZ5W2R=r59?T~`5oFU+ar+IB0OZ#3l0lq+G!J$0Fm%9M)bnyio-O{3ju_wYUY z9cY7#Ulm`h@9t@jw_y?e%6((rgF9HL$`VHA>9Rd$O9PWp&A&cZ{nVhESIn^3;U_0U z4t$A!es`MQ!NILN@k-Mu`1=w&S~0?Vfn>2 zsyzjQT82W&G^It5!3bycpRX{`&gXGMinEZ`LN1fm~fk}MaqO9eaY^bug>?6e2 zBK<{UG~U@o1T;_JR178VYT$BZnol2ky&p4gsr2l8Bw9b{DxP%HQ|qj496yNu`!gNG zP+Ksv1G=R7)X4)gEBxL@kw9^MQ>moNy6JWuuNBvNnn_#fG+wRwxQ{|znPWUlUz5Yb z&rhqm*CT@K_Hk9@`Yk|I0m{!cXRiK8>5}eQ{7zHNExJR*g987#w$f(PP_(%#kS-&r zMV0gv9fS6#zH#w^DZn4awR?tDPuQ^pekhs)1u%&7FdzN~PL@hNvo|r(Cq}$vjbZ%Q zd3>{^VQt0k<&{E;E$K(s4x2nxy=w18nTmrd*jV`a52jg|A_6c`OE)J6v$VfyG!&qs z3VVCwDJq6=gip_uvRm#~Xt%Alwr&G|FHi@E;juaTH4yt0J150^23 z$K~dHZS4;Vc-g;Pztmy+%f_9w#lWl(4XrUWSsT zNg{6z;C}DTcFok_9PAeb2hp16BSeOh>f3P63=KuU)r)~WrFgmHUQn_#U)OsL^z;LU zk78Q;3;lRON4!Z?uf@(sKk3|k^)NNCJkS97T>Ios_Zy{~^Ac_c`6Mnmcu1}2yc6BT zj4nuc#C|7hs=lT^6|Dt%Py^I%05@L)qLU#aIM~>K6S{yBO(<&xdSkH0d$NfbfSn=0 zD?SHM*l}tRQBh04=9nl@1{QEKYCu@#=HvuB57G(>03g}f$*B))gAf$Og)Ucu7{Cfd z&#N_jBp)X072yV!e1JhdXnJTg983ZanC$B)K%fS0{IS_K6%Qe}|fEl)FON<9IT7VLg;9NmY(P${xSv$8UN#<4BrpK=T-`VZ&Y zZ{Oy*ebW8ZhiL=4FEArvB z@;UClhM3RA4x~nb>eHci@pp!JH24a5lsGw+z}mYCl|*U(yZol1roNt@9}=0~^9W#t z>O_;LCsggWwjJFcKhyE!+BzaH!{-yG0Tw~0yl$${>(Am=UZZ5h;MpzvJB?PhsrvP+ zwm{NAw+$7|l*ng|`uE?>*bOd!+2P>Hr||m@CXE4}sba5ie&3m0>nrM^X24@7{b{?1 zTVczr`*0C9&w#k{KBq`AD-*VI#L2)ASz+oE*j|Z3btPV^)A1d!TLlD~01N%^7!%A$ z-Q4~!Gvp7l53N)|rJ$cPby#1U6&X^MHFLncsZ@ zzsnqOktL}{54Dzn3|CT8GIa(}vws0=MP>iK_;cY|t%akb|Mda{$%8d8%ge)zYXB8& zQgKa|D#H{}3yjpZwO5ODvX5lFx#n*dX=FiQ!W%(SQ}aU%{r4!`?XG=k5=3~9B|%ka z>F5c1|DCa{oE#1j(ay@sF@J=~zo8sIe9Gh2wl>hHYs$;}k|>A%yIhN(W`jUG+q^MJ z=9Z?I4=COvUQ%blvj;P1}nnj`etTk z9Cn`mn`HM7?Z3zYSZjb)cc@qW7-9M+p45_C)u~cgwl)2zR@E+fm8a{g1snvVk`PV~ zcxdIi1K2}f)G@~kr3hbr2(cwWhJY$(N?Ih2lL0V|$S?gZc^ik3Pe2i4thwI^o{Chp z-{eegm;5Jp>$)4-1C4Aqmu0I;?B4QP_@>Cvy4?wn=3EOl$VuIS^A~9Rh{UNZO((!q zes6H!ADX79PzOw+Q9q(UX)T*yZlL11f9VX&3h<$adFKjk={)JOX=$=)ke2=hY|6So zC(~*CP65dJ?Ghz?v;3_r@I+#P8I*@cON{>7;5!B0h!5)c9qXO^I084FtMSwPWpIL7 z(S@G%_d?5lvc_*B`|Mb45*CbIfUZe)NMHV#lX!`rwb4*gay?H0b8fySlFHd){^w^k zwYD|ci~YTwHT|^VO^4mzV5y9_wKaOY)c$ar$wEE-wcL9aW*U!sE;fOtp3zagTc@h& zGB8FF=~QBmPe`~0jBQGHoSO?zJYut4&)o6tO3OiRx6AJQFKll7=2MJa$sWg9GANMM zlHff^BdaJg0o=9u#()`MFq=f#+LddTrGxY$NLG;m@B!G6AR>Yk-%rY<(+nkXtN0z*2O4*{maBcAvYV~u*J5)hC$J+&)T z+}Ya$ExQ0<+-p_I(~N`VJ;myERN&bS4z91S2aU(YCuK1(PyIJr4jLO7k!6oJzZCCs zVdd^`|A=#dvJXK%?O6XTtz>#UC^)$;6vd(=fv3P>G1xH(q+{q%_>}V2ty=h1Ns6ix zOCc}>uP-JsM4>h9T#`TuuGaLKlpWFfJDv8f6fQP*XJ?=yKmM(U>b;`%XUTB;%ZuXI z1@x55;)%|QaLv9ZU)k(#w~nT}%ix;*Rw*Wb%Sp(NNQ`1%Q`@oa%$y3+=smkRKDP$Y zdtDBSP^YHd`&!W*kQ1W}6UT+`7#zcdMrcPARA3~tp`vSWP;w_Sl&Q$YZf#b{NEFsq z*1Ze=xdqIQ^8YA|a!a?F@Za6qiE4u7P($ZFjEV&f(%TuQ6g z2_S7GEb%#%al`s*HSD#l>Zi}2f#lNd;9&OY6XxL|G;oeYHOx;iG|h#C4I)6g zx}e0io2~5o`ZbTw^?I8n3f8ToYfC~B0$j0w~~FR2A$a9^VEib3l&o=<14(jb){4wA&g1w0FzKD-ntaJ$-$+ z=AqR0U0@Xk{~HjqU9#%F0fVqZ*X#p33C_uWy(=N&dL8WqqTz2#0A@VV(0u;R{u9 zrynZW9?B=!(7n^Czwj#p9RWSv!4N0n`@SF+7K^^AA0_&w8+zDDxCUf+d-x&aFBLx4 zk?bqd(>8r^VVGie0$$%y9w`BNtJgHIdVEc6%!ZiUX-x{RFK&Qsv$R=%RlN(L`D@f8 zWHby5iACH8Ww%iTZMi9)o84XAdE~G*gl5nZ?vNJpXgYcP=%VBg39G=iq7)>5CVBGn z8-WayG(S(=+dit*ZZJauN_Me(wZq5G=#VXB(_k zD5<*y<;q|J!OD84!fVzx|K$GuLJ6_g0l~YM8_9#dLR@}AoVEr5A?T*3n`mKVUp7s5 z{?F`W=5mjgu3(`_027L4z$E4vIGZTs&~%5{oo(m;`NQF2ny*sGXJ_P*3GBT$ngR<5eJ?5*u0ks;NR~#IUU?Uc=&$r25UmS$7G_N!m z4w5p*)q}Ow?D6POQBe{2XKH{L?yzLz;NSpiL1c77U;T-An}5DYqo?5B0n2HuI1_Sn zw~(WxJR+Y!8dMrnsQhrEC>0p)JhQ}$T$H?zp#aV_gxPnRhk={j;2dLerG$V@+;GJZ zc=RmR=LhOJ!5l$7y+DK)a0!$C_E_i? zTg*o|FBU@$EssoE#jy{OSbJes4N>6goX?O0#+>7N!APW%q&zpZboJb z0QQ21_*|4+oa6NL6&4!R)9`b7N&5#v(IIyBf<92FO2m1mTRA#TYSz`%fUR-jx$-)w z$AEhU)Ej!RVlt=$81ph^0m+_?W&x}}K_FuG1%E|IK(GZOp-V?d9T~1*AT1+3?COU zIwlg9daL<)vXGZr5tzg?eAlG-j3DS-^91Va+g2=k`xbqEmLB^Jj4V`1=nxlfS)q{y2e>bDM&tS)IFr0xT|08PGYvN*wTLfW8E{D=_@Qa*Cf|?W24` z*DAqO_zSST1#IsgktGbts;oReUIVlSjd5ylC}WUp-{*t%UU2{@UGHQKT7Fzq{bnyX z7K1*<_Q8Mt)PQf`>J=6g0MoC5K?wdPy6^jSNYL8cdUX01n;qZ(Ut_mTB27Kpv0_kGm|1BK7;lRhhU-R z^bde+Jew4=$G6Kl7U%l%`b;XdQa+FyG_~Vx!Y$=qZUL(o9FPdn zAzXNU-&GVK2+GU(?p2tez{ENdIGF_Co`8yi-VY5@9jVgVB21)G2zOz8&?>uYAw)01 zDAAwRAIW!i-b6$afvQ_pW>>VwfUcP~pDkyslr81tFvBQ#mwk1HlFr6**+>uK_fq!KTgqu3D^o z#?Yo9@X!BEF8+3#3l+`CKyc5*%x$y>WG0lAmlvy07f8)p)$5`a6cn^~M7nkwF;|t} z(@}379B6}nl}fq@v2T2v`ud3XZv*Pu^*?>`2hDb}FgS3T-D?g_VBXAApw#Gx{?96+ zCE}^61a@}?%34r*Y(&`0gRRfgxjHOR&T4J2-?epsx<$L~2)lU-aM@q>Wx*Kl>yx5IPocOG|MOUoXzr8d(@eho9 zYMHZBtx%Tbe!ttfAJvI&UIT^%AfG!g;-$@;`k%6V(aG0C<644NYALHBOVk8i1wGx( zW&Ma32#*4$18@m!fqVS@Yx(<%4OegO%m2Gg+y;rI&9qvAcKEFJHG84$D27Rk!)L(# z6dVF!JXRjYcSl>t3Z2{mqsDZ+_mIyWa=^UL$yv0tq(b;PJ1J?P(g3+ojxQ=yO0(^r zI6{n)Dh4!QT^)ErK-mMQ?b2;yv)0|bDMgCR`}*LY6wK!ynkgzj`+%&3AWRh651;3L zZlSt2$~7!%OuLcM`D!12dRhvC@Vd$dD|i2!J7{JCY8EhX9j|uyf(A{Hrf#_R{QL|8 zm%)nw+PlXF2Ii~2h=+w2fR~1s-~`kwZEtUbE_Tpq%ZX?8iBP%X|BF42`1Ke<34+}i zydHP|DtfM_)d6$}>=P7n>2s9^-&Q_`a;1GFBdgS)eG_14Z5=T%1Jc$+#l(PIjDwao ze8dD1_A9upFOL=*z#aiX#m7GaFDyvc&sG4{e&FZcP8rY4>VE`P^@aSRV9&R0R(9Bl*o=5L-#_`=F}a)Z za3*m@o1K5f?fh;4D5JmV^c@c^&-Ukbrfh)BIYlqs$I?`FK*KwFEW7LhiToYwGtdG9 z3j;6=xv^#@Rn4M45RY7&imtr@_G_?2OrxkhoO_!(WmZvnE9dem`Y6_wECd;&umEw; zkM6@3Bi=i~7noAQigpbr#WGYL$8=I&5*(Ws+poYicE-mRj0$ z&$JKt)+DG?uYdmkIG4{>eqexhaK6@7nMgw4`*qQ}&lP~+b3QQH-spx0Wp!w{i?-uA zQ(u9j@-@g|jT}l`hJny7nR|HL9n4-m|Gv9(URI!-dg)tn4-!}P@_t^305R8I2i4dpr}abE%lE%L6N*CkrE=Qckv`5+^`7QVAA|f_WiQHW!~t) z`h<|f>J2J8F|pr;`?bI4jvN<@q)P{n<2I5XXgZWzS(#Y%l^+>lb90x}b0^o|9u^><9eUp=u0xyq_DKhK;QF*|)&E)gMKt~5PkU)`7Peinq8^!PNh@cz^26#|r#$Z zdQ?yqt=Gj!&&$(Eb5IZ&|MS&Hra;7P0r1n9)%7fwMEZASz^=arLQJ6R5uRl2W~3c* zcv)aLYyhhgM~aN%xME)v{?0Wc9+!vJmOKp(U-FA%R0{L#d-*|~_0dHqMWtJFWHh5UEeqRIef2gbMn< zY%OU10gwJ}CjY*=JqGuU`py6OZ-PXbG&oFh)c+sn1GJ*Rb1uo1(Xr&!$2yO=RKucz z`d4Q%%trk_bsm!w`AKODsQ>-hnesTIdDXOO85!Q7oeLB*_Vx9FUc*V&WvsWvndoAV zEMFhgeyrsAXk=cz+L=z9;;5~|pE4(l*q7a3Rra;TV}XKTFvuw^TC3Wwf4$@2tg6zq zxGMPbhb}_V{hL1Hn&Wn3t<5qm^MviWQA1Z39B0hc4BhkQfX(*AQo~Pmvm(7H0{)H{`clg$*0&%nw|M%ws_H#GIbX(4wTIQ0X799@{)IBN!!%o! z?jX;=fIILBTwK%XxuK!Kdy_iabru)|$2a!}nIR$5awFxdyVyv#g9)J^)lI9`Y^J^K z1Xx(8sHP8P}W8>-`aN zLT^^kE(}+O>MzJU!Wt^S|G&cHgOih=3Y182-QaVr*tOl<+`iPR3M~D$d3t<+f*s~KbAK4wFTL@&5x0SF<%EMkpsu=)8RdZF9LLIO^iax42gAU~76==d zu~c5aTt8AKtZB?@!bC$3=fNmQu)kjBcOW>q)gvV z<{*%!lg?afOirmi2HAaRf$~!-DkGP-oSmr>Hq82=h+=X$xBaD$y~82jZTAmMolzR! zgLw$!VrV@Qy3M`e zL?QrS&e|{eAH;xj(92a6p&qJkrH+Gx{Zx6aJaN!9H&<17F4s2yD9a@OXu6Q)k+!0} z@9i)r3BvfDrRnLoV!cHy$33L@;Ng(c$4vr*i*AYLGwL6LeiKR!leay2a5!4j@9&q` zson~Fdx7%HPfDt2ZJt;WS(sxb&^0evDR{YFZ>Z9&@<`(sBw!?V=n9U0x0Jg_QE)Q0 zB%r{jAc%mx%$N7RWZNbApuegpSiG9o{_gzu*3#84b!0J4bso_Rsk4MvXNWcyw)b3j zBQ^ppa%_5y3b&K;lIj&`HMFqsG`->G{9bOS&rhxe@&W@J2AB+yD%1fG0Adw8?qnuN z-MxNIN*25DOi3B~8oAjVYyxSiDV-;ZW28vOYIIo^vQ zod;k>bed&&==tH|Y=_tg6;(62G(Gr(_3I}5{X-j8Be9rBem?_LHEg7pRu|8CTV|zL z*`0L+2@Cb~>4vE;*@8VS=q)b0)2_lXs!Ajn%`h-AfikK6+MdT*3JK!aFM!2L_`1v9l%qbWIPW{29|Z8DRRaR{slC3UjY#MpK{f;{w=uG zxXsaG_K%LdL19mXSgrzPxY!p(#h9ol`L;sE7nzqynH16tQk|ZP25DQzAl?XG*7L;U z#Lq;w$4k6AS?$*g3uy~!X?iP25Reu!#?2D;N=+mfd4;dPuawLAuHTEh(Ur0$AwRKT z#;m`0PK*#IqfEA!=`($FVPe5(NLp^qZb5Cj;n;^Xblg0I^O(CO`nIRDl&BUrS8&sU zsQ`gzgS58;<8dKjo#rIN>t}DSlI31nl`S0AS2brnRV(`E%|nVvbaw7JN;o&Ab);l`5wJV;2CWfhIa)vU}Z~I zez#d2D=RgH=&n9-V4m9RBh1>H=vkcCOinDwV&1|Mt>TQ#;EGF1)=;YJKU&Q14#a20 zkz`;9`T3KUT$IC9E?v#i{bt#s5XI|iS~Ss^lq5YYbeM~4t1gWiBMkKSg5K`GK=A$n z77C(Oeht)RK!4@E`N!DkG*78_Iir9wyx zbQDV#0_7*DkV1l5nUaFOF{zN7lsU?gR+UIszI2qq<0Y4!Dw8bnd!cXVO#G+IzeZBf zClCILSpvR3KtT%lT>uSk5O`nDn*ti*=6Lz#@j`x@1XM$VkDdJ0nq&P_z3uA&=+7`rGbZ53_6gQu#f;Q1zLpx0RC1o_TMbpo&dUwzkw9i zR5ocM)PDuf;F(;iHID;w=0F253Z#LU^D%)JUf{C$_N&tEY#neaM1YCq-C`ZQ_pC7) zfrfzrX958xW>RwU?)EkY8k)+{7$C{-yoG6*OA)I+7?asho?0U z@JCVj?`g=sQ#i`&(zhc)4&r8OIs++%=_6a+taht#NSsx6i*%k`M@~3jo&&E7PGo03 zBHI1(S4Sp`luBQ*;D$UVUw+}zEQaIF&wS9GZ)kb3fhMJ`-Le>%_P9Yb5T}oR81&*w z^24!esT&bV0e9#{_3BV=x0t;3m6^Zs&JY8G0@?@cJ+I@X1NDk2*xy{KuzQ(c6n|&xzhY4rk(>Lh#^}v^lu{Cs>W~k1=1a-;kg{jn* zmfN{1b=RYP-~Fjd(J*d-31D=DGiOaj#H6=t0Aw!gr`E^wp(xs-*$oFSLaDH^X>`?S>P_YS<34oPAwcBhhC5zDwd<|w?Q@l^}fp=oK7HM zDKK17|Jj$3o}5FH^QF(^r2XJyoNDA4IA%$~0|#0MT0y5Ovwo)^km0B1?_BWx_wo8`v;^4_aOalt34>^S;0Oln@G6Mo2M>@Jc$P;;BYhSN&F?{{Z+K`Z z4k+sdpdl!*O-M?DhX8E_4L$ucSjF@__VSaHlR=NKcKHlIdKP5CA)hN+`h9(`OwKiQ5y9`JBLZ+ zD0pViue|^D72-e02gt{`k9GV6iBdxY z^^Q%kKGM;REG{mBy4FR`p0*Iskv)AP`%73!nLB<#3hGCvih@3MQL0T|I7GpU9u9(H zFG|G_lJxEE9&K~UD`T&ecm}puv2sz%G15*NwOdZj;hD2<@QUhSY#hxQq{8USpmAyr z6`e2{AwUhIpp+P|$`uz9`m5(_oX!oz*WfH+>!;v`Ai$!RAoelYP;eG-LM=DjA41s2YAeCFjCJT3m;GZa>{U-H>PSXdi|+a4G9X z^6*TaopCK`%=ZkdNsSKuv_T?y2YCv7(bJMrfd zB|byzAMK^3rJ;bRsekLdglo&eT~kwYIv#Yk14x91jEszy_VCOTAZ|EmftbY~<3bAd zN<07(2kkd*R+c5vCLo2z5ynO9Hh06cx|*d&u98CefkGAPNB`$dePQaDH;^|YH~i`n z-(pyy1yy6dyLB~6E-_zXUoSFiYnIQ`C>B%COxD!AMM-zU<6d!*CkIy0o9mwT~zdY3(v@rMYCrThgo!PO9B6rO5yCmkt!c;T8BWs3Nwf8 zIaKFR*!!^g@NlUeywMOAqP2HbIS!7mbKj+LP1aegd!97i-zwPKH(keX^nsS~-vE}i zPI35-sau)J6_=JWK!Gr`nw82rz{lI#HPwgLMk@XrKo4H$A;&)US(&NIi#}2RYJA~hpGTjU}hyg;!(&cgnQg5e4Ovgrn#QXxap&A8( z6J{bh@g_nP2J(*n9i)=RIw_$ZBQ5M@;BiOsO7%}qDIz3&m@B1B7S4}tcE;`(&th|( zHRaIDM$va_dggB%q9hrlESK1D)$6lVRb=D8zS`Irj#^&^5ms#O;Bu`_uKJWN(Q)X5 z^O3%Pl>HCuVpC0FR8*~nz0Irugp9)Sq_l3oy|HZVm!sQ}kHRiY*O~xAUwb`EagUDfL6#BvP z7c?DYI8&$DM7HjS|xJbInpyx4dk*8!4ZYgcK@xySMDcqLkgxMCTJ$0Yr^Y;3rO+AT_eRz+ItK z41yCW(17+85xNkXH%jx<$ka&6&-{8=a4+lg6OV>=gH4ow3_&7L) zOB3kW`2JcvHm*)lkq7;azpkz2{6xUwC<=t+9g~s7S|S}4$`bR|&d%lm&CmdoOsZ3@ zyCZ;1$I@oAy*+PKODhNjGQ01hpgWJ3&3DEyA)WAqweDx4CtBgyY}A*&FD;pIUv^ht z7$_({MHoM&uq^iWZ0U7W>@D4)*xIIuePPF;^7So55wKEDg7RSmSQ@Y08srmdk*Pvx{aC>B6rHzo|d1^R?9s_Zuy{%^?VqkFHdU>BLf%!V(lXBsH z5XwzD|FZLMgzoX&bsF{W;5Owgk&2d@2Q$)3uQ|>JNoBo->fT<_H~>k*R=sLkD}vtP z3DC2rRpD~nLhRPR&&{VGBax+XP(bN#Nxcg6FQiC_>t1ZtTxo5xzT6FsobtS6X1?9% zYk38$@a|pxH+UM>3b2BUYC_hoX5Svn3>3<{rZMHRawOdhG05eLB{+UkI^gqIYNz~4 z^zUE*!JbA&Mj%sQ?AI@*w2`IZ{9pR|SOq_R_<6p#0%hXPYb2!bu&}U@kh9B6Gz3_X z*Yx?m)g34-|9yNHGO)6um5hyqf84nM`tmVCHj8+*(&b76;Rc&!AjpEOfmd?@d_Jb0 z*FT{we^!U}BD=q!KFS&nCT0Ur%L0%bK<>ymtd#ux&tVMh8Vxo&8J}yxA@!j^QAmN! zcI6SAa6qvG%G-e+vAer_wZlZEzYml#9urhP^!+A$8M$xxqeA&tx=Z!%vUiJP)jE%- z%X9rugTTjU4MY09!+9%SkSHq-R}8vk*%G>^wKQIB z(-9M^AfSPWXHC?OACrU`yyTh6+Oiq0GvmVVT3m1D>&wSN(SZw&oNPcuj5`W@vehEc zX4$LWN-{SU)ynsur`2C`aRs^@E$s}OcbJX8dYBAG*x~bpE7OQ^diL_s%~Z@r6%lJjBEZ|# zm$xhg1PIWeeERfBb-J5Wv%PgQj6r49D>uiyOd9hdM|7c&*X9l6{*nn~%V~G$R?Kcx zR5&HkZ=(oIEhgnH*4I_&XxBOwTMVLc*xKG7{A7DuJ@6Xx{HQFYxTfzmRWh8W?rwJg zW;dA5JW1n=or6YMMS&Mu21=rMsRCc$jWW%AFQ2U(_a`w1x(Hj=bty_2|^3$5Jbkk9%1c~H^73wU$mZ{8gFotYzvwL12{-b3U8-A*ms%`W}JZz`g z;!(K)G6xKfgFZYKQ%;O?Ju3+?KmaaD zx7KVNycBa&_5ZOx0wPjUQZh0~|EdftX@{OHJ1SRoJA9Unm_*u2$&!K2Z8n1po;FS$|ssIUJgSSd^qdshs;KCpA?>gs~&i8bPRnUG)SK!8y0VT%Df9C0*8YGpxl$BCLS7yM7yu9g%3$XIQ+c_u zo82xyA$4zVZ+m!nI26h>H8v)XY?qbM0=+D}6e;Qzv5SON1R62%Wx^daB$983pYG+x zSBt)Rs@*#l&|m*Z>tMLtzXBa2CM-kMZ$ym`B@?8K8L=c{%ar+OWhrQNAGHr=78h$J zwW>$}1S)HB72qj8hpyMuRlc>I$?LAa3u?2o7q#kJ>#T8aYFXK?cE7*YCM9hM40>TP z6dCUs%vESonir3P7W{R0u|#JMM(*uj-_oY`%iq0o1^6ubhYL#$4FIn8K0d9K7?o6hXAj_tmU1NLzIUCl+7^KF}Oe_}Mh@73MI=qj6q z1f48?tFPT|;-92vuf0FZ!q=B2miKIl4}=4B%jK`dDQf3tNW3g>Z%H?5-p)tH?@t&P zYPFf40BCfsh2xv0XK%PN1Sm7{>pYb0PEC#>st}MDssesxw8xHOPDx|b}`0m%H;CXCLk{2xY?{yElRsWu3c?qzWE)4H>-39h~0y&YL6~y~&@YOndj2>L{&R`m`GFYm7u2(81Jhb*K@VO`uunY}tT}zXWhw z$l4+8;h$e^!tghpg0CQ#dmAT*w0V@*Yro!b_`$$4L;cVb@D0f`9O$yh-x=jQ*xC8L z@wZTJYb4!0Rl38w#c8hcn|PGTkkR80qMmZ?W-iC|nX)FeS)c#qjV}E;B~c}N59K$m z!6gbpU}|LrQGQRmIg!HR9Y1SRI|vXdDY^oJLVw#({2;rzZj?p&IiM`}m9 zVF8c_SpmiDqvgy*j1$eq1C1eM*RRxrEs)z|*lA0R&fCKEKU^q>gx7a;sPE$4YkkU#kaGt)l5 zpZaf^kM$k`LYSdCRYz1IyZe*VnKh;Iv*6gou2;(HaqF|gErjArR5L!@3$Uryq>`yjrsNv&dIshATEi#{$M^Jtg(B0&d-bgyA~feYLFNq1cVbn z=?}ITx3}A#;}EjUOms|LUE`FLo1J(!w<(FLT_*@pd(x<}K6Y=x?8WbOarTIdJKhk` z+}jQ7z-7#;<;>kjC(et6#At&X_w-d;m!Tm6MsT|AjygNkkc!1P@tlTzdvTf!LJ12t z+zZacKeRd~G>d8Nu6n)vo+Ja&{^y;2#TbyUU(xV+Jjlq>pz0i=+QIfBnc}kh%5_FpHSpdbku?X-v;H+>I%nepS2JKd8 zZ<>NtKsDE`w?#Du_(2KfPiSeSRy$ixCFyPV_3TYf;?o{X=S$yb`L5g_HT*E>b~Cg; zzrTX4%~arXL{K}p)SIyersF{~(99=8+s^_I_$wCl-u*siPR+&hT>BmZ^*ANATm_@U4e8=b?G%&_bwr2=^>UV~hC>v)4uS_7*!w8T2Gcb~~p>#f#GA z`Eeu^_Is}ipdS(+l`PF8iQyridn!nTj2Li_(A!Q%+bPE=Q9^>!<{}o2uM`)f0`ahz zw@;H54RTe4R3nLZG_I?NKMBJX!E+iq8s)L+F2!#Y%SOB!HHG@4c-wt9w__zf7r7r#6>LRkWcnip&$xqiD^STZVJ#4@B3s0 zvyy_ZafLNWASzfGHP{q}SYE+m%6O&8`MNX9IF%&OfT!(G6#zZ}S@03zD^c(R=5LE2 z<9Y6pFg{_RIk@#HzQ>!SBuVJoFt5|HNzlty_#PA#hHnQ~{;70t(%RVAyv0RxXs~O= zO*ko4%kCJD7>&RB$LI3n4$qr>+YY+$C{weAT;)QS8j~j1-A=xv+r2`QhypUI0Tq=g zASV0H&{3wCj*8$>rsHYz?4|t4U){;+a#nA4+CfA|^LhRJCxcJh^@3KLDq#`A=M`sn zy_a@X7TVp~wb^*RBntW)$8+t;FS5zh;nIX>W5F$3=pg5ZtXw0e78=6d(b|+I&*3>A zwdh?|XC}aX5fj52Y0dAVxFZTd%|V)H(I2}>>UcSPG&LDKq`Qi|4&B~%!Z-LG{0En# z_T@P(A+f*|Yi3qm?aI8thF`lrPa?n7=jqWB4Gkf<&eC%@%eUf3CmR0I)z$3yXAhC@ z7nyu8YGo4KzWf3VV?_!GB&0fUC@%ft*TdN3OHJs7*_i?i+`hW7kbwbBB`R*elGni@ zAt`nGFc3yKv=k_RI|LRJ^si)Pnv0cG))C2c2Xf~avNnv`hV z^r}oq8#xC2z93fx6B3AoeNH%DtN|K$LGvyy9v+@AaduIq{(ddcqGVn)+Hy>M=$viP zA|iHFd?&1^WNQmy+}e4W{+U%*%L-OeVetqHG8kr%F2v+aEl&c%#l%S2{BIc;-OsDK zgKESgk)F2G%skhBeM?a;%#48&(jl3sQD{C>v^J;fN|Ln3BEVB9k_qGo6gB753pxAbp=5QzaP$ z4(nI78eS4pEdhlJZLZ@5!rEEZ*efa6^>_MM5V%gYCewhmiHB)T59#rqIRRvRE*Fr* z2trQ)tOBYIn=76LGAd~;S#j8KWHmLhWyg_*^s#yw!v~D2HE7|!eacq?3(KjS{7*Sg zXx_FASBsh9TxE)sfZWXr@#oHIVRxu zYSb#n;9h&U0h-8gxk4IhY9PqM3LIx0c_m_~iHdM56cf$H;%-tiU<5*))<-2ueyy~5 z1|eZjpw$Z~X0C9EsJgjz2t2#R(v>{i5Z+9UrITiwFthaW9V)&&?`vg}WTGGySWFga z?z#31cD!K@YV>&dLGzhbIl@U1uS(HJ3xCX_oj%c-X7aJ@GFdzQDYEb-auacR(e-&r zeK}`uSez&o4#^IZ_^K1~#lHsvfk}LY1f$P1V-^=8w0MOm!{4^C5TWfZ@XTp3RfMVQ zmW`2dAJ3Cl-I=&Ab#Z}cmin6sq*4u=F-wxY=hy434*#V;WyUHMAXK5Sd*z}3Yq3w8 z2$i*NTDE(VMhWaS>+?CiS{=VsQhiPR(oSA#@3Q)JK3=TN3iwEm_H(|>{Ql%+5MBJ( zD8nVBS35j!X>Wf`Hs;qcyd96j2ADVofnfXCH$G1fy*&r7B9mgJZuLgo-a&lkyr@_j z6eGhtp=Eia@81T0)8~YvHPJimgMg@qlAn9H#FgFsIXpY(HkrD0776oKHmmJz7s5Ho z0$bn#r~;mIygoA=DOPeY_R(q8#}636eo|mHuF(QEeA&0d7QZ8a2|7n|)?0Ae!sgE6%&3ii=P&Upp)mV=8TR4J2`R~7 z)+{24pkk+IdA4}|Qm?fNWsem8p_sPULFj4)kV71*;gjVbQK=VNg8nwA{|3r0faoi79!al{l6n8m$G`ZAB+^;8v)lhVN!F`$OF;8l~fNjT^ zG_F!c5#FPx*m<4bGnB!HsWtEwhfO8GKm)Msvc)(NQF*C-H5&Z~e{$Ag_ht-iMA^T^ zW2ge>uny0C+4Dg)lE@nh11D`l`l?}*HbtdP2ImUpdYGNbohJvOqvE;KGr#OMA*`>< zRRZWRRtvlJMlOtYegj8{(7E|`9)tt;e>V?VqL}IlHRCj z$O!L7rFZmR3wg%yo2JmIrPDmY9doWqnz~xWT9_sF2l1kXjKN=Iw@}PUuKX_?b!EYZ z*n}Zn2*?4uaDF9KTy$zUj8#+L`@cL>zMob@JYs4h=rUy~!_Jp?f+0c5PB~2lydYSwvXq+;NR4g`1K@ z_y3~>$dpG}I-$F9{JY7C`7!*VAu&sxPn5cw5hM`-tJ>Dq7E(9JMlBBf@>47qhyWu8 zjNXTc0ICzbXeD~W|A3D&1;>xmVw9gLaHU)&(mmo5zkFF`-P&gMe16ks_gZutQN#da z<5kF{wAhnw%KjDe$k<0^6oV_A62sXTw2>*Z-JWZHAt*(Q%4FL3hSI})%+wx&5n@O~ z5;Z+r6BRjw_yZw-?2}fO0zb+QFsl`4ehVp6W`{(t3yF2}WW@_qTIx5rj3ipi+ieYL z1vEi--c5b}N*3(!eCH@iF}l4lmpbEc>UKM&X#E{}z4Pw!#3eIs!;p)9+F1Ez(PyMn zLli6Pm!%7KA59VuomwiiMDkg(vJSHRm@(X_=r= zqhqevUOtPWkB1rWzkH3)W3EYfkLYeuH#x0ME)-4?cPjqY>{&=!-ZY>{!jju-`*^i) zvWX-es^z5(|F970Ox;{+B<-54Pp@2Ksi73XX47&nSY?=Sg`?Kdl0iM6v*m}?)A7Nq ztz~1#br9RgT_&{47*pL~i7z_!iKaq7jOWCr08rszBEZ7HAr)l{W}3uEDWXM<7x;2Z{#CxxZ%+eRQ5*1@4VEKFKZ&`&H^QdV~B%+1Ee=E@TB9~%5x zW_$nN9lnneoTo!uAnnzDa`s_>Pp?b&FxGvu2>w%%#G;Nr<&;G;ZLAABuAJ1ScR72B z*jv?N>V6pj#CV9_!3ZAq$mGsnyL6}tf9Z0;Yo#-Ry;g!}$U`~AdldWG-(AS^&{i}` z$Nrmqrxn!vLH;0FGRHX&nl$*g-BcMGl?ml`TI9MLJ{I2S@~0(xJ|%7&IvCgx)4^rG zCiqjVW?(}PteJRH{ixMm{t>p{Lia^L-QtHStH`m^$Ml+cv+(kiY!LdR6{oYF3d18jB{M(AZ)T?~pW{*V=)sCKV`dVnD;jK@ktvk9R<=kxt)gAwV z7c+Et=k=q6WwRibAsdCZ)w9=7x9EOb6lJPO2|F5iSr8SVIAthr~i&)UD{; zBxgl;P;8>3ZzcU&tHqebQ=;c_tL4MjR+_FmCZsR*(awXt^J@j~MH{Z*{Dd(lG%AOi_eHnAYJ`og7SON_L_@E-o<16;fR`azWAIbVBMzV zydG^C+_LPwua;Op<>epf>etDX5G1%oe|6;q?f14yvYz?+ro-l{Lp@VCxc|@uj6DW@hFxwSyXhIV5nH zSo|_9uQxO%#ZF~6#DensA~=rLygDp=}@f1?HiZ#wkr< z9>+bfGSKK0TBVoNEbKnM-?-iwz(vt}MPpMn!Gn^Y*_1g++RH)RRrn>jP@(ex^F!sy z(vhPnqGa{?MZ)&@M}M}Cx!=ppgFPJO_n&jCjwsPh-Qt$ zDsyIOE6I8kW`ZI?Rs1T6uI|M}B#mx21v)9hlE|E8#X@w!FcQYnZ#6fgbE?A15`A$l*20xYa}sMy^3D_r9T*;NyejDl04_l7v6Mo_VE8MooX7}(umB5p%AYK zf5vm21VhN$JGZXXhQqtp(5_dg%T&w`ucR+cD%aEym2>JKeuXu|UzHo17X1Fy2f2K= z?6S&t+QP_5w=MspdYhYCamiJ#G2KUPV%zjXW!m+AHBGw?nhsIvPP5jW0#Kb>AZA5o zIN;2U7mu-ZJuEAreMi-*Ua1pWl!Z*vzNQQIlRj-26vB6*^v)D`?;ZDJ^V7Eta)1kRk^^0N;)w|DYUsITq7Lqow}4OOE-z8QEW zvVHiDDY|v`iWseDrYM{GdVo04V8C)IYw@^pwbXXZ{m{~Kxp`_;d_>x}W!R?S5Zbqp6znbBla(df=D%BXT(yVJ z(0zFhnQwES`8+w!rz5^Q_7u_%>0|yxM>Cf_9*xaBn(mX^tY$}oXt1c&H7yAVYxva& zQE%Fi=Y(E?_{)GiteIYZI~mSn+n82ctD_!G7O^C?tCZZMw!bI9kwApd?uWu@0yZx-8ez}?Ga4&XjPT+R zdsBGeQ24X|X;u8W6W`Qt!;4>@XzdGDvZSN(;tDM5Bb6687@i?G5g!2@OhFOEGZkj> z-8=H%fO)A;no`0L`uBCuQTaBRe-WE-zU#3SGtPXAEBQ~P;dIt#C6%36cf!8&SM+&w zIz$!*Ya9;$MoS*Wnn|Pw^4uA1iFx?7a^p55U8o?Z@#$n@Ccf zrp`^)>-OMa7GHvFi17g?(AZMtSb}{Jp@4QcJ|g9UF)Xrf9t(HCN_)ar@J0|NA)aZy z{XKc40UV3_oeObKC3|cYj)pQuZ`-h_KC5y~Qf7VZ<&UE10aEQ^GysAp$aR&BJl)yi zK%5XR!jT-d&Vm(T(qIHLd+n&|qN}G_AD-Wpj^}(69&9Ta9bfUadE#p5{C&EJ#qu~6YrCrCE)yXKIxZub}@zb z6s^*!w%h!9`j{wsvXXJOjWsmKG)oz(T6{XnkQeY>qW$4HP-0@uh^Nh3TG-L85^(BB)t!K>&bLDD`2~DBVI$xNNYLmud4nIwjg!h zdDz9*6zYeBY38#DE@qPk8QJC542!yh55HHwgk=uxX{SwwR6jQFmy?!U?YAfB?9Lk6 zv4(?#)@K~&QF;T=7+8sXZu_H4=!Lzl@7wn#pS=jOsn||<;@T}Z>^Q z_Z8^r?dd#?;?ds2xOpKs89C-If8}>jo^8$ik{lj`gQsyTB5qGR;p4P!%yw?>2}i=} zwr@-C1fK0B*9NshX+rVjTU$1}ZT|YuYu*_PJb9m~)z6p|nd{QwSH)Zgr@*h^t~RTS zzkEp3lOLjfve4nx8_;rgvX&LdgfA|TsfZT|a~6Moycmm)Mx~%AES#s92G=SQ`8#Q7 zn5hs+bzxzhdLIG<-vw@w#v@1az`sbst4(oH1{!7>SpEii5sy(t6FB-zOiU;34>opo zc8>wybGp73PZnhIc#d{&WJBd!M#sg)0l_{SY(kG=NK_OG5|Y(S8Fmt&MPk#_mztd| z^OZEERe>NTY8k}R`J3VNhGWOkl(?Lit7ge!x=x_e+d23QncQ9-rhtN;SMOrtv+*X) z&XA9aKM89=*%oCxC3QJ#nUB9u!e9uC|5V3Hdap;l=)eH9>f%3zgGaLj#$BzM~ z7p~wjREeE$PO3Bkof~HLwHabEA`OizBd#&5^Z*PkS`!pK1PLAqJ`t`Lj-Bt%%tG(m z9T+`i-go-H+X!L=Q~YRu_ID;ht-iM7hrrRkt$k&c-?^lnQo5Wet|V<=%%BgeA?3$B z)7rxreGdgmM)a$P9)-_lSrgKyB6>@tqRQV)nEl3$83I{=?u3@t;;IMw+6cM7UeRta z5DjO+M5w@Tf>7VF><-2#j1OnD>_9l1CF^VBX7-2d(pCR2KsxUDt zi4gBS?fZ~J_6<%O=9FWjC93HpkZ^f-cbD{E^1*}XK1e$Pg)R$dd_^!JAt8|`$5Ek? z+&+hHAcg^062`yK_-c)xvJ`{-b0UPArV012Ej|Sse?WJzh>ihv+{YU}%q+sXR~K8O zwl}zyaYhRp8gPE$2IRcFVdVE%(k}|lBU2b&$J;H)6a$69e2+e=OdS8Z9B-$4XNAwoOd;d0hs;-e~C%b_bF|Z|}0ZEg1l(WBjL93NR z+B=GRy)iqrXq+mrObva=PZeH)g<77xw!Aq2h56@oWF^v^lYYz3SaEh8Tgo?=v7^j$ z-oP{l)E8g{=29pcU`&zO63VWraZH=BM))crEv-z6CYArb&pwvyEu64D&~rHLFG-do zgOmvXp1f!V4FKUfRv=Y^RJqzv26^X59xwsdg4kri$3oP>Z>`t?5%ri@EFwC}`?)Q$E=6y)yk^72+!SA)nziWIZdYXJNrBdbxO0ZmF?UfzD$s&B-7 zm`b^t^knV0*=EI^SGxvn;(!rkZD$AgxIkt5;n9)Ke^ev}0l~@-6H-9gP9lq)+TGOX zRu5n$ZXaJ!2PzuYBL<#n==&QphEo_n=xd}F&T3=%boJTt2a=-rh{TlQ~?OYJj&$Vuh$ng5Hg_YTMUkKcwrL`inYCbR6lx5(aGvPX7i*&~F6kR*GrWM?NK z+m)4-kZd6%`+51^&+mAi~=f6`-?psPo zNC1cmRL!#4+>j11JKHkv1hhGDeK_5iyuX~|o(L9IW?l4K)F}RomdTp)0i`p1iUPGF zaa~S=>-)YeEG*y}LlnhGbiIRHFikBNm6V)J9@};C+qSr5)GY7P)%|rL6eI9!|2@^E zfC>xa^JBx;9Jvpe5O9TEg)vc`v_%5am`=+|H}#_DCy%}m=t9AA`A;=)BG6(-6r0Vv zgLa=3EE8N7vKTq@JC#lj4mSy&U);g@^aB5fqXQd7_>$E5=qR(e*8hu8XIl>Sw1lx{ zo)bg>{7!D!|Awbt+l3!L4kLKFUMFqp9Shoju5l=C0*nZWtyK~Dj#`vt5qg0nfj?ya zleld`%6)MK}zRKN`l8KyIg%YQ_LnF z1igxtR2s~@rIxuxO1(MK+%VV2s}75mLKA7wO@i zp&8!+90W{)y%T?0#lt!Wup&%&IWj`x@WID!_&1cfv&L^gS6rAG=o@r=)-l-enC9gU z7L{5tJ<(yFSf)28CDnuDntI7^b~|t-P|>g*Am3h3bAfD8zp?S4ElZUZ`E4* zU0{mMSHH>VArTkyQQO~UB_td9aI`c49|?=&tMF%TE|Cqc&sxxsyNyXqDJc)`d^FE5 z4~hO%&+vnWkTsG<{iBH?k0_XcFMa>M5qx&d?>W|w^G)?XX{ zdQ@KEb-4ONSgPp0l|c~|hso5zpA-lFC!6#FQgdV^wI16)KP3^HKCV2vIImbNP!k#% z*>`QD-a*}WMhh;O%nWL6wd&s7*l=%nu)PoiP8{`zI|=Tc)m6P9*wyy;w|G=MHfc1P z6W+I+*#<|Wbw#Gd&%Z;9KAVl@y4cHuo80!A!C|xH;41>TdT`=`9x!fU!BR-5{&HrP zpP!$L%M}#Gpxvg31%pY?t(l{ApC6CC-@oG<1Y@HQ79C*4%Rw4bQSk?SC$Hg?+&F@y0+aJ+@#TF8}|fxGy=aTTn|%%Jnsrt@Ek8Xsf8$OY_N9?k4uRQt?XsF?;J z!5ZFl@$nygtQq9UvC*SHI+7d~YOiCE)Ry1WBs5vqpNWnRV~Sp2Eh0&dzVgl~374MW z0DH2b=din$M?NY0`QjzF?33;>)MEZ`sUHSKK#ULS#mv>!RX7ZO|2>L`h=4<{eclZO zvu$qkvY-f8QHg16Jbq5d0D|hUj+V2~bFlC&Dk^Gj7H21Qw6zuV*?M4NGVRkx080I! zi$D-&B{1CgHvIYa?OU+XF38V+-0(m^K)}c65Jddw2)Mu9X8UgnAQOxkxdjC!A1_!< z^PircA_&ZV-t%Gj?-#gnS_^?hcoR}m!0Vk1rwc{^Bfz&$Wqp1=#N}gSqsxzGBk$(f ziT92=!p?X@=g>tT&@T0}be)b{a2O!zGW z2}4YTTZdmf7dd^@b0wRSgfRqJ!(Ky>?0YNw^#`y9$iOdpA7)1Nt)qRxVeA>Qnk9WVXK*pQo* z&7|TX2bd(Rb;Vxy6upjc+YOe#dPT~d=wMekX!&^S2gl(~WpFU_95wW@Qz2 z92a-<8nfV5O!vR^R%B&meQ#J9+HP)bW0TFsZlI@UQEBMv>M9^m3#M)mG*@QYbO3j* z{i~9al1q#9;!(IZV8r;=ty^HvK!&T0iQXPf!nU@)4snrL`T1Ta2hOnMz}IJe)$7AX z6IlC1M&9@G+J|9N0HNO+(Wa`E)^#KFR%BZ`6|1cdf~t`>uvp#*r9G@^77HJIT+`w=#mylx1(Hb*F zwhMVpXWxtpeKMmS<0Dpwx`uxFzNP2ggy^s7UPt$Rw?Tp>9{CB=5B`%}Jj9=j>oi|f zUIr;;+oDUlMyNhczm zTt*=#sPBEN@?P`iq#*L?w@jK{>JU0ortmqV%t#ulQ0}$@g%nX+$5m3X_Im zB2@jtWba7*TlS7mTmO!cjG~%^GkxAK=yTLPe4(F}^PbqW=h4^OmQe=XX0>@HTk5j` zcm58azc`aJI(_!gTtVo>amEa9nQHB5A7zyjMuXXz@}W|Zs>LtrIMdjv~`IHzbmZ(1|se{wl6WQ+3p%iQ{!8| zdD+`Eaj`L!t`;)4u;Qa`E}FGEiTeo#^$?|%u(~KI-2nOXB+t}TG!i^fVWO0GvA5Vh z|DmTx$Jiu%YwOmLKLqu;tZ2@vOUld1$tfygP04?Q$UF&qS642xGo`{2P)G{#lVTuX z1@in}4v@DcMMVe#VkKscj291|!c+>J_Gc%DxmsC}S7Bi>4-@U8PWAXtj0L>3E!D}P98S-8|b3!{1B|rRu)6{gQjA|v(T{7Ymn%OjF*ht z-;l%$qb!(E*Bfv+8N*;lgUGL+u|*11&)@)s!&ZKs0S%!dEG%mH8tO8iw?u3}l%kW_ zL0ZM69fhF?0xFklXb%E)0;${Z5CQ|Llxvh@A=cwI-v8*p+7y=FJJJW8gl7}M+N*kNHDJqIU>#Q4VH~Ada`mB zI$|7NjvxiR)iwG2+)Ui5$s6z2B~^poq(&P>`kdl8IJl{#lr%K#ka9SzS`*pWpd-Nw z4C51ji!Z+%L2Bx-b#&5T!;0Ikj|(k2y?y%(34Zay7zr+6O;u*9tHEN?Xccs!^7u+H zD-%|;{qJJW-qU;Bl3Nkys*-&8==VEC70X`dGFNfPwzueLqV5sV(grPd;X(tknHU{? zevdxtUML-i7=@``c3SG`>qD2L*J=>wuYn*Ca&QX&GVtGgq`E?pJ)SD_Uocz*jTtzk zU%h(O?DMmr8*ys=|Dy$f0(=FGia%La?(gr<4q`{1xtx1S6O5`%`4b0Cx6aVjDt*U5 zh7G=5Y}+`m=@Z`lOj?O@DTW(yx6oL=eBkr%sGXZC#>!LH^!J|8wyNa*RC~Ac@mPw_ z;qjMuc(jbCHvb@NrOEd`d@5~(NLKOoZ?t4AWn%Pjh6Vm@N@+0<*5;6i*&w`Q4=~8r zD9QXn+SNtcrHz$jUjLIP!b-IHQJjNw=U{4XySbvQ?3yI6aDt+=RW5>jT4p%hI%8Q_ zcKZTaWi-EEUx#ZQ6V2LOaB&B&;|}m}Yf1+AKC9fB_9@KT{+%gmj5eN6$h9*3NiZij zx8w6?Xgcg%T{W^bq3jZL`c79GY+XIMd#1Alk&+YR<1Z-$l(*2B;EglAqdB1hfj(_1 zuV4H6`GJi%LDUzpoCjPaSH?j_#D)98*mgIP$40EE*G#SqCS?tBZjvO9|7rK6WT3Uo@10Y^q+YFW7)YQ_- z)-J9ZXDOm=efH-db0CE>uY^DkK`Knd-soL(mP={3a3dBEXZXG0J}od{ZJZWkg>5;E z<@M)h3j?kKD`C>rE}VPo_oQAZ_}`qI$@FdO_}CDeJ9!@sxgv0F%Y8b(G|x}+;ScuJfU(xUs)v8s2FMvkkWtHbKlN|9~?^A-7gcf_{(5 zyn3=qT~kxYywc5RlqjOWsbc5!6|ZUt(4i z+X8GPe3p&dmOO@yx@Ti-vQAFPZ>I`Vy?ZB(ZXlXQJt97wL0vd}HTwHgwM5R&FuDjn zS1n%a`_BoFNuhSUAsQ~beHirB^08D;`CMf8Vo&{VfKmD~YwPPl`0Qe3#gMLkKQ3$R z^~zoTLC5;uYB~FK$CS@pdzMCz^0il?=C`(klvF65K-micPK-tm?yzx0E9B0*eS{i| z3tU}(wui0I`KFGJ>XOC?z(oF`L;`42Qc@w)o* z8x%~371<$Ee;p$bIbNF1Q}O~%ovRLb{@Q1Z+)QqL zBOFA<_kyw1@uVg#;v2uYEXj>`Y#ci+li5)-rTadVxZlsCEBF8v6kb}#h8K2|F0{7C zk%Rp8JXc5lpV6m=JY~a@Wm}b7jmnKB)?-#3FXwsw2^=_%=a$~H@^-oNXLGp27d#Uw0cd!dM>vlOLI)k@1^+ z3=uibt5ouI)h>wu<0}Z6@Q2M$O&WbYJ*zM$o2vk+4Br|dX#+GQOGASh5-0N=k(WcY zQ0R`1jlrC4^4qsiA?w3DN~P7};v%SSC1Ls!9GaoThjIfuyviPxGdKf#5UOg^7GFKE z2T@XrQqJ$~?%jkJ|28oJ&1Yysg9t*$&R*8oXnc+iaf&eIdwg^hOD**J`YHo_uYUe?1M^~l zwD1zYD#u*SlFinnBhb~;%jT)6+D*GoKh^AwAo1vT6|lobAC8WWVuX!y(Qdrh;c+v3 zLHu&-k7i(Ct2CW-{S9pFVJU+ZmRe~#z}~_)ls%^*lDxxoX6loLn*~G1LE)b^ec$qE zo&1ulwX&rdgId?f>nFU&56^dYLtb2oGmK*uNe1I>!jfc}id#)mP!+-*1ubV0|q|2Q>#<(*j~e)Uhf3>nFB* zsp-XHuC0xe?Dm3sJf_1wA*b6H_}l!uUo1b!56DmMPvY=e`DgB&vkmv{OIoSHMX(;&D^O35 z&Y(~10S%m#*Bacxx^i+?z}Bp$X5&|$dQq+81VECW5XJxiZeYgfVY_7|THf;7Pp&#=T+fZ_?5r7rkP(Ok6@jM_U_WreW1cJ-7)* z)sQL!mG}c4o!qRfVE|HK4!w4j<>%relw}!UKwJ%Bm$Of`A-4%I!+PiGuEiZ@^>_ck zzuN1cEetsDdaaEDyH1A%*-bo62L$1==eD2%5|4H zC2`N~Pbn9-fwOZlH{YI@xj_{5tZxa7v2oqW_cRuycJco$9Vnd{Z+~gV?`O%+E(%d6 z#*`Traz&lqt>QX$7WMNt0GNwb(*NJqPEpaFY)#>y^HcbNzkmPkDcJ#S5j5>UCN-*b zz5xUX=qBq;5(10>FZ1~0O5QbI+tafj%ugoK3kfh}=J2Vh)05c@>O_hP3LwzuGM1xs zdQy*%4(m9hoo?SM6*ce@M`E|Ny3XOl{Pnd~U`SS0R(#?pDJXJG8E>!jsP`?<2vbhx_G+}yzgeYCWeHdiI(2M2%v;&(0bqu$QID#!iWc? z8`aj9Be!A8cb)8fIA%GU%^DFv~(RlY8t6P zb^b{cfc&*3?>eStJGeDH|3=BLP9z@;C0qYxc^Q@ARN6zIpq$ATKX#8ZH?4J0Q3!Dk|ohd|ZX}7sBaQbXINSqv^0>I$CX2(Xo>#71VJC%XgY@)tXBU?rskIh#D;}bc=?hDuOY`%;ugk2(QYM+y z6N^iTFf%WF(?0D<9Q|dz8%xPfwCZPcf_L1tvbJ`7a$$!?$X=HoAx59@Z%c+VC5Cg0 zNsPX2mPwaYuP$~xZ~MrfChmT==C8ioygWuT6r?(@z;!w~J$=}lG*5uhQlc9T2tD+I9BI7zv9mAPBfZ$I9mCCoR^)&$H+YtY`w=7ugG-U=r&D?xGIC{wQfAg zvjr&~Zv%JM6ebQ2;bn80M_YlYfbPH4&bx7UpFfWAs*bMOZCD(+V}FvoY6|wUsEzr2X^0N zhlLc(%*^Vh1gG3m($i;K%HX!Ko(3B-40K=s`e579Cw$$#hQM(0+c$5rrU93-t2AWO zFWcVRgLKzN^=urlDeuSOzk`F43h&yc(3AxFEGP5e&P6sT z=Pm~QM>P-OlHmxe6R&vMD&}>Fc6f!AF}Z_05~Z`QGk&cQGj5%4Yw!8Uh-#tp)5axB zl)TYr8=sUVf#1#^lcXZ7?v(=z6fUv;b%26DxZ7A|@iRj2p!5~%tAm2g%@2%>M1v@; z4wP1y^e1%WjVfy+&|wJjMr4EviBHxu(;dY@2(aR(>&>np1!{iNVVm=E`l_lS;h5BsYu&_wM#;0S5BbwO1-F=-}40nf_$C`^e*y+PMmVeQUU!Y2RWe(64%}j;I#I z@{$Q?di+$aPHhhhTdH2V-MrWl7bi$2(e~$*_T=r~c+kp{5^Lns zLg)C(*7p1{g0Qhg#KuBu)7qFN@@ck-q`aISI~@E&U4tS!Eqn4+tgI{p4V%A1-9w%l zLo-viNsgHR{Vj9-`prMHX$wm>%%m!KR9{!V#W*;8rV&?!D5#0Hwk(zFw^z~#$;jX$ zAs{1L0WPOFjes|mWcpC$iy=otT7OC34bhv`*4^3 zn>~V*V%{jx)^^a*=@h4V_@4IjHSX@0n8*qqd1#^L7b+eOGhOw6v;OYI zu<@MMoaD}CpI?j-^(c6%ufIE1+R0`x)!ChbAYCs7bankt;yR~{lY8>e;mFLchib^7MobQEi&yyfo1rgVful;4~r{$fN+taQNWH{fe3(ps6F+zPiuN6FB z8!3eP<21|#8nK7xqggUx)`hSoLc=cpdU=9#&`Jc96X2n37fnb)67G^!SuC!nHwGnm zOWLo)xuBFeFE_X6v9X)|El?|8mmte?7k>Bd9Tj|9SFVJ;2fqwmBzU2%qnx<5b{bck zWR5(b$JGe~xpV4A)gsNBfR&aeb~DfNI)?aE7){u1?P5#3``d$Gxr->dkt@h2%MVq! zmG^KEX}<)#HvR|R`S$?P{vgbLX-g?AvhXWoRHFS+du_9^Uey|FlitJM536EO@wdV| ze%^b&0`HtfDC+ls#={oN!aUvChLO5U{xHAwk$)cQhZDabHU;IJuD@j9Y34xy-u;;) zlM2()x33i%-LjR{aF14&0o@4AsxZ{REQZFGk1tUn7nmw929eZuX`v^zwbpu<_+df8 zEE~@sRY%!^k%qCc<~5-Z^!i4M7MyZJOfsB!kTqRLq`&=e3gsJ~X&>S5t??q0f5G;3PGj4ix^qLwp)1 z#mn{J&Vwew)IJhQvOD->+nIl4lapP$sH+WKyYTqPtZDwJar<7J>js>0`h32MIL)cY z#pK>o&`(PX3e}{%1h`urB)+kL1G}xY^&M7LVcMEap;O;+)BLM(T6z}3kRXubV`SNMk3L!Vaj9fad5^U?f;i{8UZIxMGoFH5 zfVcDGfG(-L`~bvNfmVq{-TDeLI{MX9wC8iEOkpB|08@$;PJ|$#ieT-Lll%9_z=i4U z+oLyGlH_C^uHgimYC-2kx5D6{4J`G_$~|4^@^)uj@9Z zQv%oZ$wPwo_iJUk)zOfR&9Q-g-QB-w++^Hd4%4o8;XUbI#-oXoVaa?eF--*2kkxou zrCB{E6LSHbWUH=PY`^8>XR4v?`Y6vot5DJ66B&m~@MOE7_gcD}x`u`Zl-i^Xy&9U=;PiXqmNK4FZCa=L z{hKx!C7b;to{QB&FrO+(BBqKOwUA{^6af$TWifAEXf$L9)L2+f#!3aKs8Q8d(62n* z-}fRXpLt8sl+2ddI?_&ipV5~};%!x+M16_!bZ4i{blqSI`C10`d$Il%Tb?7HNM8KA zHiJ5ChAb@HdHmS*&(iwz!XNw-IXTT+n#H5h(bxd3{ousfdkB^I zE?bDq@HI~}H^|+p1Pv#ud zCar%_&g@s$t(F$8#s;68^1f{D*l!t$lz$N&KX;Zt$JMrXlK&TTWWoR&pLTNVF5dlT zFV`>TE~fr$%x^j`ZO*EPZ+%U;3R01VHYZI@Irz^&M}}ZD0uu5d>>4bB;Tm*J!E}g( zA23;2fS31@Cozl6NDp1J8Z9Vf;e4Q&Gjd&7nTk|}l{8UT7pMEh>ZZHQ+{as76oD6K zkD)VE!yWo0{dDHz%XOLCSjX<45IA7SB_vaW>hG?vJBo@}aFcPy$Y*Kpfil74fIUk6 z&wnrQnsh#qWz5UV-_dbwu{&~p$8;qZ9r|yX;lkae@w{&w981$nQdfR|ucTd(MFLpf zrPnH}EVusIC{PVO}0(SsE*S$?f_p^RaZQ0DXFMf-gyFjk(QPgta>@Q#gSz4%NLt6(AE87TRl&#YsM^$ zNixbbi=$150YVCZ!1*c4D=Rns7t71bp}T|LIHq%aY^=rqNSl$UeKHx&qAVKdrN1n> zKZ3qpVSTeodef?U5{C1*)x$+|(_~mfq}?7rZa0Sv@g-0^=g%W!*f8z5L0G_{`ZZ4A9Qs zWT~&Uhv;C1VugPOIR;=i&d!?|O@#1j@ZVnSYH`nL!-Fl5aiE;7tgIv?T4);o=YMXl z7SkoGGD8zcD-#dc7s#*Z6R@uRlOw?J(la;z38aBdlQ0#)Fpk4nG5}0NsNd|MH<0e| zCbYM=XBNGg^*q{Lf`11TQPaSV*;xcBR;5)Aw#&uxF)U>0@78P8`FUkneGgsTj|?f4b%0cYdigZ(bc5@-j$3*Qb$nH zgtp3&l9D39Q&YyCLQ+x}E2vy9v(_)>+)#mn{zSgJx&H#Q zGG=Iv01IHddl!a}aj8Z3W*R(pc6Pwu=hay_q_G)WTSIgv3yQfN0-ymx}&uG6@b10{^l~Ypuxiq0LbAFevnoO5*(la&Q~%; zFaqKl=X6KGZ{NhEZ{E#%8rIWFd@%%-ff}jWune{v5CX@?zEDhxpL>i>!3qiZ3r?H3 z$S^Pfh^~PFd>HSf>8RkE0`|a35aLP3WU~o2KfQ%$P;G5@c$?n5A>&P@A|ty4cw{vZ zb~yA9ut8wif)Ec-GEfj+p*W|CQ(zE1t;cD(cdXaT6HZEitwU1xv4 z6ND#1euxZ%I2E4H&rc!nxxm|PXl%UoqlJ=#1TWIV+k30u{DC+Pq-D`a9uDSZZ)J!Mr?VhttGGUQHRwkbo?UhfUt)a2k}CZ-OHbXe)rogb)jTP0bI! z`wo77exTz42lhvfj<6>h)KaObsNjQxO@h!#NCbR&PWXVczN(4{iBC;c%0<11jkUQ2 zQwLOd_vr{AQ(DvzTP7Z|hnpH3A)XlWiLhki;T){_1RfoKx3^dN{YEAxd^|ir#Sh0E z6h`Fali$C?cosfBezvBekx_AWwuPf(T2j)phjXK&Nhv8Tea)4X5M#9j$2a`?@#6

|LYc6%CB^Y9@$j+e>YS6c$?C^Z!pM7A9qF@U3kt`k;D zg!i$orlzz;c8e5nhfHSSQ62NJSO6!6YY`J2%{P9)Hn)7tn%8LDZuH9+4C*z&1-)u+ zg_Mcc)z!fhJ4ZXax(+S5LqImXZG61E{s931Fhab7m!~H$A0NEaq7Mia#up)btyg)B zsS4Ym##R^LuE1N)*Qt0czrer(5HpYXrxv^8Ry`XxP@Z01a7PY1*JQWlaJQx0wnnO*aEY`C1|84?sBk#(i6qQX^Kxwq1)TOYs&8gTXljZ&$8 z`6~_j7_}uwTGX9rRpvf3*fp|@L@De>p@LOH{QO_-zD-R*jWea{DW9zg>Nuw-H%n%F zKEU`%HE=VYzHsm+B_%<=FPy_>aL`&-T3c27Qq#mi_Hhtgt?{?zKr>wVWXE#yE@ukc zw1SlmZ|Vzmov@ef(%I8htBycQcbjv6v3v|3VP!_ul7B{|hlQ5bGpz98_HB~Q*9N7i z8!VYgI!5r$hbFGH)Nz}OowT&1BswGD?6{U)or9e{N#M9|`-wPx!ksj}3piw}bl_se z3jKOjD*5$mPUD)Qni}96wP6s5k!X?{R)pwU8}7Ahk)SXIBgz`I+1}phPD?;cfp)3! z3veYDakmZ(4D|PJhMxSC{s&zA_D<1~l2aDJJ=U;>j;>e3Dkml(fkispN)9D2?5@3? zodyuIOvRCgW^ILB7F)v;zu<#|CK0B46z;F7zTwWUt@VOk4;33!XfPoI1rNuaJJMVQ z+Qq=0u>(qf=Z>)Zujj8{zb@-wf;$S-Zb!%a>p)h)2@iCJEjJmsI8))-LHssw;&9c# z0oK>si;agz8g&oWHq;J4;ar}|{rzPnC152D&lMFD<5ynC;iw|0?k2u}{|L_q%(aURB|(zc^ev%;YCi@I;Ozksi`>(qJ|aE z`6e7niTFFUCs%mP0^4TO*#>V2tbXL{Z2OTaz zVFKH>!l+t-)S~`|OguCkIf-rovar=K(A&qx^v)Gvi$OU!S7ig9Dv+^@i-jdUmjNd% zNbfIi9r!^2^}IADPZPvN+#oj$K06VQ-&rF4KubeGEhu;}LP`?aN=pWc1~}br-c0P) z$Qai(;4o@rWD>zl5B$To-rwv41-BWbXWI4zy@rGD7B0OX3XFqHOiaSU!u;yVGDkqy zF|AAD6QRp$TJMq$7qEY%Zqc%}tt}q4XidG!&c2M4wL%;XF7h#eWoNkkO3V4}k!;ul zz*Zd*zWeh^krA%4vNEUmzN{dki-e$0E_mUm8W?*$HhDm%6Kn_I-AMcE-=v7hebygKcipBLEL zYJM+%2R4+D`W<3@J-y2~40uS80<@S?VTGROriCzs(y^1O7#XE1dH%kYn3Y6-}T`7Lwl`dZV?wxAu z)9P2U3FrvXO%nZb?R4&tn*_uE7BF{y%8=k)YkH*95&0Jb86BIq9ry--$L6HY#8mWb zt%}WoQ>^2JVO0SL40am5T!Z-a^*$j+y^b zM5M=2xfL!m<|)Zh-<_&TO5Qg$El$-kic+gROn&vM&ehDyg_EA(j)T3uurSV#rdtwB zzjwYtf!Xpn%*0epS38HCa+=e$WIAe)2M;f_FQt^8A(??bsk%y$kZ`P^AO}yUMMaMU z4SC+_`s1Mtn#JfHh~92&*pZbjila?L5gc5>#>Q%FwB(n?L_Rj|RvK>qL^C4I%Nc8- zCycnawu|%qaw9QJqH7JO;QZI$7NgGKP{TlsUajq#a=u{c_tlRea9kB*;i^6oLpQj;}z zPyRh7Oerw%0OGG+y=r;o z%454%6%UFjXf@D~y-l`e{|4pp#Ia$`cbOVVrmvIbF^~_F)Im?Vxd|kmNdI=@wx1hs z7SAz!k-${*fna}g+S;1Y!TyuNy*npI#mbE4T`$I8x?h&6(cs??wpTFi3`0u$-bPv8 zFx;eBtjoZVdx@!ett8OZe)yniYI-)C|E?)It4uWyg&#{E{&i{+~rM@k%iL#0g`W2&|p2n@M zq9)+&6C3ef@K=$^a6n)zj*-2a_;?UoJ2IFP>m3M}7^38dFv#LO#79FPD*d z@m0}ALu0Tn<;$=pD-D;kYe0M5`C;4m2W}Y4!3nx37}wu8-p9dSa+amdtk0*G$`JFd z84S6?PnIW3us0Bf22CoTWPnEyj55f{g@M%NKeS5jgb*V^SxRe2F)ELfVX?PJYshEg zj&M*bBj@{#agIG4dSY~B^mw0=fJ{+S|NT&}@@&gV1=N&p1XQ5=XqH?Vk`lOvN9@rM zURkNsc1pzO_OrC@V(Z)4a=(R%v3G+|NkYQco~S=dV-zHM>B;ghoo*6~zP zQMXxQYN&<5An1A^jI~@zt^A|UA26a$#;5s}DK_ErLmUZy)q0MJg7SihyfsA0oNr1;-?C-+dz0&Qqns< zUN<{=-gN)_H*`d^woZS2j&?JVHK?w(`JI_r#dt;L?@u9buUvCyWsT-E`+YvWQ$v4O zX#}OEWioBuORRRkJ7#Muw1Al~MI}p9Qg3JKwf=LNNV)8Xt&D<#mNwN}xw^WHtgJ+c zv?7+8DK)w9wp8Wsj>umV6+0jRbl$8xGgD$LZfL*)s&Mm5%)T zH>a@`6zEy^P2PMU{cq*`kvR+-jfjA}W+;mq)#$*fh!`q*Xl|nJy)unBcxa%nfBuY3 zeHDl1^ms5G-pMkC`+3@r^cA0cT>fhSht14c9-6Y;{`c#2+wZma}@@x3tjt_5{S3;;_ zXOj;J9!;6!-;$i1B-a^D7(gyiiwC`HNpf<$a&lgN!eF}2`6^3YcsLd?c=IjI{Ge1I zqlh*!{=mbdytMQ$K7K004@L=LZDnbco`hKaf8tF9fd)@mIn%0yA!f1~b6_C+7N<3p zxFR19`G>%O-KpBDYLFb}M%`TJ<%LtQ)0~^t)|Rx|Zat1HBtu|#?~8fmfV}+vEcSxE?p(Xx8J{SCSiMfviEeP6ov$hAHHl0G}U77GBm`B zqpctpsj7}iv8?<#eA`&%hQ5YIv#(dK(+A>Y*2*fPhmCoo!@-@FA+@#K>^SeEpnbb+ zA@WXSO2Ey)j*|~yI)45Qhu>X5IcU6n<}$>=Auq(t!raTbaxv|C7IdOk=P2k=;AEm? z7WB0>;7{MA<$)9YpiRaZaXf+IcBfAUPXvZeFC4(9*%+tmact?UX}XAZ7AD};#o2(z7+SNIQ3Jd6OJ-M@bu9=JrN z++}5b>*ca_~gd7YFOs*f-uYI zbysibk&Da0_O_z3G7uI10*yxhJF@@EOp_I4rH#i@BdmbPep}s(qe)wzuDAW{wzI?mK@CCBFst zo0=Mapf4tEK&_aQ_V%e?8jV7uqrc*WcGO9 zm1xKtIk9j0s996H`ulh0vUg}f!Ct@#f+*Z%@#a-zDlW1Y7DY!KGNi6)KVs4LjBahM z%FR`9EWHD|UD{ZjL?sT6^pp9rmsCC4nxBT!?2eSMFW)9T5(5>L%D*xW0TP3KDmtdr zlg$ko_g>LRsi*VZhmXF5o&6cDy6_Y3KVK8-6FZ?)*mn@Xj}t<{Ja}`9z_$^lYP)JR zHTkhIm_c@J&_g?BHZn1h@;d-1xc}29Y42?)ePQjWfjEx)DZ~Qd-`c)GpRD}9kWmQT zfk=xVEuT=Ro&HoluvDyivI0}s@Wz0YQM;HptUb%Z1)w!(#{6Y5K=3(Z=Z6h4kx7?{ zk;b#ZHf*d=^Gd_LyMTh)#w*hiJb3Vcn@klD@g^Boyht4bgNv1g0OD&>Z9h1qBl)SQ zKn#kG(D(Ng}+j#soOQ&2cqTE0q5gwbzp00i^3p-JA_+~hFn1N347Uo=qRAZ3rTl%h8ZW`o5ld7T4`v_ zJ2^gnkYW}nbKwB+Ftv#LC28=06R?6O;2z@bO*{SS@7}5o45om&wl{+=|AGMo4C_t7 z03|@&(0rGhwYBEuVU&Bw%$0eK|xVj zX}Gbn@)1yAir9m#8%~ETm6>%sOG~5 z1c7;IQse`q;qeavAma@2-SB1rbM$|dB-;aE1Wmdp%tT|zT*al>o238D+2X-}9WOHG z`SXwNOW2u1eDk=4J zbXcKs>k8+8Z7aCgyhE zu(IlS)2|{InbVXKfhX@et8Ls{{vJZxLoUoc z^M!GIU=xOLu`X_6=-s|;tfpouAjP8cQ+K!Wxw$;1-BRz#%a_aj{q*AEoX&IS z57=KL$ib$H9dAchT$~&iSf*S}B(JmS(?O)4K4tUGEn($E=;-LkXC-_%+d6XSv92On zd3(QaDb=C$czB4No$rypdf22;S$*>p7RhnR?aMiV$IEj=q?bZBpOd}S+|p7uF?Xn+ z!Pp1Tt_)GH-nlu5Zd)2Hh6!1~l;5PL!jSL=%!R^yA`ry@aiK;=ra`<8cil0_q#{9f zDDuJi9}DclOJC*nU~lftpM;sWF$O&qPTTUKjppo0UeFE<3A_E?t` z7iZ7=0q*vKoNxG?=#QUjjXEsbu9=zBZ}}?MK*%>RFbk`$3G>OE1~!KYd>iO$0l9N> za_a5tGblHP!t4^xa-D-Su=8@D;6yGVvSS`78Q@6+0d@xVRI7NDTFg7E5XMIr7Z!k3 zh@+Kqadf04Bt-3}%u<{BF0X6>V>vQ9S_20@oKbL;L4-(dH@tC_w6vGfc?jHfb^Qac z1us-KunEk{;c{k>sF+wL_~_Bm!64iw!p>U4Wtyp ztflXUS74fNNmOFr5^P-P-cJB;laVTEWLf?a1*r@UnUXUlCHE->b)}w$(gcx{j=xE{ z4~kUx$ApiCF_(v={?-408woC22Vv0#sqfuCC&$YnrxJQ7d0brYr%xl9?zZ|0Je_SU z_$(WXc4c&P^Jss+>Z6W~ni}Y6$GWc0>eW_0)j@cTv~+ZWzH=keJIQP-ldt|g@!ZrO z0KU)vK2O}?;S2I36bdVI=);Gf6BBmS5^q1+Yq2~uxmHtq_vK3pK|xvv54*~b2Jea0 z-r1Ms{yh-TH}GIlect3*T=f2BMUmniHY-|*w@d_{!zZ!#K8svFtpfp;VNDuZ<8 zOoiA52M2n;!xCNHpN@`?r|VFwo;?@m6I03EP{4nGhS8~%Xk2-5-rDA`R8vQ~^;Ejk5pHThTF$vc3D_mO1=A0aQA)#BdYO1PKkRox43EVf}*YJ)*O8_jFTqGXs zuDes1HzGU02#O*$qGKLzI)L~m%8W*UGldVE%c$z!Ki6rzP&yz`eZ0It)?!>^PZ&xE zaK3Xi7_R_bFA2ON&~^Zs!yFq-ie~{3gZ%vSC+O!VG=OPA8v)%dB_(ApCWNF*=PFQB zQ^O8}={Qc4Ivi?Io&PiAs8Rtt?h<$h6QmVRKsWFIOo&#%a4papw?6yM*=3|zgPoK=;iUa6zbkq~lG=U2Ewe!Sz>%29S z*tZE{r9tBg4@nX04eD6*5CQ@Mb4Wsh$)e?D7NRI?{1Y@F(c#Qti&cQ385kyH1vSmz zBbc9kh5^(TkV|W;t7^b;sRP=AXq7BHy29SR=HRYP>zfDVnGQk#V z0jKZWAb*0z4l`hql9CVEN&Dv30geYD1^f}%3^;M%GF4So^%wf+$UJPVDuYc)BspOl1g|9(E;Y~I5A4KL2T{Lqcv$0rC(&AN~gnOQ^H znl2C6>ascVKjFozl)eQs7eBvoLqm5YgrMNBjSX8eGM7hhTiuTtr5QRD(!Rx$+1Pi` z(=yoFM4s-x$BSea@+j%gC_Wn!BQQU>o)-H3f&MLTP}^of{P25fGJMqU$hR?10wxcI znyjiyir2j-$JB>hPvGq`727FxW<2qU2&qviU}$-XE4j575Qx=DM2X6*-T!-kAA_su z6Ia6YPLoqc>>6>o?2y%$(a~x+;6Wy_>x&mp#Oj!|D;1vkTIGW<56*M2be5Ksh!<7( znCaX?4h9p;Uf~(oK^JT#GP9i)zr@EyNMX7|lu;oJP8QJ+&_gUBlt%6V9Tzw)b^Mxn zc<-@~zn>oielL(KH`K{TGg(l+j&lTyi|4AueI!71o4dkfPcxl`49GMX8^Wv{aFFhW zGQ*hHfA;skx(LH`kDdJwNTOhZg@S;s1@1>vqU4MWCVb3^TlCtsLEzsXDX!)IvZX}? zb4%s!to2a_Oo$=ld*cP9u0cj~uaX1E=grI#;^Rj)Y9Kil`Rs0qB&gNyL(n)dB4fp| zTOT0B6a)o)Gc&Ter7!lDmU7Sg!M;KbJP$h96>8;2UV>1JS6lJ&rHPJ@&pGJ0H0Yqz zL6rcXI_w`EFnjwwQyWG33Z{BcCrX5(NlQuL6B5F30!DPMaKhLSae!5%Onq*C9*L5M z2BqP130w5ejy=5SL{`|F@qD2iDLFs{H2rz`_^uC!#DEn@e^gMnGZFReIU>k#vGx1M zkE>3Begpn~YA~39dAK}nH?q?6)Pm2cV|ih3SeleGU(sqHhUq@Po-HG&-gzef%po*X z|7gdiY<9cILFHcGXm!ztKkjZdY4x5B;t1NeW3TSVWT#%k35KC?boP^FE-r2w7J_bn zpMs%)_q7Uxh`(TkGKG+A*C}Z+xo|gnkZQicOf44*f<8eIBBPXN5_;tiqKPfB&|H3s zW5mdiiu96+EnM`cQKLuD7Yqn`zFA(S^K|p`#C#K~BG>9#*fB?nf_U-5enX{iG0^HR z#R4~31-VF|5RgjIuU@4Jys%VIh;C|X0*yywTq7JYz^;XWNej$^!e#PdgnN1AD^j@y zvt&Ay3F&sH*b=+egW5CaIYj3ah z`B!8Zr>3WaFFRBf#kQzjE-e0bCG9}y2thdjM|dbN;{`&pdO3%ep+P9IB zPaPdAC3CI2%?$5E*DtKPk>5oWNKzpZJG+c2c*-h0=_eDXXp+OE6MRN}es%OlM{7S| zTkGst>Ff~JZ5VygiIqe5j6@_}DwjW&l(^0A$NQ)U(oPGApu#FPS;aR_x4orrA;$3u zC!2q><&H0PZJ-Fa(!$DbTcV2eE>98%!J@~V?Xa?Z{F3S3aE?eH4{s;p%cAj2Vbs9M zZgZBelL2 zn`69L1{+OSbd0Obd%%lv%APk%NzCidJ0Br)*94I4vvqtwTg0ba!N`v!J_U%qoNAf^IW(V+%qGW8ImDN?#I4WV;~Wmn=>$U ze9~=REfW#lA?i-7eTr2|5wFJFD3fMBw(-_A~s1q26EPH`+=%%tl z<)r=gC87HT^mTd_LH4pXBp@wctW4hcDDBL zs6o|9g8-f~z>R&tjvyi;qE~7b%m~3kc4cKHyxQHCm|k+;5J_O!wnWnXx@b5BXqX~b zlFE{j?!LY!^p@~|XLE_&oJpz1thr%3JEmDC@kscG&+RlgAgGdYH6D5;B>@c`9a7j#ZLbMg^uXOk zKB@jM*U)ZYkTP{a*`gl*8!+a9%sEZo1p0XY-E@XWvU*y{`?F4nFPop zs*kTaQFIjQ@*7w3qfQJEM&Id|es0;OLZ3~;O-nK6t?arPaZH_?^ z2>nPnj673TK7}II&5fI#{ndZCNvLsbpniwrFO&uVmEI`#?Wd~&?t~y>sDPzpWWWvy z>=K~jS(u*ohQ1I`u*}TN(GaI7=)MKTb~*>k;NT#bO9J>KSf;84$8t_lwFm z!1h38tehq24u8Pj2%8Haz;8$e)M#!^T*G7)ezPoLZ%&|U;6>oKf!oxbW-9L01z^gc zgQcOWg+n8lsd=C5q?K_&N%J};27I90{>!BTm)p|ZyhWhq3;jV=)sN5$gHsf&vVOc) zi`Q7-!V8&7SnYA-`V9D^*4*?7Jxk6o90P6)j7wl0V8Dl+9vZ{mczm(l?lyv^l|RCo=Q-kd_f9?Lv%qWZ_;|L{y@8e&5xwSuSmZmr0nOWdiv`)jx=Cr?7Q>Xu zg>;oFl>0wyR^$u?=c?&`OeQB}=!>{-vB33XOlo$sL8qX2v1`?{>ZPOeHYKIBEl@~(o-Op77-K)h1%b!GCvEue@PZZSIm}O=e*82O- z#XPLC;Xde3%NFtB{`ExIR^zxQA@gZMf3b3vDf=tDNBo6_iM4i%cuf={oI16=UR&cp z(J0PN4rD0g#^sT7h7pN0p8qjvhc8*{FHP)wI$O7|U!#IUH2n8u%;)F=1>^SZDR85Q z?#s3o3mko*shvQt2jtf5h3 zIgm~f@Ye(mZ~SjS`rcMh@bbH*nm-XS$+ch30)&2z`1WIyd-i%Y0zv`Tl4|c z=Caa#pD=25bF%@QD}4MhW@_yo;bZF8SO;4swJ%LgPg_`7z0c%;#}{@)C0J+x`@@OA z19vc(0Qd-ntCc{bdJS-E27-2I^Iu8-!@Ay$Lq)#zlJ8#$iD39ftfW)l*P*0q~llQsKKm7p|z{aDa8!N(Xsfpoex5u z8YWk-x3=)Iv&HXS4rW(m_{lTkiyF2x{``uv;x=o!HU6eAIaj#UMaZMM)g`~Ef@&7~F)~ILVFt7;<-nVq33}3{UnFYAHB^Wi|kgOgYSZsc> zl2$LS)2%DdKX2$u5ev9#{@kc1aqmwb+g0PsGcn)Jsn)EVx02e7U%yLp*lAhBlK*b` zM=sPUExA=~k-jzClQ3XrMhwoz&+=_Z22qh`8r?fTe#Am-%;`3|77j05aa}TOpDA;C z?C2aXj>6gqKAP5IbRr^#DP9kFGb)zz^@hZpmM9m1P&oA>3cczWi4vf?qkqd`kM-bh!%oA{00^!f_* z(IcZOqvK~K47RrGgO0~qirI5M2j&jO{!xUPW?ep;bC-I8?r*b%bqiL5Fz}@C=&jc} zBwmIQ@VP-%|Ld=d!@$jp!`PbdZrqBIy1C+gdnz6}q)8E4MX zLwMC$D!SM+A21zf3LK)G|8@EN;*rzoo>v&MvJxS3P)f?<3_~Gq%rLjpy+3VDp*Pzq zc(nnePIb%@^5@Uz-Pwm_ntXG7N!0}fqkp|fQnq#O@pG&1~`^DV>wePQ_ePWYnB{y$k+XFgpd|M`yasq8?s9JaJJ_@@8!GYTFU z6RK6=5WCiBzF*g`yO<~Y-#R+$!eiGKmc=b`ogSg39w6D=)o zZ-Hg{Z|NF1I1YN{k-PkpnN^2S;-KT0vpor;^+tM_jPxz_!S7M_!9ny1@tIi!pDZ;`wlp)+czBZP+?m9v zf{pa#IXJ!|qZHnGprxZXX=&LSkVSC+tD<6Iq?Eax-RcLEUp8Z9MA0+&72m%n^4LZ1 z&6_de(<&>U#fl~S`fiWsWC?lmIaODGDkYL}b<`*jrd?3tLEh+ z8=Nd9w{7|cx5)Vdyt`zabD!hIU@d9#I0!{=Snkp{Xi9dn`tjMh**3`~g>}^Xka5%P z*DvXl@zOQMC4RmX#)SCJHx&@G`ua74iOHO7EEcG-r|ppuYU%(G=s16PInBVK{HxEs5(eKOG{mh7+e>r)Tc z(vnjW9?E0urx*vaSa90ivXJfa)Nkwj&SBZleix@wx2A-eKK!`-E0s4&E3DmcB3 zE7O33UGvqYUF&r#GD6TJ@%(H$JdC(fckHv^pYvnS>YYDJzur-9qo8BO$IawNNI`M) z=+XD1P0Q0gj(^ARk%G<+nZ{?SZu7goWWs+N6SLnHi~VbvxXB|4hlCalAtq*%!dfT$ z##y`4o?WQwbtw8X4X_PwYzz#1pYW)m@lI)r&r>Yy?dd{~A@c0f$8}c9kF%7Hb{;=f zD%zdv(SD$Yft<|7m(5q@dThJgSiJq;=Qk&DP4+ZFt{}V&RBNEg5>t_sp3SSv5^18Q zq4~XMh?cYHabjOzFVsi(GJMUTjn(H;j|!ICqry>N$6g$T>dv_brj-{IOft0DHZ==Gn&KBKhjs`)AtEoX;Z ziblrk zOZ^}x_TRbu@A)wsAzgO|rYxn;jf`-KkIA8PffS^sdp)JX5*`n7uq-qLnV_6%QvCge zqL_=NC-maWl4XLlxT&c>&(0Jx@FQOqs1;E=ladRw6A5Z46#qy*-PZnO5~^xKCB53> ze8LR=P{JPNa%=@#H_Z?1&6g&PE_&29hzuh~wiuF8|R`9dDLthAelD zpcV7nx`mOOyJ}-2a+d-v==IT&U3&ymhiP&WLDyuR0j1Yx^GFQ_DWhUxbo4_Uva-Vi zTTILn`?`-!LU6UkCcYrB3;tHMA%cGYlH}vQNJ;-ECYF|_fpB(KxraJF9ye`2w_xOQ z%Bo)alDv3T8gFyB?+DLsvah3bzAuIieOkPzCfT3n2$jC9|>bG#v4^7zfl z!3C|;TuP*%^Lp80yy@vik1$HGC!r-ws4_GY@i7NC8O3`JmzNG@?|Woq{&vStL_N?R zwnjzZ6ROeSz6}ip@cDfYDbC){cc?+-%p|yKOpM(R`A8h*^=9TOCXY6xJUr@jm{}h` z9$A_SWC9H?DNfhmVCBNhhP_O7YPwd4|4D$qs6jsi3xBVX$ML^+ff#o0sA{TW*i0;I zhJv!8{gGD-mRMk1_c&e-q+cKcn}v38^yCyTuQ9lx7mZZLlIoH^WV(GVVCUM;#?~UY%`!>czj)Ya%{YK6?7=uPEC=K@arn89Pk> zNrX1S=#)C)Q>8%zoZ^wYvGD%O|ZCM8K1j{$UX=;^tVa0v7e*vT|1|0-b?jCA}^efQL9>&N=? zaKe_Dt6a-&0m-WIAc^&>@X5{;E-vhW|GbCPd=P;MI3gMX+$D*^Ei5e|E-D5K(KuYK z&1TMBzdc*e&s%p?$}r9z$-%8fmK%(_-%oom*&hxdpYy2Wl7mA!#~ z5C{cfVS}z{Bm{1l>1>No8T15*ZifsmH~1Z5BXrQw(!IPA>F5cC{jACE1u2!wDYtRB zP$Pc23LqFde)Vcs!G4o2Nad4`nG*Ga|79?e#J!;Y)yBiWAHP<$9kl(cl%!$#@+~ZB zxl^;vM|!?Kme0R&x+g)JbWliuhq=6<>^1&_uG`qF6?)S4Qz~c(!zTIia;S)->zvqD z_y0J|dZ|4oM#vR((rF+*d{oUA($>+GZuL&64QRQa6AM8KUO|D*A87eMYxM%l5H1;f zxIPVMp&I+M!JOb8S{>%O;Xf^Bk+D{pLS3x^E#jvQqhna0_Y)Tv-yOd$-h^=)xDC%A z_;I*{C#`>eB=--pD zJyS?bO2H#4x)&LzZ`&aLE+9Ei?}vuGPdZYpEFwrw3J0SU3!@Y@Xsr0DIp>Gz#xm7g z$mcaSmNYW2tcEJbOI)2*`1u(GJ;PZI3nV0XmzL&qKP`~KcnBrvi+E|+t&_`BdKy`+ zdM7qBcHpn&*M|6{F-MfL%-bWhjX!^x97%P2F7CF&@FZTJlh|feei;eHR->oFt-!b~ z9WGu^&rvc<5<{XqEj2o-T*BK0#24@cM2N34X_LefQPowez5ZS+*&>N#$`SdUI(=8a zyqFc5#Kzyah*V)lSI=I7-?h(Xfi^0?eYSs=2iI`CiMO>Rt<>{B*Kp#?7r->%S5@5A zwVLqu-U#Ivqd=t6(|1!5B*+tYcDCS9@Va_FSYOY%i{oh9#~7?8yWuB_cqi_QO$2`e zA$yxtc3=`8M&Iq-ucUHSrtJ%LNI!Ug8taZ%+%3qq3~grN;<6zpudQO$)vbYFMG!Nr z%2>TCmhbd1p;BMFXq8<^h%P6#xL|mzLVC1JT-<5VC>(VR{q8~aC+J$3SQTdqPLS$O zWxP&F*}3rcFd`?2QT22tMIc!Gn3(7io|;>>ZVMdzs;$?4$6oc<>%3H*RC9NKd2YF@ zK~diMz3hYAA%AhRJM@qp#QqTPJ4y6m`BA}Ae8iQ1x%cYIR4G>#M+EmlPYyOwILxEr->0Onws4Qj$6AierjU2r`uhP&r)g<9`)hksnbv! z$i$)rg`nRh>g=Q^C)c{V5~uEAVj5G*b|VwwyZ*`qo(wUT8ec;utXZZ^5E3E%j%M zPfH4E&F553kMgijpG;5p)MDn4VJjrSxeMa_qXXCK1_3Rn-S&jW+0y@E0V2>4&n-`s z%4ibo3ax^JJhlm6Oq6vVnmkUT#K(yU*;;QQR?@OoZ9n#3^;Si~CxtJ!|7Wjw~8h4xN z+~I!{h{5&l!T0pw0Lvq8V>trMFrx6t@1{o}#N$X)z==SCX<7{opJ4h_2ds)5Ep%G} zT6xaCrTlA*AznfPeylI-2@UA;@x=&{o2pm7{F~x?va-+`GbawC zW=`B&!rl=tVNvAuZT9g0r{}h%akMkNyxMndQ2*wOivs=jeL~A$OJ8e0y?7|31vn>s z%8buJlCuUbzq>djMCgD1@N=oXx}$@uwUyM)?x}8?8uQxBw7-qy*6yrAmx0~IcY&;5i&*kaQ&p4|#cVcP^j>rmI_B~O+J_d7L{lbznICw0Aew4f@zoI;OL*NPs^8$@@hU+h`4`$Zu^I>iOKgZ z$*rGXlSM_rq39N3uJL=bf5zEvJ+)?dsqQC6**7(rTwQMCC0kG|u^F*@{I28?pqi)> zBqSs{;2S=484(NLXEQ^q+2}p`?_*SaCTR;if`Zn+VR>a`bObO%v;|;oQ0q7kbexcY zK$>JO%n*bG1$lonI?yXu_V@N)U3!K$G@Q;gFw`tMTzKhPSisP3zsX}iGgFXN|HOJz zZQ?Nurx~p9@`~QQD|&}O&{C`&O5~~dkcp7go5`O1Zi~8eCykyy8H|!E^z&y1#V$MQ z>Xd~18wJ^79V{%wyqufD?x-fgX%>am;MHQor=LA|E&-xhU0gO}olEblL<_jCWs+HW z9y6C~K4Ud@#6qYSYq980dNCH#$YKE$q_|8$K8SsH-N&c5%D_gr$f`GyvG6TE`5?3o zcJ^4s;8dQnTV!i1XJf;S7@kt$<~imGl{ng@5paKvM`)>|`;?dGd!t)CvyMun!U}@j zK9!$7oS1Cjt(+V$@6<|99$akAum7y`OTS5C=ER=92zjn=uc|57z}aiKk{^1cI-Weze!&v1^{2`(3||XduXoloA1_n5*t7@t;;M>`4 zgANq*$GKg0hdLjg2g}h4x^1X@(jU%}jv|znl?D48*Mjs2eemjl#I*MIb`W3}j}Wui zEu;L#5ff|Ms{cp<=jN|pb77PM(-zxo!_%=`zEiv0Gs&_Oirat{tE-P$@>PZ?6r;ni z_vgQ~lmGVkpv_l?8a6?*MP4QGDPc)nw;bnLiiZl`tBIx+ffw}h{(28+`0C%a_}13U z1&mO~kfCeDm5xVFPY-PW5qteenM7GepKZ22C9nLcVwhA&8W)q*;5e4A+R|28_u$|N z12=caHeneNw_J2xbXm%+JB)H(rA3{CTr`iEg9y&3aY;QIkXRG>cp1<#M+;Ji%U z2pB)p{j^>KZsti4XqhE;JsZ8LUjKV;qIMhIVwL8$L&{Y*H*-Wu6=?e)1_&5~M@Pj5 ztl*r6ap#Wj{x86BP#Yq1@!i>usEW5kXq|IPdN+dzECM0=fjIL^IRzM*;)K&=m9XjgT;?L_i@{)Oj**qL9DzI+||*b*CC#{Sv&^nQDI zCVzl1<-0R-gIY=3_LQqB>jbumM`;6=o*Tmw$Hi@JZIG=60|uaRVN`HRCGrI{{Rju5|BaDNhRSdpj*TdJ+|+U2FLh30^P=j_6>KK}LAA7d(51lM|3pUK9O zf)T_Am{zg7jK2F?o2?fpB};~J2Vxx7oEutvj6G>lQUjMH8N^o0U$ zI>BItj{pF`ezuNq zQ_Dn02PPl<{QUh{LiI3M+K>c>1l(&u8*x8Oa&x?N3`~1KN2NN|11|<1<~r)?%R4)y zxVWQ0e)@E|0%8uP1pog21A^s-!q^R1GH~(#Fl`U(0|ZYGf=vt}nORrzp45L}b8Bz|3Jq<)k*5R7wyHIH`1mj7 z?AJ#YKu;+251yp*?j9OcP6Q!I_@43}#acLCc{k=l;;XW&-@HeD4IWk$KQ<)CNz^*HG*qvipY;l9TyDhjv`o*kRB=L7}jnT3)P!VUL7T{ zqS&}$k2f(wU@FoVa5JHV3HQZg^&;r?0$oeW?<`J>JCMrpgfR&h70_YG=>h!N1rl_@ zPmLq3A8`FXbIxN+$bbR-cG}*<+Ij<6uJ{OR2s)CuCtv6aYe7Rp15EJeK@<=WaFr?W zVz?|73^d?jVQaepH>}5Z@5aIf1Bzh$@-sopnDyA; zqusiNByMjHmX7c#L(n6DwcAjpz$cgGP5@WnX~`8;XGl!>PxQzU(3^aYb3YHyfH9I} z;dW#4_tHKdhc%!g?DtOef&M16$%d?!^M&(N;4livH9#C(ps<;#cgkrzFKcWR_Jl_GG+FmEFMZWKg7r4_2#K&%^(Goa7wlLrc`|B)qJ zOoTXft?k6t``mWKm+1-jm6Ge-#d>W6^>%Yk3`7VblQJ_ii-ygpuDV)=JSV@E_;NlZ zj?*L!-V&*f{dYh2mgyBx5f8NMAy4JoH_1#&P(N&~?uxuPF+f<4j5?mo#Z54Qb=5=D zg%5(Ext)sIOaO*NH_2Un;YLo`2maG5p-14iCr?or;WIvRr}RGqt@{1@1D6|-SM#2WiePXCC@XMrPw4pH zfHf;4J|@_oP1O4N`N66K?m3`AY~H5$Qcxz>xVp=I$WyEDeEUb)Q%-OKyIh|@_24b77i9HQ_R@57m% z9^zLsBdQGJVtM(2ZLXzYK|n{Qm8k995MCOR_;YVwvv?>|1RdiI{9ELrl2($hZ#~Uz z>c<>kxl6t}Sk!ot*{1#j3J8Fkf-<^<9^m?nTKxnBIUhgn85oEG++Q6q6yWP1pvU)k z>lOk83|MF=nqc~$?<_b@Lc$y=1wDB|zrW6wmPL$W%F@!eY^T3`c?VOp4L7(sj04VY zAy^GQ1G)^%j%JqqkIH#~*mzobux^s&dEpDgB!e?0fIg@+YT4S|jq#}z9c}BIi#?At zA&QX!O$xm6>O~5wWI=O`sK`ifKY`_1X}Ut9UM>hN*w~c&DvqcOC)C-lEvSyS;;0H; zTpZT>Z5W)5ZYC;MMxuF@w|U@uOv9oI_yC9lOKnxDv*Gr#&7{W1jL)k9vk_QqVCVv* z5y|BlgDttk4CgQ}N{QuQ^BF^|HS#e9efjSLR4AZeB+3n9vv3-1>iUmum_5|bPt zf(A_A&sY%=y%TA)v%&&(Gpli<*M|7UTZ#azt%cA5%j@IyPjL;O;w?zq+0n9cM?}&T z;B+w3Zn7l1*tZKQeMSDMdi1iF9bhz6#>tooO;PYMg4L3To4cIM$=w|aAUM-(c@?Tf zvMSy~g#-v5=q(~4ypc<^5B1N8p+_tw`Va7rpGA@r(52o)^IO?2Mr zCA*MzXyR> zD|x9@Z0DwyqF?HrbrD2$)nx+{zhy6Qc+%8NlB50R(hM6o8d|OBGCb*lll8YWz@hTm zrl;ph;HUg*JKZx^Gt-r%B-A(mn052A2N;2#68ll{9tiPOSI(@39elk&8fZ?<@&?#5 zFRwmpRFE0<_Z--VnF@9F-o%NDl$FJ*Pz-Z4LI?x$`L%?QaklXqQn4x|a{l95x+z%hxpC9$Yfp2~d z85zlcSth_w_(?9z{=bLZA_xeGu?O#dfNnH2#`$a;mbk%oO#>M26dkG2TYjFAitOjIAWjArT;9>~)8jNKblWgtnqp9`Mzsf#PLwXL|u5sWc z&{2=#FG;5NkbXks`ly^P_^gI1VPIoJ`k^)hKA^#X^w;?;iq^tifV%LZjs5`jLv@{p z3oL$Sor?TE{N=uj+K(=+&Woi=vwIw8oR5XQ)iuX)!@H~Id zooo(&Tnv&Z;j(_iV-$)ts4dW_7Sz>9tKG=-DnV;*+)cPb2k-It-jtZW49$8JX1Y|Mf@#ljPj zEtuneXD@&EE~szyz^nP-l#34@v|}rTkBcf}QjW1??ZK&)zAmwU^}F#{xyd9ZV1k0B zI2~q_74kn;l#km{uo<)V_L!unx$N#RxScQl7+9krOn~I!8Yi~H{df_d0v@yz|4L)4 z#m`?!g>@jes(-KuX1St1=FXQs!SV-V-*TUXKjb^G8Yr{>p%n0k((}SA%I?R3d(V2? z|8q93UzZp|q>7DbXwXQD=Jv3!g?68Y6vmxp_%$t*_jH9 zA47986=aDOFp!Isczh4TAv8#!cT3DNT3SORAc`>NsB08=w&}Emw8Z!Hptr9_Nh#)8 z$;8A8hZ!>#_CCzV2m10IXF5{kWE!D|F-lK1$`1B zu1jojWPOK=YiB*Dj|l767o>I!yneVmZZ8t}d{hba0=xkz2CWu65pgfeZoTeotvLoE zAszt%OfyH66T~Uwdd%k=CoG5i2L_-h)_d9)rV}6g2@p zzLc~yAfu>{HBga*eOj$x(DLB}pY`5t;L}=OtShA{)Pe33gr4tB z&%}}&jP)@YcSTD(^lc7yIToXdAGlwhDn9(-G~0lVASKHh9@eX=@pZD+U{tz?kTR7F z*_x}V(IAPjv~dV6ixVmbV_sekRk9bCGrz4lp^K65>6yXY9nFbKvRTA)$L^buvXF1c z69h@}(k`kT8SN9C_u$n?63WkEe;rKaFay6h5ROQDyny`mjSXusrkk*v0&o{nhDIKP zc_!TC-G;Eh6CgC!e8=JSipDmc%*_Dl_$C6oT%1`=%35LCAdF;Fm+?L zz6zo*AR9q|j+V9r=T%e`3IfR533}u$N1~0u-Fme{!BS<3!Z1tBQf`ug#l;*cYET*5 z-Y$i-pUbcL5ZjF$ygTbgo`aWD@2zun_R7^&W$-#Uh1EvmKEKiB8aCo@h5nC&-G3Wx zI5afEqF2~ySP^$0@;;R#6V?%Sw=I-^Y-tn8qHpQ9P53GBnq>D8W6}ptwyMDE^xth& zh@dxQg#iI|CMMx_leZtXY#eX@HlJQ<|1KdhiH<`=O;sy<_bK3Q)r&#`kBgM~nml8t ztBkP`{W;f0^noEkG_s#1sivoh0xza0MYBKJX+NI1^JPdGWFNcnG;>Kz52s&54RlVR zGvUUALvfa1t$a#OEteHPw0q6EUJnnJNK&o-lxUBOljq{XLY(X@rKOwto+OTLNq8gXrN!5Z}H`0V(Ocn zeV7$hIguwF1;;t~GECGEVD*m}z-$sa@Zb#s)li-a1900ALjy(@3?mBQg#a%Pg32U$ z3t*jnd>Rc{Pft(zczG>9>OgaF!v&t(!0SuTf>0YY3XVGMmQtv^j2m26ffR*ptQ%9< zygLpw=tLwWAW>?3_wG5IIRFxeFm}K-fmq+3Z{q9yIt{8fFd#SsrW^iq-zuE1sYjZp zixe~$Ny^(XB>Tq58rXk*-XbDN613X8pa|3<^#^lr|8po)B|;-eMQav^hll4|+)Yfr zR%XqDE#VJ`sh4lqSRM=bZHWH{bF9T?f$Q06M~V4OZaoJ(REeKn5AVh@rLfYqM)Ps< zioVz?fJhPJKvBE`+KdeS=nJ3mAv-M!!Fwb`Q!E_&t6|+Q`n1aFoi73xqjhUdrz4lU z8a?j3m8X3~{SD8kRXE@P3$wbEq9QMq@Ie&F;|v?!HlbM{5OBetBo6>FJX9;t;eubj z?>ReM2m(Sv4>(G|gAzgs;&=!|5r%X_tah*yF|n|ifO6XXvM?f|3uLg+ncCUj_JyYr z3S)400L$zssXVAp>Y$i}*U*KQYjE&aSS8_sf&vcY$DmTK_Bq~y0$yK7hnAk+ak_dH z!U;7Od4B<;OcXwGOt1-7o=~EPX{c9cf8&Ncz5`4l zG#iVd2m|R7xKe|9sow6#AlUsn&b5>c={Xi!da6P1P4duR%{}oFmKGO~jMdoN{(Ini zS}FI;PRQ3oT1IAJz6ndTZFgtd(TTzgSP_9ARZ&K5TCb$g=;?W(9@A+|1QtqReMw2F z%wy}kAiU}GPuGUUM)xxqp)58vT`5HBqcv@aoSmqWy5E1?A8 zklC`V$;fzR@E%0mO+&;uVZ9$*?XxdU zr&RRvpP#{BhjUGnj@w`s3^RDlJCBo-iMF^R)R_oNM7c5FEJLTPys{GF>xjbly_z9i z;iFDDYr*U9zd*djm{g`FO{pQTz9JAI$>-K!4T>Vc)bFNH7K#O48PMW_oM_)mI80Ji zO$`|Vrbv)C0L2%iX;R074JBCj{SG$0<YM@&rYzB}W- z)}Mwd0j)RiU;jPz5rvEp1Y=OO1LOo&Cf;D84W~wI3=G{CpIRS=p@^dotT8rf5+b>e zzx`;7s>-?EGZYf^I9cBJ;#%)Fd%^$jWRH=Fg^QPW3wPLkCR(>_86ArRju4hg8O$kt zLTBd#`hT*@FnNT0c3w8m&HW|}cb>}p`XnNToQqXV%xQL#MT4=zcW8l!=Pg&Ua<-t$ z+-R@uSI?->#?vntdC#8LQ)rh961!ad$%P!hzE2gWby-csXlOBIFCV67HlDrJsHMf- zZ2H{_ttOIlHRx7*z+(%IMsRrrS5#;d0_RWFfVrs%w(cM~H_+1qZ-_>hWtk*Havm^q z7lP*>cK4gcD<$P~P!d2XEb4tgo27IY17jMFyU@LWL#-@l+75(~S`LtypTje<4TlqG ztbmERnS+A}^ufCAM9xZuLFS0hr~&%LDFNUNdh&D5bF&sTaQP_l4XRV-ajG4 zBA#(pnoCa5?G?EAT00qMzdbPjj!C1f9i^0opj_Qh)s?PnB>g$JdUk|E|x1j=ZoYzw`VD5GB>rz5)cT~vK`MUyPB~$jLDyjF0 zZGNe2BBkRq_6W9%?7aV4Mhjg9({i((-NHaCBS-_?Hg9pJw=6%Ii8Irrd} zs=R7e^2WgdvQ1-sbpjtBm;^IVPlWhdKv^&ni5whjVWj^(%+dNU8_#JVGcF!6Vz|y> z`MkMbfe`^L$8BU=+irVaN2jXKzA2>|k_jg`>WrZ9^z~_77iLUJFy~a$B6Ob@q27Qw(lad66gt7{OgRVWHr)S#Y`$I?PPrbu(l@Uw9SR|Q{7XWHM zjQLoJVe#MEvK7o)02vBMn_zZ9{UrCC1CFrWNHtXI0nqQ)VV1f3+%Q}h;_B+$WCIVp z^bZ;^|Az$tf8LjvBiD!^UK3CnL#7DiG-_$Vq!tX!)Jn&pMGTcHV-moup{OcuAd{9l zU`C#_ z^^3L!{@-3gCs9F#%+lVT6!N^TcIFzmIMMBD=CDXOPWg6vH-B!p9S%NOVJS1ZIt(9h z5|W9NTfs;uQb^@qz0A!WW}r;y`Ly=3KOHYmmB4$G z>n~qP){(_koQK~1%yzGu`pMp-*X+7j5e!X|A6X7?qz1)TX)k#B*SEDDM>CpdQ@(=B zmZwm-rOAMM#~-YW&OsuliLP*CFVzuQ0!C9u&>F3r<&5nbljAQOd4kWZ=@}2XprFyo zA1a$DSxD>jp!xT%X&GeJ*gWsg=O6|HTmsm~fgG{4tV`K|0jr zn_^*tgwULx4te!zri{bv;P>LS+1%&3)YJ>~yX7dzM)vb%_o8;uv9}t{&?6NF0q%xn z03bd`E(^>w8G!nR>=v4V$8Vl%$$&<>&ztAEdMDSrd`YICm#Zy!=S8jaw*rL-2v!W6 zyZ{Pwi{vXSr$s@eL{;fqxdC*3aVGiX3369%V|xEO-_nm2IQiyL_wLRA-s~Y{sO`2p>CEs|VEvD0)X>bsc|9@gjPMFf-DJ2Y@j%uigi@vVA^sAKp`I1& zNa?Aql)TQoH<%-~SorJ#NlcLjqbf_rz>UohXtd!!$GToZFQ??F3G5!$T^pW+V@1As zczEKnlc$_{_*JbZN&+zjNrg;&oep}Kl5Hu z`C*vZ^X6#D!SS0iUQX}eA)kDoQA^y&T&2_Euc@hn`eer!6gf~ulvP(J;$qMzQ7aaM z7s8=O@lP3-9RB#@NeqdUpJnjQBEEFAx4ZWfApirdn{Uc5`_>f;5i`QseSC|EAou6* zA|qsVO-;YHM@%9iZFB&i`*6cSlNB8irekonl{7MYPe~*uPeW4f)#FH1{rY5{O)1s~ z>&~||EtBY5szn3r|7=%X{BC_eEO+OFv&rgbmx(GP)zjT&#T-YE1M~Ps3E}^b%%HAs zzmwr$_UiCv$CqI28T73%`CG8wMXr%>eKE&nCajci53yxp>fy3XiEr!xoU1=TFkpy)-jJTn>J#5X58%dke2L=?GSy`%!HuJwoSlp22{${2Wr4_hMx zD=RM#Pa$tl^SNkrJ-x7CEIL9h_Z~uidATZbq))%P|7-da&pb96DLxL)>%pwtXAf*t z;uO93JtxaOyS%)65R~jHwkE;;*DXDOJA)*G2+OqV&D*m>MOD$#|C>j=TvK6O`gY4B zmK>K{`3Dsd$uH{rPzC#*b3YhBW7N20!;MElE_nLX3GuTNJGtzl`+lXkc;E^1k0~M) zL|`Cj3wUSi4?Xsn9ggxqApyA(FdKj^3eJFI65{8>V0#dtK zEfsjo#>hZS3aZ3AMPpC`myZ4{JNhh4e)qdKWd<3evb;R>MZx|A8wUr3wa^WOkf?vq zM+J$XiWh_6O<*HZ6i60UV+{D;29*Td%LJ-FYoUxJl?%1295 zKmK@qfTi--+#pim&$c(mKS@AUt#uz7clTSTA&K0qmdwnMqS{FNYf|#0){cCvXoNpA zdNBY6(fWxC(fz(GZgqZq_TgDUK(i6wD|cujJyK4FxI!7J&(RQYaO*BPJLE+R3zuJ9 z21~uw)+)*p-W2q(ziRQdh>S!$pD4V8&^bG6Y4GM+o#L4N`5Z-E9Z}NX4Kk?jzF$Lz zhIg+K|4xX>1v^B%J`|YAW~uGdfWe2KC^^A*5@DU_3*8)H4kCdA5sa$pw|lRJ64?-gBLAdy#;#I(DRf~^+V6G6jPUmt3Td} zUdCSU%&SGL-6@3#u6FjC;xH8kxH%N)@h&(17jJ(VR`vRY|DqEK5fBtbkP;O^CMn&e z64Kou-5@Qkps?ufl5UWgA`K#4(hULv0s_*~d*E8X|K8Vi_H|yI7iV4Tg=+~jzu)H> z&pqz(xgT-_$Um+uD?9rA5%vF^vJXoZoSUvu`SZsP=BqG7sDDl6dYyI)xZB&#iP?NW zgx|aya3!_wsc6v%r?VyU|37}xo7F3Wj}F>G#C)*=9uW?rEK!5LnVA_1F~}I43RATb zJ=;oGwz(-S=nTY3`*lVY4u{b(WS#!@4am*a){#&_N*cCnV!sEI#McYcEy2vpyh#M2 zv7+9X%UUUJ516u&S(x{4Hrz1DxA1Voj+xd#hH_8w%u3fpmHyeXgal;LNNBwRu ze3omgV}IXpidE17g04W4ZO zzHfNqOeUB{!m+0CYpOI&>u|FM?wT-h8rq73O(G1;yFk4jW;Wipxas#cC@4SgpEBbh zmd72xXWd6tgH+Xq`qNgte!neGPXrT!HS|m#Q7fxs;zuZjPXY2c z-KX*ht9460>?YDDs%C>m0PARLB37bEca;b2!dfthPS{evl zR|zPgcmQmZycYDrdwtLNoYovctAMV-=6IR!q-d{-tjzE+&>n%#0cwaoiP-ffobrrZ zIHh5!-12}_IA~8LRyFeM88%5UhNvdGm32nRJ5_;Q2CzLSlO(TBn1+W$FDNF5XZHQV zR$t>gNFmwe4p`@$>a8W2??3t2z%+9aaVY$h)YbM>{6$U#p;^cJ!#4X@gosgwG|Sc@ zJu5~6ML=`r-z;C&3VQb7$<_Q}R^MW)^{a2f%M6!XU60yB%Tkhgy$|kkR0{oUA=tNf zJ!tUkEL06RJ$$ZY{@FyH^!_j^A2a6EKn(UmThLHC5%k`z`%P482sTo?2Y= z$7c`x!*8Tj&05Hq=st5;=8ldLI2Hl7vjDZ_!b;&esSD z-`Vr@4&2rGzUJ6Ek3K9q`j^ ze{Yp_y?y^4e+O4WQ`f3Ouay-HWONZTd`#o)jc9);7N)8wMJE5{%bw+!Dua&2;cBh{ z7net8q{VJ{(ZR-bKIiC*gQN`~%mbQR7#JkC2$2kGPt35x+HtR6XG9_)PwH>i5=d?k zC)T~f&C5GEF#$~{5Yx20o`+K>L`4~%?d8P=K#T06%Cm z*Knp`3JG>`?T{r0g55m%ldi`@SDFSG^#m^TSYc}bDKSJYR_+GIh)R)r*yc)q3P4|} z5Xn65Hv0uJavrP~#P4O1BV5xtRC{}0HJ^y1`J9fuBpH=Zy9b^>Fg!qM5AZ6jp{sk( z6_j(X;JxO)?=i|Fa^r`w3f)Kna^yF8kH+@x4^8V4!Npop=k z#3cSL`7ZKx8p_JikQdQOgUwJPuTL^QX#QB@`Hdgf@*~Z&myoX$-0dzFkFG2Fr}FNz zNA0@p%1pg^<9D#5Sw;!3=c--xw3@DKb_DJ3BM-r}xsF@9&xjNm#jgBL_+738(XIVK zr0*L<3+~Sg-OLee6j@U;S>Ljq3S82pc@btK7-`Jd zx3eB#`&3>pp2a|5WvT>aK+Ig_o92I%^f)dsnRgAe#r^STel0nft`3@}zyn?K_Oo0y z)uUeAxQzA`O@}=dL&R}o5|A44hmq9q7L_$M3&z$p7-FGBgM`uUdb4NrH))1qG%DZ> zzy{TZh$CbgEq)DHu74EAG^1Iko|+^@g2#Smy``teG;t#KvsX$5B&nwGG2}9bG7%!3LB^G9o@PPIy!xT#(Ex`<;p}&OlhHBP)dH8L}e6fVJgxIaF4Ti zd&$Hxjh1LQE#tYYDo#yn=hX5E>ie8RT*fR$wlzI-Sed@>?*nUR6PoUk5yzz$W&7>R zKUf-%+RhKGIn(6u$Z}GjJT+vkIy%`Q$TOUHk<9UcJfJsJHfqJ{kFQ*&vWxseh3EtKnS zG$y8Y*?8MfRA0IC+1^T5B8TXEpM&PB$TE^fJ70*Y(GkOPm?&SPA>g<3@jhmE>9~PP zOQRD?iwLKxbBl3Uct)2rvzP1Gf;BHEG$ETvO@PbxwSCItPx|im;-k@?m9*%u=X*-I zd5?SdR?8h$URRj%VPia#4$kRBPYXXe)L7};u5lc=ix;mu7u>LeB-%JTOnEPAFi~Ex zV9?etG-ZJrKb+$-WK_NU-d$BSNwQcIdcYp_l0+50W-f{1yu6Pn)IP*1jyeulpy2&O z9)y`3%0(nDfi2C%zBrxXF#=Kh;X}O8c^)c=8e2vGKEhG2ZhUcnEKLDR%W_^cKW;4Z z=kJXw6-`Fx-ygoOmm1^a%k0gPu|7NNq_y8AM=>$|nXW#7%{4PK3Hz=Lje3RHZHnUb zQT4!ozBz3%NlM&c(jIsG=MFKgz2n!#g#vYka&K>IH(rOO7gMGrS@`66=_Q9za$i?h zd;i3gn}&TT3T`l#;z0!#DZdDVZ9N6aRT>;v$%uzQ|A6+|-CbzC4!&?gEjuW$k zT(7s~QB%H{$r_Dhr2dnAffQNWW~+aO1C`QY)(XNhn6+O{P5Q~ElJ7NN9iDZ!9J>Q} zL61M*@|5k#$*0{9@zdD+txd%_Qf&8k+0tkoyQ0-{*y@i6$6mFgPBbfdKzCk^+5$3cOg4a&B( zY@N<`q{jCRx@=~@?z=^V$(=kgJQWYka@ILr2%2v3zbCM9e%7n2ixB2G zq~F;kI9N55J)8csjri@ud6@K2`9*su&|;59Mk;B1m0b?KDaO-%DG>z)iJQFkTa>jq zew=3CGCKxGV}FjyxNI932IiUOPN)B9m|@$8764^JpN?M8$tJaqmbGTCx0ex|IW;w!)P(sXYtTmmi$SbPW{&lElZ(SGH}^3>9eJL6)HVsP zsOe$8IZ5JLH)f3}Wo~ay-|0&!s&{wd;5b`Y%>Ow-$4`#PI^0SczCIg#%lpq;YDs8n zpooaNGBRi!L>K<7h6y5Ty(kdz9u2a{UcpyQY`lp$inboRGHsu%5lv-PW6`m*TD6~O z*eWjCLJ(BlUA&ZJmdb^$B_&q^vS=X9s7Ql1KYGmxxwCDDL!d@PZ23a!u1ACCD;9)& z?-?GxKb%`H=k2azzr6OI+04P4@o=*Yc6!Z-LJmcx3B(Oj3$vYs>S{d1@9ig%)Mo7L zf@!}GICVcaaU^`$MzwTxk!Uh5FB)bgc4J=o(%z1NaCPl5CCd7wu1gseD9=F}+;YE{ zc&BK0e{iAvCpDH^HPfyQ-7@#N!q^i$i6)jvj3$ZKrw$b8!CyU|wl>PFQ5Q!DO<6s} z-s$NL>_>LTpv9jg8@!1iLF~7pk%UwGp>=xk^C7V=0#ftks3ok&N+|sF!C^6+24;L{ zv@`M%&CQ$@-=Z~>UJkhV5NR{lH(nEiqgdOMxCHq+zU}C5cC@NwgGm$|H_1!Jh9<0n zWy5A0(@1CZVxot?B~;7G`Z{kcRDVVY_d;g!a@P0K#yo7)$wKSft$K07XWQ>_`Atm` z@;_Egt>(_n#!j~4v}+vKd-m|N_{aXw=iREsn;zpqu2b})aM{XBh4y~`gPFMdW6p^ISyKH_8ySLIG zuP`)ylb`jj^$2pnvecZ<#m4P4N?R5kK}Kel4U^TMA^PbKY%2GIzA>#4(85gvsVl`&4 zKNlz|NtJ5b1qEl}M;jZbod0}?pu)pKuoR5!B$LB3ekZyUw>ZN`(6!vqD9iZ^#`6k% z_Jo&f%HnvwOWjG7HpZQ8emAfs@K+S#qWs+RhufAS`6=(;<~(Uyx$cX01x+8nS8p2E z^}I}DPaIQP5MP|2BlXP3p!}?wpAx1<>$_5O%17`es@&id$TH5xJk(l;HjzcS}h=1Ig2m=tp zv@utNRqS^HA|7t@&i(qFewX>K$~6lCUui7FK320E(04x@JURF*h2B3=&C8*pr}T8; zvqFAWP4=jfkvgTZvOu>+Nb0qiKu)*HsCt3zlP#6rjk71Fp)E7DzX0$_PEl;(KpY@} zkVl#;<#y%D#-KNC&X9y)d3fC#QoHsZ!|A0_787If4TNmkaza(3`a4n4{H%$oMJ}7$ z3dH+9x*b72y&uMiBLxcx^QT@3FVG+!Bh71SZ~9O%b79GJ81dIIYkK*=q~^ET_a;XP z&c25>9_Yo~p;~R3!#Fb*s2WU>f`UH-(cx>6goy9F-0~EPb}6COwn8r##cIF5bdv0B z&dXa)y=!R_{k0s0iLm(K$!Mjeh4`kb8i^s2qN=2ekFet73zjwgeME!eg@KY&v0yVB z8a)~}0iZLf7iMqF>5Up?sdtA7O;YfDnfp{c&5xBQshm4Ft!?6uA12u0>MBh@ptMM# zh}JYR@^vY)y86O#I;*D2&0Q;LZ?~nrJw9r;AG7f@@lJ-kNvYN7uFAykzE#>-G7OP$ zV(qD``Bt1{2<1B??<`P>iKqfh({3TJx|ToN(r>BUFT4;R_Ybckd__f2b$rg7()*Iu zL-cuRTK%aBd@icFIfCO20^jQo5g+z;j6c=agNj=s;D+$K`(pss)vfIeepnU6ipAMg!_xtL`w7q9Ub?{O`5GV;b~Pq4Vz-dr>prn=Oj(@Ohmo$kdgVmUSXku+VAb zVlRIC&M-HQJ8xS36IPWz@bcHVx6?aD`po{+jc;X{&8!lbZzQ9BJQVvZov^@dpSAW& z1A1YO4xMhKwi-*ZY+{+E=f}+02knR^=WzpKbCr zBxx;g&Oim;P*`{veqVnmN1^urtER#2vQ>UL@Zx4Z$z9v4g*_-H|9xMgZM0aj@oe04XrvNzO6c1r~VCHgaeSG#R*jmsYteJVXxM0985&KbPlH+?&XIBcMBcL zGra3>C^~2>fE(>Jf}9O-N?NDl{QEWR5V}90mxZAjI4oypWg!qry$5@HJ)NEUtO;@t z82@`~OS{i}l2U54fsbbFYXat6%!DYqoEAU*Z+vFbchCBk3m!^9tFQes3G zt!uqRMEOFh|wfz74`D*xiumoC~ z#T6G@zkdBXs6Y7M7k1j7?v?T1m+Ej8GyngWKjywylJ7JskBlUIFZ%a#fhp<2+FCw8 z{bgEZHMNzcC3ALCeE*>m9X?1eS*F=Oo^MHDofw;+U*vQAc>WkQKvHh#f7Bl~Riy{( zYp=|)`QUn)o;RxAGbttV3;ek|F6a?qR7^X-${jYz5Bn%SNsDRKR35{DySTsqa+1n= z(h+8e+tL7?&EdN91H3Y|nvwHIb0Opr$qyL*E+fu7ZR-S!Y_g9cdZ{R>j&^>BAFutClO{nCuQVZcR7zLOT4{ zwt`s3!cT%yusg&tp?Z6bYibv7(R5rKHhSx>k0#%_yu?zq7>gvKxi-A&2qz9`*@&jI z(9*8Jy|G;XBAb??&9r%)x4FA4k7#)%E?Pgi63$78X=GN>aZ4|2QwW})(=efDSbRgaBK#032)u}y5ahWZfc7j1BU7f9J}zjsO-+k9+2S(%@%19u;Ek#S%mzlO&`3jL}@MkWX1 zcPH(_-k$f_q@Fw zXhldz{qz3$*;Nh>#gx!~BjB}Ed};CB_=TVnXWDZ4c>{3@lur)}i}$KiEhA3mMp-}Y zvWxKB)o313^!78W6jD@AQAAMXw4)=EsmF42Hha5}%E9L zq%Z~3a1EzBQfqd_%jb|~W9Feue;3kv1i0;gV53599)e%; z6Zl7=+bKm&hzRdqGHeeGfgBDFOVt-Ix*+51(%Gzb@9-x~8G$PS9Sbj>)fflS1GrTf zW{))`gV9)+d`@CW6T>-Jc+P~tBi(x}{A)z2dj%XB_Qlr}wGP~FVrVz?+~t(+H4rFx z?74Wo7Z`2E#*W0qbsVk$cU+yk%jY zpBo|*o}v>gec)nem#FLp@Ro@?RmW+BxA=lupCdwk?BiE3H<^ZX0 zAmzhU39LnftB%W5F;xFRW(nAnlUogn;V$;S=QaM>?jh>(UV^Rn${UwC&#FMqVtz>IoyezFA=h@Xzf;7b3p zWPkb82<+?E!DkGfymvej^&Us%qUryHhUT0nKQZ)EJ;`GR`rZNH2Oxq+Q^OIo9Kp0# z_FrUZ@Y>(@(g+BIR99Q?@)W3P+fsTr?4thm4X8b6!XV+`C|+*KKeC6hxYWS= ztf8^Gu_2?9k_e5am#hgeObOZx$S%s(37uV`?x-Dq{kS==r0$n^e9jDd>Eqkq(TKA8 z)&`9~d&8t-DPQ_BsVgKRsnG}Is_Fp+MAYn7Ls7UK6z$HumtC?_f97*#E3x{5wz@K zgojP-NBh^WMS6|8OmRvSMnKuVOHOVT`m9$#8VsYNqN2~9fj9eMqLS=>c(=Le{cDJ) z{x!;~gUq@NpUUq4*r8QVpo<$qG`0D0dnWRE)eZSz4t{@`>B{%Fz9`Zp%Ob_|gPRz@ zKm{D5VEM@$T7`m(W5s)DYAU4v>YJ*w(^#PX>LIf~x!BoH+Fy93il%cUIC9I5II5bV zHWDoaH`f;IshOFJYc$PSZcs}iz;C~-XN{Wg>qC6kH$+FkDmMALItv5A%gtSPj|3ax zh_`<7C$cm)R&jfeAdD=|7z-UiEmS;j&Lb+C0hY88mWOkqL;7XmS1RQ^TwAY@!P*kZ6nQeS$s z$=e|n&9bOfhkorw0Jbk$=D-_jwD0D@>I?Rglwmh;BqK2jGXE{OzG@O>B}(T&*cg0K zArTpzaHAEBWd;RF0fYjftfGR3@EwS(Or{)5P#_oO z0&eVb>1Lhxp`2 zee|SWN_MkvrfHsH-rCmyInpD0rWh-v#2hWufG9OlT*&Sx^8QWHW)dmY&DuuMV* zB>0BmW-oU71=S3gw5lyekm>T{$I005--*6OYYL@U8om<~NOPO8Sjgca-IX_rl0m`4 z+1W%_npTJScb)&PiW(?>iAUHE0Gdqo5A^wl7)d9f;7pO_I>gLM3|QznD`Zfj)gHb5tBEfaFt+`H(WLo?M=X+v5-?E5j3Lo^U1<71OWkB)X`e1 z0Fh>4&Z~0cGQ!lF=HR_i1h?dqC9s+8(w_*O%sK`>_ zPUNU4e@jVzWudGjf@b*pdycpaD!#BVp!<=7!-aXohngmk&)Ee2mil$hoszQ*LJiKtlWYC(*PET1w#PnHy_4i@DLGbY*0Aj1$?QSYA z&Y9v>SXa0C_LCJSB1S`F zsF`D<;lI#@xrm7oj};1COidK!M(6*${bf}^CcLgw{bizFOI~yGAkzUFuY$)vA95z? z=<415#3PtiimGYqWrC3=xJLlh(winE#KYqX5R_Q=mM>qZ2|Ei^3L&Co4&?i89auXd zo$nWLd&rpX{hqYcL~y%C9&+1iqY}let#0|_Z@X;WQ6JSFGJ`OBK9^8S7Ji&wxzrz+ z7$gIEbeD_r(&(4ZwDk0aH8l{)BLzpOm=_kA64V)2Q~@Re7#oP621_@X)ZdHVgnm!? zJM75d>BcmymsHq@KU9yg5pLDhDU1wtP^2!;3c$_S=u*1*RzOFGVCe_7s`4JsE8(7{ z=@rqrd_N^ZoQMBl~*2o1L9S3kG5eH(`xZR0q};G>BZ@8Kh!ht-@OBYGT@nLCdW2gJ_sb> zTc_l-BH?)F4GbNqem177E0*}yfcT_cWs{PdYg$}25&1}p1&@r=N?li%67`(whB}*G zshIEC85c1EiG;?7^C>q0;=zMGgC~@P20zE-PP&;t;bCw9*Gsj@Ub{W@il3Cp>wE%ZnKKg~KiBSk~d|EB7|&3!LY zAe#Om$GU3TtV9JMfe>2(B{YOMSaI@EQ8jgT<{MuNF`pCEOXrSF+v=H@zIJ_M?H3;6 z9u{Wj{;}u8L}6sq*|Uu5n>QUwbc>z7v*^wFzgRl|^~?W-@mt0Gb~}Wbt!-nCTpC22 z`a3w3#>LbVlUx?LZd$+BGYrR<(a=??A5nDoGbZGK!6lpcS>?8LtFBeB^g=m<|x<0Vl!7`Em)ez16d`no4!R6pGm{&q6 zu8W7V>v&E90qOEqn5qh+mV`nUI7TI~Y>pc32QfE1cAegGhP4NH7MO_cgcoFoZDh6( zW9Ya`0`$n zzE(@Gp(jD$WPoh>*r{)Sog5({<=(NvwXH?RAl>L}-8z`UrSBn*{-mu3=7F94xXc=uNTV!j2w3FeOg4!94I zr2PCI5LVjH%lvl-t$g(N?;x{~>1W~Mht#R4@6z>{k2k^!@ED`yzJ(sI-J`!cCI zycKYLhk@RcIq-9Hv(Jci$=(%EQ7^I~06gQMK)C=Wdm<#Bz3;S6)W6&%FIU5{FH|G_ z{r&tLg+W1}20_Ok;=Q05ZcrAl=>Z3G8RHo@+5aA1ijJ9?nTuOA%LQFjPF+^J$G4Y_{r?SEA z1djz_yojIsZshG?=|=;_KDN{7llk($Qe60oGR(Rf7(7s^Hm{!H*SA>oAG?8JOaPPEU?U!*3CfF-V)7?|hX1dkfV=SoE?SQefZFEY9&$MEOCyA2tTKSRTi4N@D)|5moOf`wrLD z@k3JmtM5&mJX3IR#l^u{6VrJdvz+tarCDeGKmyz!I9@Sz-nUxJ&YR~GR=J)u_F-k8 zTDa<_H8B~fDqg)Jhx=31WVQ`Kl|`1|OtFiTe|i#MGg|>&{f-U^OI1sh7+jT5&@e~8 zSXcV@rMu3u-^p@wbwv}FS-M90(C4cIQmV?{N-!dpc`Uzl;Ai?^;0CnGEUp>*wQN41H@VQQOnP+k1xUJU(s*UrNscA-&d7m$EOWwHb#o@V>@~>pq~}@|9i~mOGn%}YKdJzIJnsy zgI!%Oe5eWv=+^~4YUG>kPx==OAypM`@^>f;J^r?mtG4XWfp4EF-P+R#z?iBZqU%Z# zCME@q7qiTd|4~&HE6jVnS6Y{>s(kyUVP*8b44Sw${&~Q93Z0>N)+tovijPb z4rL0S86T7kuG1@pc_^hg^|f{OA1i~ElGyk%Wco6%EhtE-aD7X6nw1!+ufPoHUcW#C zo8!}itvTEC*Z3qXFQJs3^S^*@Hl9%_Gygf~l z691kcKaAzJr43Ds!T~unFc6Ykoo93|*F6OVsDdrMWRrRmrQGd%-aG&NmVOz&=YTC4 z3F&nZtrfZN&rgtQ&BK+kuZKTOTe)FxRNQ8vs8ctZBuzk%Gi!G!N5~`VaZK)7bS)$CHWYg;zcU`Nn2i(U>-Z1;J2sn7luVOUQk{xWM_N z3k#`}nD1MB1Og}9r&s?j9+vL_mEB?96h}ohDol;OY6z3)vmXMH{52#?p%is^`+ZI{ z`2&e$4AwL|A1$UmV*gLvL{1tSBh%IC4GroVcS-QV&e0HlZwLONyZu&ImteWCSiNri zN#zn;YLfB81pNpaHUNYAdzlP&Dp&hoTYQQV%_$mQ(2NluW(aP}Q!xt}%c`la2DG5B zn-x1LSd>BFNdS%!F|l}N`jO+!9}_3@{`Wp>mp~VTjtkPKuIdT9`Adwhx;^>HzQXjsL3JX~Bp z805Uwtv2PZF(4)Iy#pww zoX}ZxZ0u;++Y%_(_+?;`yN!)8ya-DEWhQ8EA_veSKx>5&w3UJuyXhU=UN`8@K*Oct z{D?br$gDDUpAO(Kiuua=`udO`YuP++eWiUHqJ|-}mcI~>pQrzHP?IGH(n&J_c6Ky# zF8X?T>GVguP`aGZYMYO8*ms=i=#-SN*2LOK`m`C;t_v;BFToS(-S2NLh1E@#b;?q9 zuWaEzhmSk5iQM-IPEa=yd(^e|ZD1LY zr93DV9_117iU zaAV(bdTe4T<$w0%d(+>38)vYeKpDByjE;wR1(>%%v#-Hoic|=iTS|ohL|xml|5R+< zU4n^t)0}NLssH+#lrWNwbAW{1+!Y_!frTV!7%xL{1r6(AtotC-4}rq2LNtKUAztc= zQ?ao*KotavbUVwh7&YAle%JECLi+vAX&{^e0~4wcn3IRl$S3+=Fqq<`HZ-AE0O`%I z$bk`1dqleog0u$1D9E1%vX;$0m~fB=%wlmTi_fXcOA<~P>jyaH22A?nD1vW@l#J}8 z`RYA@cwQ1!*i3r<9Id2*94_R#V+6z^2pj}Ts|7o$p57cVJCnA!U{{8R15^Z*R>Lcr z40ptzKSyhVi=`4sA<%#!4QYcL0|;@F*@|$=1#(RAT{gc!Pk+3-mqZ3&k- zC9CB1Z>pr;99ggLO_wv)m&R@Z0ngDQ4WLmM9TIOrW#{YTlgE4=@tA=@`;(Q1iAhd< z{TWD@y{KwXR)KZ~M*a_fIew6InA?zf4yRcduw?>x31B3lO9Z76K*gLu;mwgrGGf&L z_9)Or!6JZQSHuT+#NSHrHsXi9D*#L2`JnWxs9;n1=v0xCoXii)0K80(-`|@!qn381c*yDwY3nR$}c1|H#2kjnW3SfT>>|?c~dA!Ng)|= z78Xiyz{7PAIce-=I*M$9FJ)uAY!Og{fYn4IEnuX6*+mxazZqnwm~NbYi(fs6=#VN3 zuRncg^~sYbfOiG^o;lp1AtAl+QuBo;y6k6R&4l;baI~BR(<#I-&ea;KNxl3(FTeuG zn-EF;2H6B(DX?I5X`+A#aIiV!4$n6npP;!^=d?ZwpB)t0fQuX-ABQJKYuZ`r1~IJ6 zxckl}*8h?#0U{ZEXCN~J=!?^8JUk)6oHc=8KmgFo;xTXbnME8O_paXa0IkgfMognR zZ&Omj0KVf|ifw+nmaDPx67WKxLjY$L=+IkP{emeQOHWa&S=axO#lAD}( ztB%*#_w~bVPWB}`^OLx^_*}I^JO8~{ z)k5HsE)SWjKMRk;N|D_HF#-Ug#T(KzO1VRq%pza|KoQeo=K+&XAj;t0VfZI3i~t`W zGCEB_IdY;;flivFWG9520)=%BCIx#_1(K*c;RRi30v)OW=xK;Q&|ZK!66j8(0fxvJ zbW<3v0x60j=8kVOx7X?Cw6wWEB6_%*!BYX(WiLum#IkO@M{!levpLGS`RCF%ZCb!2t?>P>}mdiVHp^KS1JIYnY9J{s`18 z_$=cXwVXk)=@&fdnVt25IuS@(5YYY--s!`+7FcJ18USNu(MRu~NWBr%svK?TY7Pr1 zq#6H!a!9R6{eFamNl(0js_Ll3dH!!p_%2|v6ao&by!T!f z>7H!2-vdHWe$r&VQZ59wR>M_Q+Hkz~eA#@(^S8WlO9azZk1?(FPnBL=j&;jKXh zb9oT~P8?XA@K!8i^dDC309~@Ipuhs8d-#Gu+W!NWRo>vo(|4TF34Xduqm~R3BOP99 zoz4a%sye4UawNU;jS!=y8SpMgIg*1&$nHp>nzP<0rtwOAR(8d;D4)KMr3|E7!<#O| zrC9D`oVV?XqK)&-vl(bZrkU=x%iv)RJt=Q%DttVewYq=o7h29r1S6b=Sox|d#Yz&W z9gE@&Y2CA&LvB3ZvE;eMZ{$mEF@qmeRykhyA{!Uj3MWZ{xuax&Jp-)HU$s2u!=lU%ixVZ1D{Z=p+vQh<@bFSWME*|t|wpP!n#qN zLnC$O_=iWrw7ur4;ekRx`vaUcobJ~b7jKc1;~_2?m_V}=n(OapW?^{+mjsyYhKaw1 z-+v05!Dy|EZNM|Y(#F9OJYC}y#5)gPB18n-rJ!JpkeGE_XLNA!fxmlIJq^4WMQ_L+ zPoeIFC7v@?0A`#Zf;hH&2_;54u3iMAhO3KaU<=FW6_?Q?D*oI+z>N0W|y2vFZ zB~Z6yYzn{(dwHky#o(?tj*O{4mD2SHjC6O_Ji)+|!BSYM{apR*=C!xjWhUT7|r-j8D zeQBNOS->JByFCQ5(dyzN8`NF}Dj~->fVqbEbFjOMi;WGo1F%4`=Nhw<0v6re)|UBB z5Y%bgaN)zTWNLg|f;tiq)R0}QL_8(WGrT4vp>hmk@G!Lp7#TohCpHc}wy*Z+OXdN% zRjybkyY4gPt$f%XL1Ak=*{p?QBb3-q-6Dk}p=4a4f1Nboh>4)SJZ?I0CqrEhqt$ES zehvOWXABJ$1WyD{D`>};`*A@jhhCy)F=Q?O0sJG(n*!?~M=p)>=1m2l(i)xXxo@D} zyn!HBzL!m4TqUBQ0LZMYiZ{q&3jNJ2(uE_k39KOUk55ejFQK8loRx>CI=yNWZem{N zUw=Aj(=KH~`1f*gaoHp%iUquNB()@pK;7ch3ADkrsVe56*1*tEEQB}2h3mS$?=>MH zWtS)&Q*vTbjb-!G=MHUz$wBynfesiYCkWQD;pJS(vANG;w`2i*Wzp$#cBonxD$*3Ed7K?BYH2 za^>k_by72(>Fy5GRkFQZ+@~}#Mp8=>^7^&C%o{DRsI>5nB2-DL zag0dex56ym?8z)f`nW0vqATs@G`F-HLmyGU8IWi=M&2fhkfw1yV2Di)c6}`@i|>5M zw7Q1l=oVC(9lJAlCDpWY_EYy6p}Fug7p$OF1}YP=*;QuAji37L76nDpm4g9-1*{ysl9je|scuas6({@arVY(dUDC0#M)t38MZz+$2Ku=pZ~bg~h_Vv*{(SbN ze!dV;Rn5#E@6UVw{*LC~@}xZ!OsW8dDpo%&b`9o@2Fn}b)zOae)vH&#-L>i!6%|SF{HjwD*W9E8Zs5E}d{9Q_b9TWs@8IxJ{;{Kr zOKyf-plh?~TVX1~0O(%!cq^jJLZW?D3uS;ddo>?!P1qEmEFN~mf%{OI@-a=6Ic(+z zW#jyVm7NdD>v+@UB|M*wE-Wv%8L{@de$sAC>|aS@PEX&OhyIE=dm`K(3fI4aXH5CG zk*hgiR)Icp>IMQIG0CA^Ct| z{RQmy&myTg^jl#chFi~!>3T(*9&tC9@C?5arkOF1QXUQ|(g(Qf6hzCwcxI!cGlyhE zs19J@6FfMC0a>&5pMEt@KpOH|I|8+52=_mDH$0)rfODkc%;#0dWAF-sOR5`QHGHZH z`e{p-cS^j_KH@t7+ZSJ(o97$f<=nZmHc_zx2uV18K#@oRH8?rBwC6u-UQlNDM150F z8~%bvfLCEo9IvgyuT#ih=QLIcv+7|&CcW_y43+NP6YCPL=id`|=IZ#6K^2C0lei7( zgF9a&MccQvMSS(0>i1o_ZLUz=)8!(y;j{2lE)?yOcqnjz=c{&Ow8Wh%t$!?!V?X$n zZNb67DQ!}(mwMjTtd4yqwc)!KQ>=6bttiLSV@@CTC#aYd_L*ob#_KI+B#aeB!wD1J zJL>C7_pe=%rujJ<;^=@*dVQ)k4my$y%wZnuj4xTsqHmK3Chz@jBE0psInT3bBwt;z zaO87{-g9GA!7s%XHQxzxKV&^QeoWxUwXod7jG+x_;R;rw%k02i2wtaz5R*T^3xJY? zi>n_LbGV=`-DRg9QJv4Ox1ND01sg0BUiSzIzZkLJA|!;*QOwg!GO`j+fbkyKW=>&| zgFTKg;0HVmF7c=k1%?k*2%Lm(2et0wtFg}cvy)yAU0gnzmdy7Xg zHTxpk4a(L$tpuMwTnbN4?lGyzC#!3I8PDfZl9{;(=PBU+t~%CK3q3Z5h8k~5-_Xzy z|Ks9_2N`Qw`S|~!c9f)w`Nxi<+ycmsD4k!u`Z+(8wWSW!S zHqGwb>)D<-!%+F&eEE$IC(s+o+b_90hVA&>8^b8T3SL4Boe0cNoyu6FMx)tUcT|9m z#hVgJieX}D>Hg>UJoNC;+PBAa-L1hx0b{*3JP^Io;=x(qCqWaXuyk3lL$U0#F*$M? z+t=TJ0lVa!+j=onvCw+Dg`r@%z(`-(w?iYfl-Bi09oH4ihRtBW2^q8M71z`K=O&$ z&|2`uR2j)EURhM8Y;LPvoraS9$P3Jtk5syGFIqo(H|BPf5y4`My-8e8cwfb1h`Y-eCwZZbiARd8(PuNg=OA#fFK~C~t3zf?)oLW* z?57x)rP{rQgWpws|{Y)bjG8{M`4 zEaByY1sMJT{SR7DgxDA|s#R3jib0yi4X(Q;Q56z9M;>>AR0`)vi1_TQwSiWB zYNx4+BK-{H>ox1+mnnVKU>FmcP9!J#8j&~+O(Nez{&{6LY>0w@xDIU zba^9S?AC}`>c(;AD(3eHHG$T!u~^mzBo9#V3Ux~x!5;)Q1H?Rh0+p!SKBvnX4#UuQ zjit?MqrHhy20!=v_bZFf`jESY(5`h3X*G~ZWDnr=J>s0yj%7yMXB*s!d%9?QpEm`T z6DTD8m1czCP)JJ{U|?uieERNF45ZpD(#n3L^rvS>{Q0v8Sw?>r55+@D_LW^Ol)2`} zva3DoL<8R+*vh7S{-{KITxQtTP};b*=1@~^mK>rJTrn20<1^1RE3WOy9Jh3^f1NQ- zg*tkLz~=FhTdf*2qI7fwxJ^2eo;;b+3yJ>t#bcwT*4Z5;w>9hh$yu`)%XX%T4)N!A z%pt){kK@9GluQgVQ^c4(PX-IsnU_s=XXXr9&+98=sQaROtLNtgq(uJk68K(tcwhOSY7gv>GqnKPG<|;ypmcbcZ~d-S}WglN%gVba8PBYd2$0 z^z}s-x#3nnd-_Daa73*{JDm_pcPOZrH(4(UTSf^lz@haXml$Ypmjf&8MD#xFxS@-U zX9r2mFG;}Qwb+k`kIzYh2XPrfbuiLUQ+pK?bDrB~^EfswO-TRCyVFze-rj}vb*bxI zPi!Tnq!xFNl%MP9K)nDRXGkl;#>cn$$zx()4K9nZ#JN1ERG^AjPUK0DR@K(Fni%>Z z84~>zv^E}s;o_9QmAvx^pd-butb%p{;syvIpp&9m7)d$=<+YL?Q*xS0Kfb*=QIVUM z*D>8Xw=WRv_K@(^k+Y+sjLPk+1gv(Qf4GFqXbC1^uzLOoJnVsmZjIBKCWrkh^+u>cvNR} z_tJ9VEjVdJ{Ih2A3Jz5Z3kz^oSJ)+xAE;9;G%izLp6t3j=Nr3z^0)~#OT4s6@qxeJ ztmx&ap#N3Gvj+p<`UJ#dMMXvN#k)^p78V6d%ozlNB)gyigdP1^6c;IghyN@!Ju_`d zp_)9BPMupQm&YX3J7boojyhinJD;096MwF)@w>OUL#87@l^6d`l`nN< zK-SrwO)Il9L*V`ay1>hqLJSX^AKk+LLcJ96CR9VbwTQzU^<_`O+@|pU{qZi=7Ahu| zm)}`V6Ax$W4mZ!Y{UZGCZPRzuq_yz$;Msn5p={xstDbXjdm-d7vbF4ZF@eKe|id?M?<8+LQU*U7jkAjXGEotHti%>v~-HZsDIl&B@m zm_rlQ+cw8C_p?m%`P+Nl$;t}1(LK=5y{1MpaD~QS$1jv*+_b*+ER>jABwT^jmTRPO zPsXZvY&nKm@is?|su3lnZbCxL+qde+?ap?qCy$;Neo$Vsw0L>uR$X=YQ*)ks69khB zey`KrNMRi~W5?k2eKYO#NjxblOvX|T%umK0O62;O18GKfjo5pr~iSn1&h9s7eoZ0kHZf(WHP(e zdx5R!%=9rt#0@|#VBTadOdzm#qkk7*N&v0H^1aW-rUcpBW^YVC?tU8`b>3f7X+Q0N z6diC#ff!l|@p(etf^d;Wp`kJH?OS+K zl4ZpNY&$eE2NH5}rA0;T3DTf(!mL+FaIhn9KhFCF==w}U03!&paEgL_fJ-yk2osDj zz?p64qrNZjU*HTbsh|vI6L!l9gE9~3Qxq2$LsLa8(+-3fSVOXs1V4TH)Bxfp#700Z zD+C=L5P?!mT<{L=h+;!N!}5wjk!@s2a~!VhYKVhu zg&1Ua$TCP{*U9!hj_><#uDNF3x!!kvzu)sb_kBOlJr_AwM(b$iUUU9jykRk-CBjm! zGhm$HJeoWnrP$xkNsX8!*+?cO4a{`rFU&XflDUsYki4C`lj<9;eK1hyXCb!S1IfEz zZr%}H((D=zi>cVK4|n!dX$cH`KEhy>jk;HsJ89Nv`A&()*W^l} zk`^!a5xDKtj+4ZjbwV5z@saV90!)#%we{s2w`4C%d*p_1h*R%Mq_%6;R z8PJ@!ebdbd_~o*qH3L#Fq%yp`yfD8BD=0Y2Vo@P!v9wG`O^w9@Al%m4nh>J|K9N9x z)Pg<_qM3KrO{stV6*XZHB52nX6gu5Yre0TrFsZN)R+*Wp=~sH%<lAbwXD_ykZ^ z?Mk~WeCU)#Rt8K>gLG}Ar)LEO(*P>uU$>D#!e9+jBY*hH3~TgUR5?p@zAp)B8!$VM zZ&m>wdD}J3I(G;VJ$PCmQ3bvMhE4Yv4p*;5f#Dp?Vvv-~FDioeG(Siah|zE0y8t|E zK%D@H5snk|n5&SHfsx52TicB$K9u!$kzLqXO)V{T>-|ok^aAkVu`l~@CHVtLiY+Wy zOeSO#RxV2*-B-_?jgCeq8z)1o?(hG_t3ByJV~ILvZf+hHR&8D02Wmtp`6VT%ztLI! zJ}QtIz`cV01Kbv%dArPdxEsh?r63P=+hS)ph@K}TK#$LbTqE<4vE0bmDobCGOPF0EU?e`LdKQa$>}~6RE&hFoAs%ezcUKzBIf4`)cdn6 z{#mV6)0?;u1PyWCbM%jgyNYjzFW7GGY>`99fU^8K?$KiZT#~l--;3lUTw1c6YL3Ol zX}_3^XS>F~7bu8+s+VRkUggNyTurAB+k5-?U74~s#~zNx-jj~TDlTa%mRF^#9{;JK z_v>I#hSY42%6|XI2KCZt9X|&Nm+qLhvFFcB?=LH=z9)FoZcIy0U}GuXW>bGmbT;tJ~#6CFSBaz4J_F!paxC$H($F!F{bX z5p)j41#c;FBRai-F*@p^kh1ycE5}tYIDPkFK7Gu(9m z@f)FQGM;S)DXlYcXRKRmcdRz|z8HpV+c@TN3fh zc#}dCa3Cl-wt0JXg*C}!UjXL zv+wKiOX);xle(KjCA4KgTUBwfg1kJrl)k!UJCUY$;*!PJKpDTh$!%|oAWs(vPKrtdDV|4_xyA_~5dm5Z&z z^!LP=X2ibj`cKyfvaZi7gqx>({1{I@N71d)Upz@@^Hlb=$fc_0;IGEsS3GY^WMFS1n>&#Oi&7+oUzHo zD_XgS!pS$~p%YN(@A*$=&yHM+dpvSi%g_rii}Q|4*wQ_17|2Msrw$*mAJ_YPM*iOW?q8mnt=dLHJNQ{s`jz@$e2zh2!GH zgp!*Bx~JXH2}Z<$YBv?xDckOxi;Ve88BGxmvN{$`DKpcXm69?P|6l9;wlUY!=}_a% zAfZ!n6rmuiUr%u0{VHA8txg>1M<;Dn^q!5&jCKvF%MD0a(a`*|m>kjbmi|k$?RG6X zg0S5-($lq0EC91z#5DqF;5|S(B##jhT7zb{8m8}dvr~wlnX|_x1yC0w{w&{ABITNTLfuQ#{b%2jjpw5R+IRgo*pCa8Fkj= z0u1=x*G$J!8%zE)e6XSP>D>$IQiJTC(2)}qaf5{RK%upldpz@*xKG}GeY-?7g(rtH z?TloaddWidget(new QLabel("Find:"), 0, 0, Qt::AlignRight); layout->addWidget(search, 0, 1, 1, 2, Qt::AlignLeft); From be029a9122f14e364aadf86cda23d7b4deded595 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 24 Aug 2024 14:39:19 -0400 Subject: [PATCH 070/355] tweak layout of keyboard shortcut table --- doc/src/Howto_lammps_gui.rst | 40 ++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/doc/src/Howto_lammps_gui.rst b/doc/src/Howto_lammps_gui.rst index 4d283cbcc8..3dcf5a5f12 100644 --- a/doc/src/Howto_lammps_gui.rst +++ b/doc/src/Howto_lammps_gui.rst @@ -848,7 +848,7 @@ available (On macOS use the Command key instead of Ctrl/Control). .. list-table:: :header-rows: 1 - :widths: auto + :widths: 16 19 13 16 13 22 * - Shortcut - Function @@ -890,32 +890,32 @@ available (On macOS use the Command key instead of Ctrl/Control). - Quit Application - Ctrl+A - Select All - - Ctrl+P - - Preferences + - Ctrl+F + - Find and Replace * - Ctrl+W - Close Window - - Ctrl+Shift+H - - Quick Help - - Ctrl+Shift+G - - LAMMPS-GUI Howto - * - Ctrl+Shift+A - - About LAMMPS - - Ctrl+? - - Context Help - - Ctrl+Shift+W - - Show Variables - * - Ctrl+Shift+M - - LAMMPS Manual - TAB - Reformat line - Shift+TAB - Show Completions - * - Ctrl+Shift+T - - LAMMPS Tutorial - - Ctrl+Shift+Enter + * - Ctrl+Shift+Enter - Run File - - - - + - Ctrl+Shift+W + - Show Variables + - Ctrl+P + - Preferences + * - Ctrl+Shift+A + - About LAMMPS + - Ctrl+Shift+H + - Quick Help + - Ctrl+Shift+G + - LAMMPS-GUI Howto + * - Ctrl+Shift+M + - LAMMPS Manual + - Ctrl+? + - Context Help + - Ctrl+Shift+T + - LAMMPS Tutorial Further editing keybindings `are documented with the Qt documentation `_. In From 2a84aa80633034f421b41853528e1af1a11ec4e6 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 24 Aug 2024 14:39:33 -0400 Subject: [PATCH 071/355] document find and replace dialog --- doc/src/Howto_lammps_gui.rst | 45 +++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/doc/src/Howto_lammps_gui.rst b/doc/src/Howto_lammps_gui.rst index 3dcf5a5f12..5c286bcd2d 100644 --- a/doc/src/Howto_lammps_gui.rst +++ b/doc/src/Howto_lammps_gui.rst @@ -629,9 +629,10 @@ Edit ^^^^ The *Edit* menu offers the usual editor functions like *Undo*, *Redo*, -*Cut*, *Copy*, *Paste*. It can also open a *Preferences* dialog -(keyboard shortcut `Ctrl-P`) and allows deleting all stored preferences -and settings, so they are reset to their default values. +*Cut*, *Copy*, *Paste*, and a *Find and Replace* dialog (keyboard +shortcut `Ctrl-F`). It can also open a *Preferences* dialog (keyboard +shortcut `Ctrl-P`) and allows deleting all stored preferences and +settings, so they are reset to their default values. Run ^^^ @@ -716,6 +717,44 @@ https://lammpstutorials.github.io/ in a web browser window. ----- +Find and Replace +---------------- + +.. versionadded:: 1.6 + +The *Find and Replace* dialog allows searching for and replacing +text in the *Editor* window. + +.. image:: JPG/lammps-gui-find.png + :align: center + :scale: 50% + +The dialog can be opened either from the *Edit* menu or with the +keyboard shortcut `Ctrl-F`. You can enter the text to search for. +Through three check-boxes the search behavior can be adjusted: + +- If checked, "Match case" does a case sensitive search. + +- If checked, "Wrap around" starts searching from the start of the + document, if there is no match found from the current cursor position + until the end of the document. + +- If checked, the "Whole word" setting only finds full word matches + (white space and special characters are word boundaries). + +Clicking on the *Next* button will search for the next occurrence of the +search text and select / highlight it. Clicking on the *Replace* button +will replace an already highlighted search text and find the next one. +If no text is selected, or the selected text does not match the +selection string, then the first click on the *Replace* button will +only search and highlight the next occurrence of the search string. +Clicking on the *Replace All* button will replace all occurrences from +the cursor position to the end of the file; if the *Wrap around* box is +checked, then it will replace **all** occurrences in the **entire** +document. Clicking on the *Done* button will dismiss the dialog. + +------ + Preferences ----------- From 217546ed3ba12adbe2e8c5d7d8da4ce2f67a7696 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 24 Aug 2024 15:11:43 -0400 Subject: [PATCH 072/355] tweak LAMMPS-GUI howto typesetting --- doc/src/Howto_lammps_gui.rst | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/doc/src/Howto_lammps_gui.rst b/doc/src/Howto_lammps_gui.rst index 5c286bcd2d..bad4ff85fd 100644 --- a/doc/src/Howto_lammps_gui.rst +++ b/doc/src/Howto_lammps_gui.rst @@ -391,7 +391,7 @@ below. .. image:: JPG/lammps-gui-variable-info.png :align: center - :scale: 75% + :scale: 50% Like for the *Output* and *Charts* windows, its content is continuously updated during a run. It will show "(none)" if there are no variables @@ -668,7 +668,7 @@ set *before* a run is started. .. image:: JPG/lammps-gui-variables.png :align: center - :scale: 75% + :scale: 50% The *Set Variables* dialog will be pre-populated with entries that are set as index variables in the input and any variables that are @@ -722,22 +722,23 @@ Find and Replace .. versionadded:: 1.6 -The *Find and Replace* dialog allows searching for and replacing -text in the *Editor* window. - .. image:: JPG/lammps-gui-find.png :align: center - :scale: 50% + :scale: 33% + +The *Find and Replace* dialog allows searching for and replacing +text in the *Editor* window. The dialog can be opened either from the *Edit* menu or with the keyboard shortcut `Ctrl-F`. You can enter the text to search for. Through three check-boxes the search behavior can be adjusted: -- If checked, "Match case" does a case sensitive search. +- If checked, "Match case" does a case sensitive search; otherwise + the search is case insensitive. - If checked, "Wrap around" starts searching from the start of the document, if there is no match found from the current cursor position - until the end of the document. + until the end of the document; otherwise the search will stop. - If checked, the "Whole word" setting only finds full word matches (white space and special characters are word boundaries). From baf1511f23ea0a43486fd2994e066bf5380dbf3c Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 24 Aug 2024 19:19:56 -0400 Subject: [PATCH 073/355] add light/dark mode detection --- tools/lammps-gui/helpers.cpp | 13 +++++++++++++ tools/lammps-gui/helpers.h | 3 +++ 2 files changed, 16 insertions(+) diff --git a/tools/lammps-gui/helpers.cpp b/tools/lammps-gui/helpers.cpp index 47d09f4515..cac5d86482 100644 --- a/tools/lammps-gui/helpers.cpp +++ b/tools/lammps-gui/helpers.cpp @@ -13,9 +13,12 @@ #include "helpers.h" +#include +#include #include #include #include +#include #include #include @@ -84,6 +87,16 @@ void purge_directory(const QString &dir) } } +// compare black level of foreground and background color +bool is_light_theme() +{ + QPalette p; + int fg = p.brush(QPalette::Active, QPalette::WindowText).color().black(); + int bg = p.brush(QPalette::Active, QPalette::Window).color().black(); + + return (fg > bg); +} + // Local Variables: // c-basic-offset: 4 // End: diff --git a/tools/lammps-gui/helpers.h b/tools/lammps-gui/helpers.h index b22b6e72c4..b3269e6d61 100644 --- a/tools/lammps-gui/helpers.h +++ b/tools/lammps-gui/helpers.h @@ -28,6 +28,9 @@ extern bool has_exe(const QString &exe); // recursively purge a directory extern void purge_directory(const QString &dir); +// flag if light or dark theme +extern bool is_light_theme(); + #endif // Local Variables: // c-basic-offset: 4 From e19556263c3c62b6a24b920a102aed5523e166a0 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 24 Aug 2024 19:20:39 -0400 Subject: [PATCH 074/355] provide syntax highlighting color schemes for light and dark themes --- tools/lammps-gui/highlighter.cpp | 68 ++++++++++++++++++++++---------- tools/lammps-gui/lammpsgui.cpp | 6 ++- 2 files changed, 53 insertions(+), 21 deletions(-) diff --git a/tools/lammps-gui/highlighter.cpp b/tools/lammps-gui/highlighter.cpp index 3f79c7a73e..9846011836 100644 --- a/tools/lammps-gui/highlighter.cpp +++ b/tools/lammps-gui/highlighter.cpp @@ -12,6 +12,7 @@ ------------------------------------------------------------------------- */ #include "highlighter.h" +#include "helpers.h" Highlighter::Highlighter(QTextDocument *parent) : QSyntaxHighlighter(parent), @@ -54,27 +55,54 @@ Highlighter::Highlighter(QTextDocument *parent) : isTriple(QStringLiteral("[^\"]*\"\"\"[^\"]*")), isString(QStringLiteral("(\".+?\"|'.+?'|\"\"\".*\"\"\")")), in_triple(false) { - formatNumber.setForeground(Qt::blue); - formatString.setForeground(Qt::darkGreen); - formatString.setFontWeight(QFont::Normal); - formatComment.setForeground(Qt::red); - formatSpecial.setForeground(Qt::darkMagenta); - formatSpecial.setFontWeight(QFont::Bold); - formatParticle.setForeground(Qt::darkRed); - formatParticle.setFontWeight(QFont::Bold); - formatRun.setForeground(Qt::darkBlue); - formatRun.setFontWeight(QFont::Bold); - formatVariable.setForeground(Qt::darkGray); - formatVariable.setFontWeight(QFont::Bold); + if (is_light_theme()) { + // syntax colors for light themes + formatNumber.setForeground(Qt::blue); + formatString.setForeground(Qt::darkGreen); + formatString.setFontWeight(QFont::Normal); + formatComment.setForeground(Qt::red); + formatSpecial.setForeground(Qt::darkMagenta); + formatSpecial.setFontWeight(QFont::Bold); + formatParticle.setForeground(Qt::darkRed); + formatParticle.setFontWeight(QFont::Bold); + formatRun.setForeground(Qt::darkBlue); + formatRun.setFontWeight(QFont::Bold); + formatVariable.setForeground(Qt::darkGray); + formatVariable.setFontWeight(QFont::Bold); - formatOutput.setForeground(Qt::darkYellow); - formatOutput.setFontWeight(QFont::Bold); - formatRead.setForeground(Qt::magenta); - formatRead.setFontWeight(QFont::Bold); - formatLattice.setForeground(Qt::darkGreen); - formatLattice.setFontWeight(QFont::Bold); - formatSetup.setForeground(Qt::darkCyan); - formatSetup.setFontWeight(QFont::Bold); + formatOutput.setForeground(Qt::darkYellow); + formatOutput.setFontWeight(QFont::Bold); + formatRead.setForeground(Qt::magenta); + formatRead.setFontWeight(QFont::Bold); + formatLattice.setForeground(Qt::darkGreen); + formatLattice.setFontWeight(QFont::Bold); + formatSetup.setForeground(Qt::darkCyan); + formatSetup.setFontWeight(QFont::Bold); + } else { + // syntax colors for dark themes + formatNumber.setForeground(QColorConstants::Svg::dodgerblue); + formatString.setForeground(QColorConstants::Green); + formatString.setFontWeight(QFont::Normal); + formatComment.setForeground(QColorConstants::Red); + formatComment.setFontWeight(QFont::Bold); + formatSpecial.setForeground(QColorConstants::Magenta); + formatSpecial.setFontWeight(QFont::Bold); + formatParticle.setForeground(QColorConstants::Svg::indianred); + formatParticle.setFontWeight(QFont::Bold); + formatRun.setForeground(QColorConstants::Svg::lightskyblue); + formatRun.setFontWeight(QFont::Bold); + formatVariable.setForeground(QColorConstants::Svg::lightgray); + formatVariable.setFontWeight(QFont::Bold); + + formatOutput.setForeground(QColorConstants::Yellow); + formatOutput.setFontWeight(QFont::Bold); + formatRead.setForeground(QColorConstants::Svg::lightcoral); + formatRead.setFontWeight(QFont::Bold); + formatLattice.setForeground(QColorConstants::Svg::lightgreen); + formatLattice.setFontWeight(QFont::Bold); + formatSetup.setForeground(QColorConstants::Cyan); + formatSetup.setFontWeight(QFont::Bold); + } } void Highlighter::highlightBlock(const QString &text) diff --git a/tools/lammps-gui/lammpsgui.cpp b/tools/lammps-gui/lammpsgui.cpp index 362a8f00cb..d75f97da7a 100644 --- a/tools/lammps-gui/lammpsgui.cpp +++ b/tools/lammps-gui/lammpsgui.cpp @@ -1431,7 +1431,11 @@ void LammpsGui::setFont(const QFont &newfont) void LammpsGui::about() { std::string version = "This is LAMMPS-GUI version " LAMMPS_GUI_VERSION; - version += " using Qt version " QT_VERSION_STR "\n"; + version += " using Qt version " QT_VERSION_STR; + if (is_light_theme()) + version += " using light theme\n"; + else + version += " using dark theme\n"; if (lammps.has_plugin()) { version += "LAMMPS library loaded as plugin"; if (!plugin_path.empty()) { From 4507101ec3fefcca7ffe21744911cd1fb6268f87 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 24 Aug 2024 22:51:50 -0400 Subject: [PATCH 075/355] show dark mode screenshot --- check-downloads.py | 18 +++++++ doc/src/Howto_lammps_gui.rst | 86 ++++++++++++++------------------ doc/src/JPG/lammps-gui-main.png | Bin 95633 -> 78873 bytes tools/lammps-gui/TODO.md | 5 +- 4 files changed, 58 insertions(+), 51 deletions(-) create mode 100755 check-downloads.py diff --git a/check-downloads.py b/check-downloads.py new file mode 100755 index 0000000000..57b4af6a19 --- /dev/null +++ b/check-downloads.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +import subprocess +import json + +try: + output = subprocess.run('gh api https://api.github.com/repos/lammps/lammps/releases', + shell=True, capture_output=True) +except subprocess.CalledProcessError as e: + print("API call failed with:", e.output) + +releases = json.loads(output.stdout) +print("Recent releases: ", len(releases)) +for rel in releases: + if len(rel['assets']) > 0: + print("Release: ", rel['name']) + for asset in rel['assets']: + print("Asset: %s Downloads: %d" % (asset['name'], asset['download_count'])) diff --git a/doc/src/Howto_lammps_gui.rst b/doc/src/Howto_lammps_gui.rst index bad4ff85fd..364c70d50f 100644 --- a/doc/src/Howto_lammps_gui.rst +++ b/doc/src/Howto_lammps_gui.rst @@ -19,9 +19,9 @@ to the online LAMMPS documentation for known LAMMPS commands and styles. Pre-compiled, ready-to-use LAMMPS-GUI executables for Linux x86\_64 (Ubuntu 20.04LTS or later and compatible), macOS (version 11 aka Big Sur or later), and Windows (version 10 or later) :ref:`are available - ` for download. None-MPI LAMMPS executables for - running LAMMPS from the command line and :doc:`some LAMMPS tools ` - are also included. + ` for download. Non-MPI LAMMPS executables (as + ``lmp``) for running LAMMPS from the command line and :doc:`some + LAMMPS tools ` compiled executables are also included. The source code for LAMMPS-GUI is included in the LAMMPS source code distribution and can be found in the ``tools/lammps-gui`` folder. It @@ -29,40 +29,50 @@ to the online LAMMPS documentation for known LAMMPS commands and styles. `. LAMMPS-GUI tries to provide an experience similar to what people -traditionally would have running LAMMPS using a command line window -and the console LAMMPS executable but just rolled into a single executable: +traditionally would have running LAMMPS using a command line window and +the console LAMMPS executable but just rolled into a single executable: - writing & editing LAMMPS input files with a text editor - run LAMMPS on those input file with selected command line flags -- use or extract data from the created files and visualize it with - either a molecular visualization program or a plotting program +- extract data from the created files and visualize it with and + external software That procedure is quite effective for people proficient in using the command line, as that allows them to use tools for the individual steps -that they are most comfortable with. It is often *required* to adopt -this workflow when running LAMMPS simulations on high-performance +that they are most comfortable with. In fact, it is often *required* to +adopt this workflow when running LAMMPS simulations on high-performance computing facilities. The main benefit of using LAMMPS-GUI is that many basic tasks can be -done directly from the GUI without switching to a text console window or -using external programs, let alone writing scripts to extract data from -the generated output. It also integrates well with graphical desktop -environments where the `.lmp` filename extension can be registered with -LAMMPS-GUI as the executable to launch when double clicking on such -files. Also, LAMMPS-GUI has support for drag-n-drop, i.e. an input -file can be selected and then moved and dropped on the LAMMPS-GUI -executable, and LAMMPS-GUI will launch and read the file into its -buffer. +done directly from the GUI **without** switching to a text console +window or using external programs, let alone writing scripts to extract +data from the generated output. It also integrates well with graphical +desktop environments where the `.lmp` filename extension can be +registered with LAMMPS-GUI as the executable to launch when double +clicking on such files. Also, LAMMPS-GUI has support for drag-n-drop, +i.e. an input file can be selected and then moved and dropped on the +LAMMPS-GUI executable, and LAMMPS-GUI will launch and read the file into +its buffer. In many cases LAMMPS-GUI will be integrated into the +graphical desktop environment and can be launched like other +applications. LAMMPS-GUI thus makes it easier for beginners to get started running simple LAMMPS simulations. It is very suitable for tutorials on LAMMPS since you only need to learn how to use a single program for most tasks and thus time can be saved and people can focus on learning LAMMPS. -The tutorials at https://lammpstutorials.github.io/ were specifically +The tutorials at https://lammpstutorials.github.io/ are specifically updated for use with LAMMPS-GUI. Another design goal is to keep the barrier low when replacing part of -the functionality of LAMMPS-GUI with external tools. +the functionality of LAMMPS-GUI with external tools. That said, LAMMPS-GUI +has some unique functionality that is not found elsewhere: + +- auto-adapting to features available in the integrated LAMMPS library +- interactive visualization using the :doc:`dump image ` + command with the option to copy-paste the resulting settings +- automatic slide show generation from dump image out at runtime +- automatic plotting of thermodynamics data at runtime +- inspection of binary restart files The following text provides a detailed tour of the features and functionality of LAMMPS-GUI. Suggestions for new features and @@ -134,9 +144,13 @@ When LAMMPS-GUI starts, it shows the main window, labeled *Editor*, with either an empty buffer or the contents of the file used as argument. In the latter case it may look like the following: -.. image:: JPG/lammps-gui-main.png - :align: center - :scale: 50% +.. |gui-main1| image:: JPG/lammps-gui-main.png + :width: 48% + +.. |gui-main2| image:: JPG/lammps-gui-dark.png + :width: 48% + +|gui-main1| |gui-main2| There is the typical menu bar at the top, then the main editor buffer, and a status bar at the bottom. The input file contents are shown @@ -276,8 +290,6 @@ right mouse button into the *Output* window text area. :align: center :scale: 50% -.. versionadded:: 1.6 - Should the *Output* window contain embedded YAML format text (see above for a demonstration), for example from using :doc:`thermo_style yaml ` or :doc:`thermo_modify line yaml `, the @@ -289,10 +301,6 @@ text area. Charts Window ------------- -.. versionadded:: 1.6 - - Plot smoothing support - By default, when starting a run, a *Charts* window opens that displays a plot of thermodynamic output of the LAMMPS calculation as shown below. @@ -327,10 +335,6 @@ corresponds to. Same as for the *Output* window, the chart window is replaced on each new run, but the behavior can be changed in the *Preferences* dialog. -.. versionadded:: 1.6 - - Support for YAML export added - From the *File* menu on the top left, it is possible to save an image of the currently displayed plot or export the data in either plain text columns (for use by plotting tools like `gnuplot @@ -371,8 +375,6 @@ zoom in or zoom out of the displayed images. The button on the very left triggers an export of the slide show animation to a movie file, provided the `FFmpeg program `_ is installed. -.. versionadded:: 1.6 - When clicking on the "garbage can" icon, all image files of the slide show will be deleted. Since their number can be large for long simulations, this option enables to safely and quickly clean up the @@ -446,10 +448,6 @@ diameters are all the same. Visualization of LAMMPS "peptide" example -.. versionchanged:: 1.6 - - Buttons for toggling shininess and re-centering were added. - The default image size, some default image quality settings, the view style and some colors can be changed in the *Preferences* dialog window. From the image viewer window further adjustments can be made: @@ -468,8 +466,6 @@ current image can be saved to a file (keyboard shortcut `Ctrl-S`) or copied to the clipboard (keyboard shortcut `Ctrl-C`) for pasting the image into another application. -.. versionadded:: 1.6 - From the *File* menu it is also possible to copy the current :doc:`dump image ` and :doc:`dump_modify ` commands to the clipboard so they can be pasted into a LAMMPS input file @@ -488,8 +484,6 @@ Paste (`Ctrl-V`), Undo (`Ctrl-Z`), Redo (`Ctrl-Shift-Z`), Select All dialog will pop up asking whether to cancel the exit operation, or to save or not save the buffer contents to a file. -.. versionadded:: 1.6 - The editor has an auto-save mode that can be enabled or disabled in the *Preferences* dialog. In auto-save mode, the editor buffer is automatically saved before running LAMMPS or before exiting LAMMPS-GUI. @@ -553,8 +547,6 @@ context menu that open the corresponding documentation page in the online LAMMPS documentation in a web browser window. When using the keyboard, the first of those entries is chosen. -.. versionadded:: 1.6 - If the word under the cursor is a file, then additionally the context menu has an entry to open the file in a read-only text viewer window. If the file is a LAMMPS restart file, instead the menu entry offers to @@ -572,8 +564,6 @@ will contain a corresponding message. Inspecting a Restart file ^^^^^^^^^^^^^^^^^^^^^^^^^ -.. versionadded:: 1.6 - When LAMMPS-GUI is asked to "Inspect a Restart", it will read the restart file into a LAMMPS instance and then open three different windows. The first window is a text viewer with the output of an @@ -720,8 +710,6 @@ https://lammpstutorials.github.io/ in a web browser window. Find and Replace ---------------- -.. versionadded:: 1.6 - .. image:: JPG/lammps-gui-find.png :align: center :scale: 33% diff --git a/doc/src/JPG/lammps-gui-main.png b/doc/src/JPG/lammps-gui-main.png index 69efe3db68036c1a833af6fee22f1d40089b5a37..c7a1e5a98cb0be3108a4c70e6f267725c20ae3c9 100644 GIT binary patch literal 78873 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 bA^J&d9Z5?0$T4~!{zTYVIGEozJs9&pKT{SD literal 95633 zcmZsC1yEeg67Jc>9YU}WBxr)WdvJogTY%seT!T9S5?lho-QC>@?rwnv7Pmzn|9|hT zdavr8Jyknr>TFeS&-8SE{dI>cDM(?UzCi^507FJvTm=B&A^-q%jtqX8p#T-oy?nu2 zh{}rsKvgW-gE7KC(_baDzN*@re|0lIspVHqL7t(GOE%GVgvUf!_sRy2>y$`8M!(2s2$d<>I_{Tw^xyY%ts7uR*#q zcQcO>MT;b021_Wv{^x$IGw?dT%AcozJ>xSQ%rq!w<>pe=={(*bNdET)$XfyTLHSLM zijoG}qI5xS5^mR(T$uztJ-w%s)AtcMFL$9|()aHhPmj2=Bg6Msk^BziDG1na-wK%Y zo_6rF{QJuLLql1bgt7Kre5{{HfZf@a#C8QSbbI^4P3nJ=csahMjSKU6^p?0LE@r%W zJR2aBGh9PooSJGqU^HULSUrCK5`a3_v-~W@Zva-?v&{I*F>P5j#tv?s=n=WmT9Wg^}>$izZ{$$=( zK1u`s<}!~@KhFRC%Y#v2{bDOdh*d&(G9$}hP7Q;y$uHNmSvn5u*dfMGbFTkUjqBoDH7W!BFCNhPQu$|QN zUDRFq^ud<$d%3)OoDyo`n-yY>k}G>F%>O9Dg%P`MRZaaInO+t>f>$rqD`CYpKIxf0 z{c28q{WOSWlGXXKhihJ=-MVlc?{$JfreBE`{ytfK6^DKE6ii#QW1!-5A=rqFw^RR~ zu>CB!hgE)`?})w=n+;4Q#0%Xnc%txp^{b_z_`JigiWt=KtR;tt*=9szHx3eF{rp6-rg^ly zKUKC9e5ks+UqjC+SrZ&45jCv8L%c(eMFJl;Wl8&&9&JPsinKw(zcR*L%}z2q($oQfeRskJ<^4HXVx5|O400<`#b z{|x%}fxUO3IKi8FRy_OuG7H8gTkL_&0w7_3Dcx%AK0`){P~$reLKUoP@cfGH2+@;r zsm{_}Ly5tUV;#rj!ubLZU?T%xHnTyL!Ncu0?i!0t>=wK+X@WP*@`@zgf4_lIIKa7L zw3x?XA7dYnnF=hErt#%mFh{H<)*Sa;#I2~Ubo1T`Pg7fca??Vv$ zuMP(qJyKuWy?G5{$>A>%Z77A2ygm*Dak#t1&t7Tjnw^XSLpx~ zU=j>K7WXz{mZo?M=wCNUO`u1PhK`vq3a7a?GmYT8ShK{*>KcGi;4zF4AQ)i)I2*O{ zn>t+Cvz1%Pg|45%Xdb7_HN)e*+I8Ht-sE8f2JxZ4<9jMYF~CFvu3#v0rT*xO+o_<1{<+voCf; ze}Hb4=Ev@YhNrM8Yew;V??Gno$ofOm!0V424xyV%l*@I(cWaq)iXOl7f8~(+Q~Zi8 zr>Yt5oY#Ead{^&jFcg+6PQK`ohZ61?rZ-_>f$JaFh%1-z(K18$Rp;eX=G%eUrB>g& zGXeX#-5+z9@W2nNXuyQx2c_vQIw4ro87h5DZ4nc9X{s#=X31%++Qtpkd(+<_dHdt? z21olTMAx5?3iWjdf9%MJR3)czjclJ`FPN?#8D92^LR7f1(Xd&&_ zo0kla^17<1_AI*(=ANoKc0EeT2_BbppUuTzwz3lxTe)Y3wgjL=MyiHj07@Ayg;dN9 z22!P56gcccR*ql9!26CxI5b?÷o$p0gwdL$(xRi!+V+n+?Hu*fUC8C6%ZC9~<- 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

::value); - static_assert(std::is_same>::value); - static_assert(std::is_scalar::value, - "View of Array type must be of a scalar type"); - - public: - using specialize = Kokkos::Array<>; - - using dimension = typename array_analysis::dimension; - - private: - enum { - is_const = std::is_same::value - }; - - using array_scalar_dimension = typename dimension::template append::type; - - using scalar_type = std::conditional_t; - using non_const_scalar_type = V; - using const_scalar_type = const V; - - public: - using value_type = typename array_analysis::value_type; - using const_value_type = typename array_analysis::const_value_type; - using non_const_value_type = typename array_analysis::non_const_value_type; - - using type = typename ViewDataType::type; - using const_type = typename ViewDataType::type; - using non_const_type = - typename ViewDataType::type; - - using scalar_array_type = - typename ViewDataType::type; - using const_scalar_array_type = - typename ViewDataType::type; - using non_const_scalar_array_type = - typename ViewDataType::type; -}; - -} // namespace Impl -} // namespace Kokkos - -//---------------------------------------------------------------------------- -//---------------------------------------------------------------------------- - -namespace Kokkos { -namespace Impl { - -/** \brief View mapping for non-specialized data type and standard layout */ -template -class ViewMapping> { - private: - template - friend class ViewMapping; - template - friend class Kokkos::View; - - using offset_type = ViewOffset; - - using handle_type = typename Traits::value_type::pointer; - - handle_type m_impl_handle; - offset_type m_impl_offset; - size_t m_stride = 0; - - using scalar_type = typename Traits::value_type::value_type; - - using contiguous_reference = Kokkos::Array::contiguous>; - using strided_reference = - Kokkos::Array::strided>; - - enum { - is_contiguous_reference = - (Traits::rank == 0) || (std::is_same::value) - }; - - enum { Array_N = Traits::value_type::size() }; - enum { Array_S = is_contiguous_reference ? Array_N : 1 }; - - KOKKOS_INLINE_FUNCTION - ViewMapping(const handle_type &arg_handle, const offset_type &arg_offset) - : m_impl_handle(arg_handle), - m_impl_offset(arg_offset), - m_stride(is_contiguous_reference ? 0 : arg_offset.span()) {} - - public: - //---------------------------------------- - // Domain dimensions - - static constexpr unsigned Rank = Traits::dimension::rank; - - template - KOKKOS_INLINE_FUNCTION constexpr size_t extent(const iType &r) const { - return m_impl_offset.m_dim.extent(r); - } - - static KOKKOS_INLINE_FUNCTION constexpr size_t static_extent( - const unsigned r) noexcept { - using dim_type = typename offset_type::dimension_type; - return dim_type::static_extent(r); - } - - KOKKOS_INLINE_FUNCTION constexpr typename Traits::array_layout layout() - const { - return m_impl_offset.layout(); - } - - KOKKOS_INLINE_FUNCTION constexpr size_t dimension_0() const { - return m_impl_offset.dimension_0(); - } - KOKKOS_INLINE_FUNCTION constexpr size_t dimension_1() const { - return m_impl_offset.dimension_1(); - } - KOKKOS_INLINE_FUNCTION constexpr size_t dimension_2() const { - return m_impl_offset.dimension_2(); - } - KOKKOS_INLINE_FUNCTION constexpr size_t dimension_3() const { - return m_impl_offset.dimension_3(); - } - KOKKOS_INLINE_FUNCTION constexpr size_t dimension_4() const { - return m_impl_offset.dimension_4(); - } - KOKKOS_INLINE_FUNCTION constexpr size_t dimension_5() const { - return m_impl_offset.dimension_5(); - } - KOKKOS_INLINE_FUNCTION constexpr size_t dimension_6() const { - return m_impl_offset.dimension_6(); - } - KOKKOS_INLINE_FUNCTION constexpr size_t dimension_7() const { - return m_impl_offset.dimension_7(); - } - - // Is a regular layout with uniform striding for each index. - using is_regular = typename offset_type::is_regular; - - KOKKOS_INLINE_FUNCTION constexpr size_t stride_0() const { - return m_impl_offset.stride_0(); - } - KOKKOS_INLINE_FUNCTION constexpr size_t stride_1() const { - return m_impl_offset.stride_1(); - } - KOKKOS_INLINE_FUNCTION constexpr size_t stride_2() const { - return m_impl_offset.stride_2(); - } - KOKKOS_INLINE_FUNCTION constexpr size_t stride_3() const { - return m_impl_offset.stride_3(); - } - KOKKOS_INLINE_FUNCTION constexpr size_t stride_4() const { - return m_impl_offset.stride_4(); - } - KOKKOS_INLINE_FUNCTION constexpr size_t stride_5() const { - return m_impl_offset.stride_5(); - } - KOKKOS_INLINE_FUNCTION constexpr size_t stride_6() const { - return m_impl_offset.stride_6(); - } - KOKKOS_INLINE_FUNCTION constexpr size_t stride_7() const { - return m_impl_offset.stride_7(); - } - - //---------------------------------------- - // Range span - - /** \brief Span of the mapped range */ - KOKKOS_INLINE_FUNCTION constexpr size_t span() const { - return m_impl_offset.span() * Array_N; - } - - /** \brief Is the mapped range span contiguous */ - KOKKOS_INLINE_FUNCTION constexpr bool span_is_contiguous() const { - return m_impl_offset.span_is_contiguous(); - } - - using reference_type = - std::conditional_t; - - using pointer_type = handle_type; - - /** \brief If data references are lvalue_reference than can query pointer to - * memory */ - KOKKOS_INLINE_FUNCTION constexpr pointer_type data() const { - return m_impl_handle; - } - - //---------------------------------------- - // The View class performs all rank and bounds checking before - // calling these element reference methods. - - KOKKOS_FORCEINLINE_FUNCTION - reference_type reference() const { - return reference_type(m_impl_handle + 0, Array_N, 0); - } - - template - KOKKOS_FORCEINLINE_FUNCTION reference_type reference(const I0 &i0) const { - return reference_type(m_impl_handle + m_impl_offset(i0) * Array_S, Array_N, - m_stride); - } - - template - KOKKOS_FORCEINLINE_FUNCTION reference_type reference(const I0 &i0, - const I1 &i1) const { - return reference_type(m_impl_handle + m_impl_offset(i0, i1) * Array_S, - Array_N, m_stride); - } - - template - KOKKOS_FORCEINLINE_FUNCTION reference_type reference(const I0 &i0, - const I1 &i1, - const I2 &i2) const { - return reference_type(m_impl_handle + m_impl_offset(i0, i1, i2) * Array_S, - Array_N, m_stride); - } - - template - KOKKOS_FORCEINLINE_FUNCTION reference_type - reference(const I0 &i0, const I1 &i1, const I2 &i2, const I3 &i3) const { - return reference_type( - m_impl_handle + m_impl_offset(i0, i1, i2, i3) * Array_S, Array_N, - m_stride); - } - - template - KOKKOS_FORCEINLINE_FUNCTION reference_type reference(const I0 &i0, - const I1 &i1, - const I2 &i2, - const I3 &i3, - const I4 &i4) const { - return reference_type( - m_impl_handle + m_impl_offset(i0, i1, i2, i3, i4) * Array_S, Array_N, - m_stride); - } - - template - KOKKOS_FORCEINLINE_FUNCTION reference_type - reference(const I0 &i0, const I1 &i1, const I2 &i2, const I3 &i3, - const I4 &i4, const I5 &i5) const { - return reference_type( - m_impl_handle + m_impl_offset(i0, i1, i2, i3, i4, i5) * Array_S, - Array_N, m_stride); - } - - template - KOKKOS_FORCEINLINE_FUNCTION reference_type - reference(const I0 &i0, const I1 &i1, const I2 &i2, const I3 &i3, - const I4 &i4, const I5 &i5, const I6 &i6) const { - return reference_type( - m_impl_handle + m_impl_offset(i0, i1, i2, i3, i4, i5, i6) * Array_S, - Array_N, m_stride); - } - - template - KOKKOS_FORCEINLINE_FUNCTION reference_type - reference(const I0 &i0, const I1 &i1, const I2 &i2, const I3 &i3, - const I4 &i4, const I5 &i5, const I6 &i6, const I7 &i7) const { - return reference_type( - m_impl_handle + m_impl_offset(i0, i1, i2, i3, i4, i5, i6, i7) * Array_S, - Array_N, m_stride); - } - - //---------------------------------------- - - private: - enum { MemorySpanMask = 8 - 1 /* Force alignment on 8 byte boundary */ }; - enum { MemorySpanSize = sizeof(scalar_type) }; - - public: - /** \brief Span, in bytes, of the referenced memory */ - KOKKOS_INLINE_FUNCTION constexpr size_t memory_span() const { - return (m_impl_offset.span() * Array_N * MemorySpanSize + MemorySpanMask) & - ~size_t(MemorySpanMask); - } - - //---------------------------------------- - - KOKKOS_DEFAULTED_FUNCTION ViewMapping() = default; - - //---------------------------------------- - - template - KOKKOS_INLINE_FUNCTION ViewMapping(pointer_type ptr, Args... args) - : m_impl_handle(ptr), - m_impl_offset(std::integral_constant(), args...), - m_stride(m_impl_offset.span()) {} - - //---------------------------------------- - - template - Kokkos::Impl::SharedAllocationRecord<> *allocate_shared( - Kokkos::Impl::ViewCtorProp const &arg_prop, - typename Traits::array_layout const &arg_layout, - bool execution_space_specified) { - using alloc_prop = Kokkos::Impl::ViewCtorProp; - - using execution_space = typename alloc_prop::execution_space; - using memory_space = typename Traits::memory_space; - static_assert( - SpaceAccessibility::accessible); - using functor_type = - ViewValueFunctor; - using record_type = - Kokkos::Impl::SharedAllocationRecord; - - // Query the mapping for byte-size of allocation. - using padding = std::integral_constant< - unsigned int, alloc_prop::allow_padding ? sizeof(scalar_type) : 0>; - - m_impl_offset = offset_type(padding(), arg_layout); - - const size_t alloc_size = - (m_impl_offset.span() * Array_N * MemorySpanSize + MemorySpanMask) & - ~size_t(MemorySpanMask); - const auto &alloc_name = Impl::get_property(arg_prop); - const execution_space &exec_space = - Impl::get_property(arg_prop); - const memory_space &mem_space = - Impl::get_property(arg_prop); - - // Allocate memory from the memory space and create tracking record. - record_type *const record = - execution_space_specified - ? record_type::allocate(exec_space, mem_space, alloc_name, - alloc_size) - : record_type::allocate(mem_space, alloc_name, alloc_size); - - m_impl_handle = handle_type(reinterpret_cast(record->data())); - - functor_type functor = - execution_space_specified - ? functor_type(exec_space, (pointer_type)m_impl_handle, - m_impl_offset.span() * Array_N, alloc_name) - : functor_type((pointer_type)m_impl_handle, - m_impl_offset.span() * Array_N, alloc_name); - -#if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP) || \ - defined(KOKKOS_ENABLE_SYCL) || defined(KOKKOS_ENABLE_OPENMPTARGET) - if (false) { - // Make sure the destroy functor gets instantiated. - // This avoids "cudaErrorInvalidDeviceFunction"-type errors. - functor.destroy_shared_allocation(); - } -#endif - - // Only initialize if the allocation is non-zero. - // May be zero if one of the dimensions is zero. - if constexpr (alloc_prop::initialize) - if (alloc_size) { - // Assume destruction is only required when construction is requested. - // The ViewValueFunctor has both value construction and destruction - // operators. - record->m_destroy = std::move(functor); - - // Construct values - record->m_destroy.construct_shared_allocation(); - } - - return record; - } -}; - -/** \brief Assign Array to non-Array */ - -template -class ViewMapping< - DstTraits, SrcTraits, - std::enable_if_t<( - std::is_same::value && - std::is_void::value && - (std::is_same::value || - std::is_same::value || - std::is_same::value) && - std::is_same>::value && - (std::is_same::value || - std::is_same::value || - std::is_same::value))>> { - public: - // Can only convert to View::array_type - - enum { - is_assignable_data_type = - std::is_same::value && - (DstTraits::rank == SrcTraits::rank + 1) - }; - enum { - is_assignable = - std::is_same::value && - std::is_same::value - }; - - using TrackType = Kokkos::Impl::SharedAllocationTracker; - using DstType = ViewMapping; - using SrcType = ViewMapping>; - - KOKKOS_INLINE_FUNCTION - static void assign(DstType &dst, const SrcType &src, - const TrackType & /*src_track*/) { - static_assert(is_assignable, "Can only convert to array_type"); - - using dst_offset_type = typename DstType::offset_type; - - // Array dimension becomes the last dimension. - // Arguments beyond the destination rank are ignored. - if (src.span_is_contiguous()) { // not padded - dst.m_impl_offset = dst_offset_type( - std::integral_constant(), - typename DstTraits::array_layout( - (0 < SrcType::Rank ? src.dimension_0() - : SrcTraits::value_type::size()), - (1 < SrcType::Rank ? src.dimension_1() - : SrcTraits::value_type::size()), - (2 < SrcType::Rank ? src.dimension_2() - : SrcTraits::value_type::size()), - (3 < SrcType::Rank ? src.dimension_3() - : SrcTraits::value_type::size()), - (4 < SrcType::Rank ? src.dimension_4() - : SrcTraits::value_type::size()), - (5 < SrcType::Rank ? src.dimension_5() - : SrcTraits::value_type::size()), - (6 < SrcType::Rank ? src.dimension_6() - : SrcTraits::value_type::size()), - (7 < SrcType::Rank ? src.dimension_7() - : SrcTraits::value_type::size()))); - } else { // is padded - using padded = std::integral_constant< - unsigned int, sizeof(typename SrcTraits::value_type::value_type)>; - - dst.m_impl_offset = dst_offset_type( - padded(), typename DstTraits::array_layout( - (0 < SrcType::Rank ? src.dimension_0() - : SrcTraits::value_type::size()), - (1 < SrcType::Rank ? src.dimension_1() - : SrcTraits::value_type::size()), - (2 < SrcType::Rank ? src.dimension_2() - : SrcTraits::value_type::size()), - (3 < SrcType::Rank ? src.dimension_3() - : SrcTraits::value_type::size()), - (4 < SrcType::Rank ? src.dimension_4() - : SrcTraits::value_type::size()), - (5 < SrcType::Rank ? src.dimension_5() - : SrcTraits::value_type::size()), - (6 < SrcType::Rank ? src.dimension_6() - : SrcTraits::value_type::size()), - (7 < SrcType::Rank ? src.dimension_7() - : SrcTraits::value_type::size()))); - } - - dst.m_impl_handle = src.m_impl_handle; - } -}; - -//---------------------------------------------------------------------------- -//---------------------------------------------------------------------------- - -template -class ViewMapping< - std::enable_if_t<( - std::is_same>::value && - (std::is_same::value || - std::is_same::value || - std::is_same::value))>, - SrcTraits, Args...> { - private: - static_assert(SrcTraits::rank == sizeof...(Args)); - - enum : bool { - R0 = is_integral_extent<0, Args...>::value, - R1 = is_integral_extent<1, Args...>::value, - R2 = is_integral_extent<2, Args...>::value, - R3 = is_integral_extent<3, Args...>::value, - R4 = is_integral_extent<4, Args...>::value, - R5 = is_integral_extent<5, Args...>::value, - R6 = is_integral_extent<6, Args...>::value, - R7 = is_integral_extent<7, Args...>::value - }; - - enum { - rank = unsigned(R0) + unsigned(R1) + unsigned(R2) + unsigned(R3) + - unsigned(R4) + unsigned(R5) + unsigned(R6) + unsigned(R7) - }; - - // Whether right-most rank is a range. - enum { - R0_rev = - 0 == SrcTraits::rank - ? false - : (1 == SrcTraits::rank - ? R0 - : (2 == SrcTraits::rank - ? R1 - : (3 == SrcTraits::rank - ? R2 - : (4 == SrcTraits::rank - ? R3 - : (5 == SrcTraits::rank - ? R4 - : (6 == SrcTraits::rank - ? R5 - : (7 == SrcTraits::rank - ? R6 - : R7))))))) - }; - - // Subview's layout - using array_layout = - std::conditional_t<((rank == 0) || - (rank <= 2 && R0 && - std::is_same::value) || - (rank <= 2 && R0_rev && - std::is_same::value)), - typename SrcTraits::array_layout, - Kokkos::LayoutStride>; - - using value_type = typename SrcTraits::value_type; - - using data_type = std::conditional_t< - rank == 0, value_type, - std::conditional_t< - rank == 1, value_type *, - std::conditional_t< - rank == 2, value_type **, - std::conditional_t< - rank == 3, value_type ***, - std::conditional_t< - rank == 4, value_type ****, - std::conditional_t< - rank == 5, value_type *****, - std::conditional_t< - rank == 6, value_type ******, - std::conditional_t>>>>>>>; - - public: - using traits_type = Kokkos::ViewTraits; - - using type = - Kokkos::View; - - KOKKOS_INLINE_FUNCTION - static void assign(ViewMapping &dst, - ViewMapping const &src, Args... args) { - using DstType = ViewMapping; - - using dst_offset_type = typename DstType::offset_type; - using dst_handle_type = typename DstType::handle_type; - - const SubviewExtents extents(src.m_impl_offset.m_dim, - args...); - - dst.m_impl_offset = dst_offset_type(src.m_impl_offset, extents); - dst.m_impl_handle = dst_handle_type( - src.m_impl_handle + - src.m_impl_offset(extents.domain_offset(0), extents.domain_offset(1), - extents.domain_offset(2), extents.domain_offset(3), - extents.domain_offset(4), extents.domain_offset(5), - extents.domain_offset(6), extents.domain_offset(7))); - } -}; - -} // namespace Impl -} // namespace Kokkos - -//---------------------------------------------------------------------------- -//---------------------------------------------------------------------------- - -#endif /* #ifndef KOKKOS_EXPERIMENTAL_VIEW_ARRAY_MAPPING_HPP */ diff --git a/lib/kokkos/core/src/impl/Kokkos_ViewLayoutTiled.hpp b/lib/kokkos/core/src/impl/Kokkos_ViewLayoutTiled.hpp deleted file mode 100644 index 957717f973..0000000000 --- a/lib/kokkos/core/src/impl/Kokkos_ViewLayoutTiled.hpp +++ /dev/null @@ -1,1425 +0,0 @@ -//@HEADER -// ************************************************************************ -// -// Kokkos v. 4.0 -// Copyright (2022) National Technology & Engineering -// Solutions of Sandia, LLC (NTESS). -// -// Under the terms of Contract DE-NA0003525 with NTESS, -// the U.S. Government retains certain rights in this software. -// -// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. -// See https://kokkos.org/LICENSE for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//@HEADER - -#ifndef KOKKOS_EXPERIMENTAL_VIEWLAYOUTTILE_HPP -#define KOKKOS_EXPERIMENTAL_VIEWLAYOUTTILE_HPP - -#include -#include - -//---------------------------------------------------------------------------- -//---------------------------------------------------------------------------- - -namespace Kokkos { - -// View offset and mapping for tiled view's - -template -struct is_array_layout> - : public std::true_type {}; - -template -struct is_array_layout> - : public std::true_type {}; - -template -struct is_array_layout> - : public std::true_type {}; - -template -struct is_array_layout> - : public std::true_type {}; - -template -struct is_array_layout> - : public std::true_type {}; - -template -struct is_array_layout> - : public std::true_type {}; - -template -struct is_array_layout< - Kokkos::Experimental::LayoutTiled> - : public std::true_type {}; - -template -struct is_array_layout_tiled : public std::false_type {}; - -template -struct is_array_layout_tiled> : public std::true_type { -}; // Last template parameter "true" meaning this currently only supports - // powers-of-two - -namespace Impl { - -template -struct ViewOffset< - Dimension, Layout, - std::enable_if_t<((Dimension::rank <= 8) && (Dimension::rank >= 2) && - is_array_layout::value && - is_array_layout_tiled::value)>> { - public: - static constexpr Kokkos::Iterate outer_pattern = Layout::outer_pattern; - static constexpr Kokkos::Iterate inner_pattern = Layout::inner_pattern; - - static constexpr int VORank = Dimension::rank; - - static constexpr unsigned SHIFT_0 = - Kokkos::Impl::integral_power_of_two(Layout::N0); - static constexpr unsigned SHIFT_1 = - Kokkos::Impl::integral_power_of_two(Layout::N1); - static constexpr unsigned SHIFT_2 = - Kokkos::Impl::integral_power_of_two(Layout::N2); - static constexpr unsigned SHIFT_3 = - Kokkos::Impl::integral_power_of_two(Layout::N3); - static constexpr unsigned SHIFT_4 = - Kokkos::Impl::integral_power_of_two(Layout::N4); - static constexpr unsigned SHIFT_5 = - Kokkos::Impl::integral_power_of_two(Layout::N5); - static constexpr unsigned SHIFT_6 = - Kokkos::Impl::integral_power_of_two(Layout::N6); - static constexpr unsigned SHIFT_7 = - Kokkos::Impl::integral_power_of_two(Layout::N7); - static constexpr int MASK_0 = Layout::N0 - 1; - static constexpr int MASK_1 = Layout::N1 - 1; - static constexpr int MASK_2 = Layout::N2 - 1; - static constexpr int MASK_3 = Layout::N3 - 1; - static constexpr int MASK_4 = Layout::N4 - 1; - static constexpr int MASK_5 = Layout::N5 - 1; - static constexpr int MASK_6 = Layout::N6 - 1; - static constexpr int MASK_7 = Layout::N7 - 1; - - static constexpr unsigned SHIFT_2T = SHIFT_0 + SHIFT_1; - static constexpr unsigned SHIFT_3T = SHIFT_0 + SHIFT_1 + SHIFT_2; - static constexpr unsigned SHIFT_4T = SHIFT_0 + SHIFT_1 + SHIFT_2 + SHIFT_3; - static constexpr unsigned SHIFT_5T = - SHIFT_0 + SHIFT_1 + SHIFT_2 + SHIFT_3 + SHIFT_4; - static constexpr unsigned SHIFT_6T = - SHIFT_0 + SHIFT_1 + SHIFT_2 + SHIFT_3 + SHIFT_4 + SHIFT_5; - static constexpr unsigned SHIFT_7T = - SHIFT_0 + SHIFT_1 + SHIFT_2 + SHIFT_3 + SHIFT_4 + SHIFT_5 + SHIFT_6; - static constexpr unsigned SHIFT_8T = SHIFT_0 + SHIFT_1 + SHIFT_2 + SHIFT_3 + - SHIFT_4 + SHIFT_5 + SHIFT_6 + SHIFT_7; - - // Is an irregular layout that does not have uniform striding for each index. - using is_mapping_plugin = std::true_type; - using is_regular = std::false_type; - - using size_type = size_t; - using dimension_type = Dimension; - using array_layout = Layout; - - dimension_type m_dim; - size_type m_tile_N0; // Num tiles dim 0 - size_type m_tile_N1; - size_type m_tile_N2; - size_type m_tile_N3; - size_type m_tile_N4; - size_type m_tile_N5; - size_type m_tile_N6; - size_type m_tile_N7; - - //---------------------------------------- - -#define KOKKOS_IMPL_DEBUG_OUTPUT_CHECK 0 - - // Rank 2 - template - KOKKOS_INLINE_FUNCTION size_type operator()(I0 const& i0, - I1 const& i1) const { - auto tile_offset = - (outer_pattern == (Kokkos::Iterate::Left)) - ? (((i0 >> SHIFT_0) + m_tile_N0 * ((i1 >> SHIFT_1))) << SHIFT_2T) - : (((m_tile_N1 * (i0 >> SHIFT_0) + (i1 >> SHIFT_1))) << SHIFT_2T); - // ( num_tiles[1] * ti0 + ti1 ) * FTD - - auto local_offset = (inner_pattern == (Kokkos::Iterate::Left)) - ? ((i0 & MASK_0) + ((i1 & MASK_1) << SHIFT_0)) - : (((i0 & MASK_0) << SHIFT_1) + (i1 & MASK_1)); - // ( tile_dim[1] * li0 + li1 ) - -#if KOKKOS_IMPL_DEBUG_OUTPUT_CHECK - std::cout << "Am I Outer Left? " - << (outer_pattern == (Kokkos::Iterate::Left)) << std::endl; - std::cout << "Am I Inner Left? " - << (inner_pattern == (Kokkos::Iterate::Left)) << std::endl; - std::cout << "i0 = " << i0 << " i1 = " << i1 - << "\ntilei0 = " << (i0 >> SHIFT_0) - << " tilei1 = " << (i1 >> SHIFT_1) - << "locali0 = " << (i0 & MASK_0) - << "\nlocali1 = " << (i1 & MASK_1) << std::endl; -#endif - - return tile_offset + local_offset; - } - - // Rank 3 - template - KOKKOS_INLINE_FUNCTION size_type operator()(I0 const& i0, I1 const& i1, - I2 const& i2) const { - auto tile_offset = - (outer_pattern == Kokkos::Iterate::Left) - ? (((i0 >> SHIFT_0) + - m_tile_N0 * ((i1 >> SHIFT_1) + m_tile_N1 * (i2 >> SHIFT_2))) - << SHIFT_3T) - : ((m_tile_N2 * (m_tile_N1 * (i0 >> SHIFT_0) + (i1 >> SHIFT_1)) + - (i2 >> SHIFT_2)) - << SHIFT_3T); - - auto local_offset = (inner_pattern == Kokkos::Iterate::Left) - ? ((i0 & MASK_0) + ((i1 & MASK_1) << SHIFT_0) + - ((i2 & MASK_2) << (SHIFT_0 + SHIFT_1))) - : (((i0 & MASK_0) << (SHIFT_2 + SHIFT_1)) + - ((i1 & MASK_1) << (SHIFT_2)) + (i2 & MASK_2)); - -#if KOKKOS_IMPL_DEBUG_OUTPUT_CHECK - std::cout << "Am I Outer Left? " - << (outer_pattern == (Kokkos::Iterate::Left)) << std::endl; - std::cout << "Am I Inner Left? " - << (inner_pattern == (Kokkos::Iterate::Left)) << std::endl; - std::cout << "i0 = " << i0 << " i1 = " << i1 << " i2 = " << i2 - << "\ntilei0 = " << (i0 >> SHIFT_0) - << " tilei1 = " << (i1 >> SHIFT_1) - << " tilei2 = " << (i2 >> SHIFT_2) - << "\nlocali0 = " << (i0 & MASK_0) - << "locali1 = " << (i1 & MASK_1) << "locali2 = " << (i2 & MASK_2) - << std::endl; -#endif - - return tile_offset + local_offset; - } - - // Rank 4 - template - KOKKOS_INLINE_FUNCTION size_type operator()(I0 const& i0, I1 const& i1, - I2 const& i2, - I3 const& i3) const { - auto tile_offset = - (outer_pattern == Kokkos::Iterate::Left) - ? (((i0 >> SHIFT_0) + - m_tile_N0 * ((i1 >> SHIFT_1) + - m_tile_N1 * ((i2 >> SHIFT_2) + - m_tile_N2 * (i3 >> SHIFT_3)))) - << SHIFT_4T) - : ((m_tile_N3 * (m_tile_N2 * (m_tile_N1 * (i0 >> SHIFT_0) + - (i1 >> SHIFT_1)) + - (i2 >> SHIFT_2)) + - (i3 >> SHIFT_3)) - << SHIFT_4T); - - auto local_offset = - (inner_pattern == Kokkos::Iterate::Left) - ? ((i0 & MASK_0) + ((i1 & MASK_1) << SHIFT_0) + - ((i2 & MASK_2) << (SHIFT_0 + SHIFT_1)) + - ((i3 & MASK_3) << (SHIFT_0 + SHIFT_1 + SHIFT_2))) - : (((i0 & MASK_0) << (SHIFT_3 + SHIFT_2 + SHIFT_1)) + - ((i1 & MASK_1) << (SHIFT_3 + SHIFT_2)) + - ((i2 & MASK_2) << (SHIFT_3)) + (i3 & MASK_3)); - - return tile_offset + local_offset; - } - - // Rank 5 - template - KOKKOS_INLINE_FUNCTION size_type operator()(I0 const& i0, I1 const& i1, - I2 const& i2, I3 const& i3, - I4 const& i4) const { - auto tile_offset = - (outer_pattern == Kokkos::Iterate::Left) - ? (((i0 >> SHIFT_0) + - m_tile_N0 * - ((i1 >> SHIFT_1) + - m_tile_N1 * ((i2 >> SHIFT_2) + - m_tile_N2 * ((i3 >> SHIFT_3) + - m_tile_N3 * (i4 >> SHIFT_4))))) - << SHIFT_5T) - : ((m_tile_N4 * - (m_tile_N3 * (m_tile_N2 * (m_tile_N1 * (i0 >> SHIFT_0) + - (i1 >> SHIFT_1)) + - (i2 >> SHIFT_2)) + - (i3 >> SHIFT_3)) + - (i4 >> SHIFT_4)) - << SHIFT_5T); - - auto local_offset = - (inner_pattern == Kokkos::Iterate::Left) - ? ((i0 & MASK_0) + ((i1 & MASK_1) << SHIFT_0) + - ((i2 & MASK_2) << (SHIFT_0 + SHIFT_1)) + - ((i3 & MASK_3) << (SHIFT_0 + SHIFT_1 + SHIFT_2)) + - ((i4 & MASK_4) << (SHIFT_0 + SHIFT_1 + SHIFT_2 + SHIFT_3))) - : (((i0 & MASK_0) << (SHIFT_4 + SHIFT_3 + SHIFT_2 + SHIFT_1)) + - ((i1 & MASK_1) << (SHIFT_4 + SHIFT_3 + SHIFT_2)) + - ((i2 & MASK_2) << (SHIFT_4 + SHIFT_3)) + - ((i3 & MASK_3) << (SHIFT_4)) + (i4 & MASK_4)); - - return tile_offset + local_offset; - } - - // Rank 6 - template - KOKKOS_INLINE_FUNCTION size_type operator()(I0 const& i0, I1 const& i1, - I2 const& i2, I3 const& i3, - I4 const& i4, - I5 const& i5) const { - auto tile_offset = - (outer_pattern == Kokkos::Iterate::Left) - ? (((i0 >> SHIFT_0) + - m_tile_N0 * - ((i1 >> SHIFT_1) + - m_tile_N1 * - ((i2 >> SHIFT_2) + - m_tile_N2 * - ((i3 >> SHIFT_3) + - m_tile_N3 * ((i4 >> SHIFT_4) + - m_tile_N4 * (i5 >> SHIFT_5)))))) - << SHIFT_6T) - : ((m_tile_N5 * - (m_tile_N4 * - (m_tile_N3 * - (m_tile_N2 * (m_tile_N1 * (i0 >> SHIFT_0) + - (i1 >> SHIFT_1)) + - (i2 >> SHIFT_2)) + - (i3 >> SHIFT_3)) + - (i4 >> SHIFT_4)) + - (i5 >> SHIFT_5)) - << SHIFT_6T); - - auto local_offset = - (inner_pattern == Kokkos::Iterate::Left) - ? ((i0 & MASK_0) + ((i1 & MASK_1) << SHIFT_0) + - ((i2 & MASK_2) << (SHIFT_0 + SHIFT_1)) + - ((i3 & MASK_3) << (SHIFT_0 + SHIFT_1 + SHIFT_2)) + - ((i4 & MASK_4) << (SHIFT_0 + SHIFT_1 + SHIFT_2 + SHIFT_3)) + - ((i5 & MASK_5) - << (SHIFT_0 + SHIFT_1 + SHIFT_2 + SHIFT_3 + SHIFT_4))) - : (((i0 & MASK_0) - << (SHIFT_5 + SHIFT_4 + SHIFT_3 + SHIFT_2 + SHIFT_1)) + - ((i1 & MASK_1) << (SHIFT_5 + SHIFT_4 + SHIFT_3 + SHIFT_2)) + - ((i2 & MASK_2) << (SHIFT_5 + SHIFT_4 + SHIFT_3)) + - ((i3 & MASK_3) << (SHIFT_5 + SHIFT_4)) + - ((i4 & MASK_4) << (SHIFT_5)) + (i5 & MASK_5)); - - return tile_offset + local_offset; - } - - // Rank 7 - template - KOKKOS_INLINE_FUNCTION size_type operator()(I0 const& i0, I1 const& i1, - I2 const& i2, I3 const& i3, - I4 const& i4, I5 const& i5, - I6 const& i6) const { - auto tile_offset = - (outer_pattern == Kokkos::Iterate::Left) - ? (((i0 >> SHIFT_0) + - m_tile_N0 * - ((i1 >> SHIFT_1) + - m_tile_N1 * - ((i2 >> SHIFT_2) + - m_tile_N2 * - ((i3 >> SHIFT_3) + - m_tile_N3 * - ((i4 >> SHIFT_4) + - m_tile_N4 * - ((i5 >> SHIFT_5) + - m_tile_N5 * (i6 >> SHIFT_6))))))) - << SHIFT_7T) - : ((m_tile_N6 * - (m_tile_N5 * - (m_tile_N4 * - (m_tile_N3 * - (m_tile_N2 * (m_tile_N1 * (i0 >> SHIFT_0) + - (i1 >> SHIFT_1)) + - (i2 >> SHIFT_2)) + - (i3 >> SHIFT_3)) + - (i4 >> SHIFT_4)) + - (i5 >> SHIFT_5)) + - (i6 >> SHIFT_6)) - << SHIFT_7T); - - auto local_offset = - (inner_pattern == Kokkos::Iterate::Left) - ? ((i0 & MASK_0) + ((i1 & MASK_1) << SHIFT_0) + - ((i2 & MASK_2) << (SHIFT_0 + SHIFT_1)) + - ((i3 & MASK_3) << (SHIFT_0 + SHIFT_1 + SHIFT_2)) + - ((i4 & MASK_4) << (SHIFT_0 + SHIFT_1 + SHIFT_2 + SHIFT_3)) + - ((i5 & MASK_5) - << (SHIFT_0 + SHIFT_1 + SHIFT_2 + SHIFT_3 + SHIFT_4)) + - ((i6 & MASK_6) - << (SHIFT_0 + SHIFT_1 + SHIFT_2 + SHIFT_3 + SHIFT_4 + SHIFT_5))) - : (((i0 & MASK_0) << (SHIFT_6 + SHIFT_5 + SHIFT_4 + SHIFT_3 + - SHIFT_2 + SHIFT_1)) + - ((i1 & MASK_1) - << (SHIFT_6 + SHIFT_5 + SHIFT_4 + SHIFT_3 + SHIFT_2)) + - ((i2 & MASK_2) << (SHIFT_6 + SHIFT_5 + SHIFT_4 + SHIFT_3)) + - ((i3 & MASK_3) << (SHIFT_6 + SHIFT_5 + SHIFT_4)) + - ((i4 & MASK_4) << (SHIFT_6 + SHIFT_5)) + - ((i5 & MASK_5) << (SHIFT_6)) + (i6 & MASK_6)); - - return tile_offset + local_offset; - } - - // Rank 8 - template - KOKKOS_INLINE_FUNCTION size_type operator()(I0 const& i0, I1 const& i1, - I2 const& i2, I3 const& i3, - I4 const& i4, I5 const& i5, - I6 const& i6, - I7 const& i7) const { - auto tile_offset = - (outer_pattern == Kokkos::Iterate::Left) - ? (((i0 >> SHIFT_0) + - m_tile_N0 * - ((i1 >> SHIFT_1) + - m_tile_N1 * - ((i2 >> SHIFT_2) + - m_tile_N2 * - ((i3 >> SHIFT_3) + - m_tile_N3 * - ((i4 >> SHIFT_4) + - m_tile_N4 * - ((i5 >> SHIFT_5) + - m_tile_N5 * - ((i6 >> SHIFT_6) + - m_tile_N6 * (i7 >> SHIFT_7)))))))) - << SHIFT_8T) - : ((m_tile_N7 * - (m_tile_N6 * - (m_tile_N5 * - (m_tile_N4 * - (m_tile_N3 * - (m_tile_N2 * - (m_tile_N1 * (i0 >> SHIFT_0) + - (i1 >> SHIFT_1)) + - (i2 >> SHIFT_2)) + - (i3 >> SHIFT_3)) + - (i4 >> SHIFT_4)) + - (i5 >> SHIFT_5)) + - (i6 >> SHIFT_6)) + - (i7 >> SHIFT_7)) - << SHIFT_8T); - - auto local_offset = - (inner_pattern == Kokkos::Iterate::Left) - ? ((i0 & MASK_0) + ((i1 & MASK_1) << SHIFT_0) + - ((i2 & MASK_2) << (SHIFT_0 + SHIFT_1)) + - ((i3 & MASK_3) << (SHIFT_0 + SHIFT_1 + SHIFT_2)) + - ((i4 & MASK_4) << (SHIFT_0 + SHIFT_1 + SHIFT_2 + SHIFT_3)) + - ((i5 & MASK_5) - << (SHIFT_0 + SHIFT_1 + SHIFT_2 + SHIFT_3 + SHIFT_4)) + - ((i6 & MASK_6) << (SHIFT_0 + SHIFT_1 + SHIFT_2 + SHIFT_3 + - SHIFT_4 + SHIFT_5)) + - ((i7 & MASK_7) << (SHIFT_0 + SHIFT_1 + SHIFT_2 + SHIFT_3 + - SHIFT_4 + SHIFT_5 + SHIFT_6))) - : (((i0 & MASK_0) << (SHIFT_7 + SHIFT_6 + SHIFT_5 + SHIFT_4 + - SHIFT_3 + SHIFT_2 + SHIFT_1)) + - ((i1 & MASK_1) << (SHIFT_7 + SHIFT_6 + SHIFT_5 + SHIFT_4 + - SHIFT_3 + SHIFT_2)) + - ((i2 & MASK_2) - << (SHIFT_7 + SHIFT_6 + SHIFT_5 + SHIFT_4 + SHIFT_3)) + - ((i3 & MASK_3) << (SHIFT_7 + SHIFT_6 + SHIFT_5 + SHIFT_4)) + - ((i4 & MASK_4) << (SHIFT_7 + SHIFT_6 + SHIFT_5)) + - ((i5 & MASK_5) << (SHIFT_7 + SHIFT_6)) + - ((i6 & MASK_6) << (SHIFT_7)) + (i7 & MASK_7)); - - return tile_offset + local_offset; - } - - //---------------------------------------- - - KOKKOS_INLINE_FUNCTION constexpr array_layout layout() const { - return array_layout((VORank > 0 ? m_dim.N0 : KOKKOS_INVALID_INDEX), - (VORank > 1 ? m_dim.N1 : KOKKOS_INVALID_INDEX), - (VORank > 2 ? m_dim.N2 : KOKKOS_INVALID_INDEX), - (VORank > 3 ? m_dim.N3 : KOKKOS_INVALID_INDEX), - (VORank > 4 ? m_dim.N4 : KOKKOS_INVALID_INDEX), - (VORank > 5 ? m_dim.N5 : KOKKOS_INVALID_INDEX), - (VORank > 6 ? m_dim.N6 : KOKKOS_INVALID_INDEX), - (VORank > 7 ? m_dim.N7 : KOKKOS_INVALID_INDEX)); - } - - KOKKOS_INLINE_FUNCTION constexpr size_type dimension_0() const { - return m_dim.N0; - } - KOKKOS_INLINE_FUNCTION constexpr size_type dimension_1() const { - return m_dim.N1; - } - KOKKOS_INLINE_FUNCTION constexpr size_type dimension_2() const { - return m_dim.N2; - } - KOKKOS_INLINE_FUNCTION constexpr size_type dimension_3() const { - return m_dim.N3; - } - KOKKOS_INLINE_FUNCTION constexpr size_type dimension_4() const { - return m_dim.N4; - } - KOKKOS_INLINE_FUNCTION constexpr size_type dimension_5() const { - return m_dim.N5; - } - KOKKOS_INLINE_FUNCTION constexpr size_type dimension_6() const { - return m_dim.N6; - } - KOKKOS_INLINE_FUNCTION constexpr size_type dimension_7() const { - return m_dim.N7; - } - - KOKKOS_INLINE_FUNCTION constexpr size_type size() const { - return m_dim.N0 * m_dim.N1 * m_dim.N2 * m_dim.N3 * m_dim.N4 * m_dim.N5 * - m_dim.N6 * m_dim.N7; - } - - // Strides are meaningless due to irregularity - KOKKOS_INLINE_FUNCTION constexpr size_type stride_0() const { return 0; } - KOKKOS_INLINE_FUNCTION constexpr size_type stride_1() const { return 0; } - KOKKOS_INLINE_FUNCTION constexpr size_type stride_2() const { return 0; } - KOKKOS_INLINE_FUNCTION constexpr size_type stride_3() const { return 0; } - KOKKOS_INLINE_FUNCTION constexpr size_type stride_4() const { return 0; } - KOKKOS_INLINE_FUNCTION constexpr size_type stride_5() const { return 0; } - KOKKOS_INLINE_FUNCTION constexpr size_type stride_6() const { return 0; } - KOKKOS_INLINE_FUNCTION constexpr size_type stride_7() const { return 0; } - - // Stride with [ rank ] value is the total length - template - KOKKOS_INLINE_FUNCTION void stride(iType* const s) const { - s[0] = 0; - if (0 < dimension_type::rank) { - s[1] = 0; - } - if (1 < dimension_type::rank) { - s[2] = 0; - } - if (2 < dimension_type::rank) { - s[3] = 0; - } - if (3 < dimension_type::rank) { - s[4] = 0; - } - if (4 < dimension_type::rank) { - s[5] = 0; - } - if (5 < dimension_type::rank) { - s[6] = 0; - } - if (6 < dimension_type::rank) { - s[7] = 0; - } - if (7 < dimension_type::rank) { - s[8] = 0; - } - } - - KOKKOS_INLINE_FUNCTION constexpr size_type span() const { - // Rank2: ( NumTile0 * ( NumTile1 ) ) * TileSize, etc - return (VORank == 2) - ? (m_tile_N0 * m_tile_N1) << SHIFT_2T - : (VORank == 3) - ? (m_tile_N0 * m_tile_N1 * m_tile_N2) << SHIFT_3T - : (VORank == 4) - ? (m_tile_N0 * m_tile_N1 * m_tile_N2 * m_tile_N3) - << SHIFT_4T - : (VORank == 5) - ? (m_tile_N0 * m_tile_N1 * m_tile_N2 * - m_tile_N3 * m_tile_N4) - << SHIFT_5T - : (VORank == 6) - ? (m_tile_N0 * m_tile_N1 * m_tile_N2 * - m_tile_N3 * m_tile_N4 * m_tile_N5) - << SHIFT_6T - : (VORank == 7) - ? (m_tile_N0 * m_tile_N1 * - m_tile_N2 * m_tile_N3 * - m_tile_N4 * m_tile_N5 * - m_tile_N6) - << SHIFT_7T - : (m_tile_N0 * m_tile_N1 * - m_tile_N2 * m_tile_N3 * - m_tile_N4 * m_tile_N5 * - m_tile_N6 * m_tile_N7) - << SHIFT_8T; - } - - KOKKOS_INLINE_FUNCTION constexpr bool span_is_contiguous() const { - return true; - } - - //---------------------------------------- -#ifdef KOKKOS_IMPL_WINDOWS_CUDA - KOKKOS_FUNCTION ViewOffset() {} - KOKKOS_FUNCTION ViewOffset(const ViewOffset& src) { - m_dim = src.m_dim; - m_tile_N0 = src.m_tile_N0; - m_tile_N1 = src.m_tile_N1; - m_tile_N2 = src.m_tile_N2; - m_tile_N3 = src.m_tile_N3; - m_tile_N4 = src.m_tile_N4; - m_tile_N5 = src.m_tile_N5; - m_tile_N6 = src.m_tile_N6; - m_tile_N7 = src.m_tile_N7; - } - KOKKOS_FUNCTION ViewOffset& operator=(const ViewOffset& src) { - m_dim = src.m_dim; - m_tile_N0 = src.m_tile_N0; - m_tile_N1 = src.m_tile_N1; - m_tile_N2 = src.m_tile_N2; - m_tile_N3 = src.m_tile_N3; - m_tile_N4 = src.m_tile_N4; - m_tile_N5 = src.m_tile_N5; - m_tile_N6 = src.m_tile_N6; - m_tile_N7 = src.m_tile_N7; - return *this; - } -#else - KOKKOS_DEFAULTED_FUNCTION ~ViewOffset() = default; - KOKKOS_DEFAULTED_FUNCTION ViewOffset() = default; - KOKKOS_DEFAULTED_FUNCTION ViewOffset(const ViewOffset&) = default; - KOKKOS_DEFAULTED_FUNCTION ViewOffset& operator=(const ViewOffset&) = default; -#endif - - template - KOKKOS_INLINE_FUNCTION constexpr ViewOffset( - std::integral_constant const&, - array_layout const arg_layout) - : m_dim(arg_layout.dimension[0], arg_layout.dimension[1], - arg_layout.dimension[2], arg_layout.dimension[3], - arg_layout.dimension[4], arg_layout.dimension[5], - arg_layout.dimension[6], arg_layout.dimension[7]), - m_tile_N0((arg_layout.dimension[0] + MASK_0) >> - SHIFT_0 /* number of tiles in first dimension */), - m_tile_N1((arg_layout.dimension[1] + MASK_1) >> SHIFT_1), - m_tile_N2((VORank > 2) ? (arg_layout.dimension[2] + MASK_2) >> SHIFT_2 - : 0), - m_tile_N3((VORank > 3) ? (arg_layout.dimension[3] + MASK_3) >> SHIFT_3 - : 0), - m_tile_N4((VORank > 4) ? (arg_layout.dimension[4] + MASK_4) >> SHIFT_4 - : 0), - m_tile_N5((VORank > 5) ? (arg_layout.dimension[5] + MASK_5) >> SHIFT_5 - : 0), - m_tile_N6((VORank > 6) ? (arg_layout.dimension[6] + MASK_6) >> SHIFT_6 - : 0), - m_tile_N7((VORank > 7) ? (arg_layout.dimension[7] + MASK_7) >> SHIFT_7 - : 0) {} -}; - -// FIXME Remove the out-of-class definitions when we require C++17 -#define KOKKOS_ITERATE_VIEW_OFFSET_ENABLE \ - std::enable_if_t<((Dimension::rank <= 8) && (Dimension::rank >= 2) && \ - is_array_layout::value && \ - is_array_layout_tiled::value)> -template -constexpr Kokkos::Iterate ViewOffset< - Dimension, Layout, KOKKOS_ITERATE_VIEW_OFFSET_ENABLE>::outer_pattern; -template -constexpr Kokkos::Iterate ViewOffset< - Dimension, Layout, KOKKOS_ITERATE_VIEW_OFFSET_ENABLE>::inner_pattern; -template -constexpr int - ViewOffset::VORank; -template -constexpr unsigned - ViewOffset::SHIFT_0; -template -constexpr unsigned - ViewOffset::SHIFT_1; -template -constexpr unsigned - ViewOffset::SHIFT_2; -template -constexpr unsigned - ViewOffset::SHIFT_3; -template -constexpr unsigned - ViewOffset::SHIFT_4; -template -constexpr unsigned - ViewOffset::SHIFT_5; -template -constexpr unsigned - ViewOffset::SHIFT_6; -template -constexpr unsigned - ViewOffset::SHIFT_7; -template -constexpr int - ViewOffset::MASK_0; -template -constexpr int - ViewOffset::MASK_1; -template -constexpr int - ViewOffset::MASK_2; -template -constexpr int - ViewOffset::MASK_3; -template -constexpr int - ViewOffset::MASK_4; -template -constexpr int - ViewOffset::MASK_5; -template -constexpr int - ViewOffset::MASK_6; -template -constexpr int - ViewOffset::MASK_7; -template -constexpr unsigned - ViewOffset::SHIFT_2T; -template -constexpr unsigned - ViewOffset::SHIFT_3T; -template -constexpr unsigned - ViewOffset::SHIFT_4T; -template -constexpr unsigned - ViewOffset::SHIFT_5T; -template -constexpr unsigned - ViewOffset::SHIFT_6T; -template -constexpr unsigned - ViewOffset::SHIFT_7T; -template -constexpr unsigned - ViewOffset::SHIFT_8T; -#undef KOKKOS_ITERATE_VIEW_OFFSET_ENABLE - -//---------------------------------------- - -// ViewMapping assign method needed in order to return a 'subview' tile as a -// proper View The outer iteration pattern determines the mapping of the pointer -// offset to the beginning of requested tile The inner iteration pattern is -// needed for the layout of the tile's View to be returned Rank 2 -template -class ViewMapping // void - , - Kokkos::ViewTraits< - T**, - Kokkos::Experimental::LayoutTiled< - OuterP, InnerP, N0, N1, N2, N3, N4, N5, N6, N7, true>, - P...>, - Kokkos::Experimental::LayoutTiled, - iType0, iType1> { - public: - using src_layout = - Kokkos::Experimental::LayoutTiled; - using src_traits = Kokkos::ViewTraits; - - static constexpr bool is_outer_left = (OuterP == Kokkos::Iterate::Left); - static constexpr bool is_inner_left = (InnerP == Kokkos::Iterate::Left); - using array_layout = std::conditional_t; - using traits = Kokkos::ViewTraits; - using type = Kokkos::View; - - KOKKOS_INLINE_FUNCTION static void assign( - ViewMapping& dst, const ViewMapping& src, - const src_layout&, const iType0 i_tile0, const iType1 i_tile1) { - using dst_map_type = ViewMapping; - using src_map_type = ViewMapping; - using dst_handle_type = typename dst_map_type::handle_type; - using dst_offset_type = typename dst_map_type::offset_type; - using src_offset_type = typename src_map_type::offset_type; - - dst = dst_map_type( - dst_handle_type( - src.m_impl_handle + - (is_outer_left ? ((i_tile0 + src.m_impl_offset.m_tile_N0 * i_tile1) - << src_offset_type::SHIFT_2T) - : ((src.m_impl_offset.m_tile_N1 * i_tile0 + i_tile1) - << src_offset_type::SHIFT_2T)) // offset to start - // of the tile - ), - dst_offset_type()); - } -}; - -// Rank 3 -template -class ViewMapping // void - , - Kokkos::ViewTraits< - T***, - Kokkos::Experimental::LayoutTiled< - OuterP, InnerP, N0, N1, N2, N3, N4, N5, N6, N7, true>, - P...>, - Kokkos::Experimental::LayoutTiled, - iType0, iType1, iType2> { - public: - using src_layout = - Kokkos::Experimental::LayoutTiled; - using src_traits = Kokkos::ViewTraits; - - static constexpr bool is_outer_left = (OuterP == Kokkos::Iterate::Left); - static constexpr bool is_inner_left = (InnerP == Kokkos::Iterate::Left); - using array_layout = std::conditional_t; - using traits = Kokkos::ViewTraits; - using type = Kokkos::View; - - KOKKOS_INLINE_FUNCTION static void assign( - ViewMapping& dst, const ViewMapping& src, - const src_layout&, const iType0 i_tile0, const iType1 i_tile1, - const iType2 i_tile2) { - using dst_map_type = ViewMapping; - using src_map_type = ViewMapping; - using dst_handle_type = typename dst_map_type::handle_type; - using dst_offset_type = typename dst_map_type::offset_type; - using src_offset_type = typename src_map_type::offset_type; - - dst = dst_map_type( - dst_handle_type( - src.m_impl_handle + - (is_outer_left - ? ((i_tile0 + - src.m_impl_offset.m_tile_N0 * - (i_tile1 + src.m_impl_offset.m_tile_N1 * i_tile2)) - << src_offset_type::SHIFT_3T) - : ((src.m_impl_offset.m_tile_N2 * - (src.m_impl_offset.m_tile_N1 * i_tile0 + i_tile1) + - i_tile2) - << src_offset_type::SHIFT_3T))) // offset to start of the - // tile - , - dst_offset_type()); - } -}; - -// Rank 4 -template -class ViewMapping< - std::enable_if_t<(N4 == 0 && N5 == 0 && N6 == 0 && N7 == 0)> // void - , - Kokkos::ViewTraits< - T****, - Kokkos::Experimental::LayoutTiled, - P...>, - Kokkos::Experimental::LayoutTiled, - iType0, iType1, iType2, iType3> { - public: - using src_layout = - Kokkos::Experimental::LayoutTiled; - using src_traits = Kokkos::ViewTraits; - - static constexpr bool is_outer_left = (OuterP == Kokkos::Iterate::Left); - static constexpr bool is_inner_left = (InnerP == Kokkos::Iterate::Left); - using array_layout = std::conditional_t; - using traits = Kokkos::ViewTraits; - using type = Kokkos::View; - - KOKKOS_INLINE_FUNCTION static void assign( - ViewMapping& dst, const ViewMapping& src, - const src_layout&, const iType0 i_tile0, const iType1 i_tile1, - const iType2 i_tile2, const iType3 i_tile3) { - using dst_map_type = ViewMapping; - using src_map_type = ViewMapping; - using dst_handle_type = typename dst_map_type::handle_type; - using dst_offset_type = typename dst_map_type::offset_type; - using src_offset_type = typename src_map_type::offset_type; - - dst = dst_map_type( - dst_handle_type( - src.m_impl_handle + - (is_outer_left - ? ((i_tile0 + - src.m_impl_offset.m_tile_N0 * - (i_tile1 + src.m_impl_offset.m_tile_N1 * - (i_tile2 + src.m_impl_offset.m_tile_N2 * - i_tile3))) - << src_offset_type::SHIFT_4T) - : ((src.m_impl_offset.m_tile_N3 * - (src.m_impl_offset.m_tile_N2 * - (src.m_impl_offset.m_tile_N1 * i_tile0 + - i_tile1) + - i_tile2) + - i_tile3) - << src_offset_type::SHIFT_4T))) // offset to start of the - // tile - , - dst_offset_type()); - } -}; - -// Rank 5 -template -class ViewMapping // void - , - Kokkos::ViewTraits< - T*****, - Kokkos::Experimental::LayoutTiled< - OuterP, InnerP, N0, N1, N2, N3, N4, N5, N6, N7, true>, - P...>, - Kokkos::Experimental::LayoutTiled, - iType0, iType1, iType2, iType3, iType4> { - public: - using src_layout = - Kokkos::Experimental::LayoutTiled; - using src_traits = Kokkos::ViewTraits; - - static constexpr bool is_outer_left = (OuterP == Kokkos::Iterate::Left); - static constexpr bool is_inner_left = (InnerP == Kokkos::Iterate::Left); - using array_layout = std::conditional_t; - using traits = Kokkos::ViewTraits; - using type = Kokkos::View; - - KOKKOS_INLINE_FUNCTION static void assign( - ViewMapping& dst, const ViewMapping& src, - const src_layout&, const iType0 i_tile0, const iType1 i_tile1, - const iType2 i_tile2, const iType3 i_tile3, const iType4 i_tile4) { - using dst_map_type = ViewMapping; - using src_map_type = ViewMapping; - using dst_handle_type = typename dst_map_type::handle_type; - using dst_offset_type = typename dst_map_type::offset_type; - using src_offset_type = typename src_map_type::offset_type; - - dst = dst_map_type( - dst_handle_type( - src.m_impl_handle + - (is_outer_left - ? ((i_tile0 + - src.m_impl_offset.m_tile_N0 * - (i_tile1 + - src.m_impl_offset.m_tile_N1 * - (i_tile2 + - src.m_impl_offset.m_tile_N2 * - (i_tile3 + - src.m_impl_offset.m_tile_N3 * i_tile4)))) - << src_offset_type::SHIFT_5T) - : ((src.m_impl_offset.m_tile_N4 * - (src.m_impl_offset.m_tile_N3 * - (src.m_impl_offset.m_tile_N2 * - (src.m_impl_offset.m_tile_N1 * i_tile0 + - i_tile1) + - i_tile2) + - i_tile3) + - i_tile4) - << src_offset_type::SHIFT_5T))) // offset to start of the - // tile - , - dst_offset_type()); - } -}; - -// Rank 6 -template -class ViewMapping // void - , - Kokkos::ViewTraits< - T******, - Kokkos::Experimental::LayoutTiled< - OuterP, InnerP, N0, N1, N2, N3, N4, N5, N6, N7, true>, - P...>, - Kokkos::Experimental::LayoutTiled, - iType0, iType1, iType2, iType3, iType4, iType5> { - public: - using src_layout = - Kokkos::Experimental::LayoutTiled; - using src_traits = Kokkos::ViewTraits; - - static constexpr bool is_outer_left = (OuterP == Kokkos::Iterate::Left); - static constexpr bool is_inner_left = (InnerP == Kokkos::Iterate::Left); - using array_layout = std::conditional_t; - using traits = - Kokkos::ViewTraits; - using type = Kokkos::View; - - KOKKOS_INLINE_FUNCTION static void assign( - ViewMapping& dst, const ViewMapping& src, - const src_layout&, const iType0 i_tile0, const iType1 i_tile1, - const iType2 i_tile2, const iType3 i_tile3, const iType4 i_tile4, - const iType5 i_tile5) { - using dst_map_type = ViewMapping; - using src_map_type = ViewMapping; - using dst_handle_type = typename dst_map_type::handle_type; - using dst_offset_type = typename dst_map_type::offset_type; - using src_offset_type = typename src_map_type::offset_type; - - dst = dst_map_type( - dst_handle_type( - src.m_impl_handle + - (is_outer_left - ? ((i_tile0 + - src.m_impl_offset.m_tile_N0 * - (i_tile1 + - src.m_impl_offset.m_tile_N1 * - (i_tile2 + - src.m_impl_offset.m_tile_N2 * - (i_tile3 + - src.m_impl_offset.m_tile_N3 * - (i_tile4 + src.m_impl_offset.m_tile_N4 * - i_tile5))))) - << src_offset_type::SHIFT_6T) - : ((src.m_impl_offset.m_tile_N5 * - (src.m_impl_offset.m_tile_N4 * - (src.m_impl_offset.m_tile_N3 * - (src.m_impl_offset.m_tile_N2 * - (src.m_impl_offset.m_tile_N1 * i_tile0 + - i_tile1) + - i_tile2) + - i_tile3) + - i_tile4) + - i_tile5) - << src_offset_type::SHIFT_6T))) // offset to start of the - // tile - , - dst_offset_type()); - } -}; - -// Rank 7 -template -class ViewMapping // void - , - Kokkos::ViewTraits< - T*******, - Kokkos::Experimental::LayoutTiled< - OuterP, InnerP, N0, N1, N2, N3, N4, N5, N6, N7, true>, - P...>, - Kokkos::Experimental::LayoutTiled, - iType0, iType1, iType2, iType3, iType4, iType5, iType6> { - public: - using src_layout = - Kokkos::Experimental::LayoutTiled; - using src_traits = Kokkos::ViewTraits; - - static constexpr bool is_outer_left = (OuterP == Kokkos::Iterate::Left); - static constexpr bool is_inner_left = (InnerP == Kokkos::Iterate::Left); - using array_layout = std::conditional_t; - using traits = - Kokkos::ViewTraits; - using type = Kokkos::View; - - KOKKOS_INLINE_FUNCTION static void assign( - ViewMapping& dst, const ViewMapping& src, - const src_layout&, const iType0 i_tile0, const iType1 i_tile1, - const iType2 i_tile2, const iType3 i_tile3, const iType4 i_tile4, - const iType5 i_tile5, const iType6 i_tile6) { - using dst_map_type = ViewMapping; - using src_map_type = ViewMapping; - using dst_handle_type = typename dst_map_type::handle_type; - using dst_offset_type = typename dst_map_type::offset_type; - using src_offset_type = typename src_map_type::offset_type; - - dst = dst_map_type( - dst_handle_type( - src.m_impl_handle + - (is_outer_left - ? ((i_tile0 + - src.m_impl_offset.m_tile_N0 * - (i_tile1 + - src.m_impl_offset.m_tile_N1 * - (i_tile2 + - src.m_impl_offset.m_tile_N2 * - (i_tile3 + - src.m_impl_offset.m_tile_N3 * - (i_tile4 + - src.m_impl_offset.m_tile_N4 * - (i_tile5 + - src.m_impl_offset.m_tile_N5 * - i_tile6)))))) - << src_offset_type::SHIFT_7T) - : ((src.m_impl_offset.m_tile_N6 * - (src.m_impl_offset.m_tile_N5 * - (src.m_impl_offset.m_tile_N4 * - (src.m_impl_offset.m_tile_N3 * - (src.m_impl_offset.m_tile_N2 * - (src.m_impl_offset.m_tile_N1 * - i_tile0 + - i_tile1) + - i_tile2) + - i_tile3) + - i_tile4) + - i_tile5) + - i_tile6) - << src_offset_type::SHIFT_7T))) // offset to start of the - // tile - , - dst_offset_type()); - } -}; - -// Rank 8 -template -class ViewMapping< - std::enable_if_t<(N0 != 0 && N1 != 0 && N2 != 0 && N3 != 0 && N4 != 0 && - N5 != 0 && N6 != 0 && N7 != 0)> // void - , - Kokkos::ViewTraits< - T********, - Kokkos::Experimental::LayoutTiled, - P...>, - Kokkos::Experimental::LayoutTiled, - iType0, iType1, iType2, iType3, iType4, iType5, iType6, iType7> { - public: - using src_layout = - Kokkos::Experimental::LayoutTiled; - using src_traits = Kokkos::ViewTraits; - - static constexpr bool is_outer_left = (OuterP == Kokkos::Iterate::Left); - static constexpr bool is_inner_left = (InnerP == Kokkos::Iterate::Left); - using array_layout = std::conditional_t; - using traits = - Kokkos::ViewTraits; - using type = - Kokkos::View; - - KOKKOS_INLINE_FUNCTION static void assign( - ViewMapping& dst, const ViewMapping& src, - const src_layout&, const iType0 i_tile0, const iType1 i_tile1, - const iType2 i_tile2, const iType3 i_tile3, const iType4 i_tile4, - const iType5 i_tile5, const iType6 i_tile6, const iType7 i_tile7) { - using dst_map_type = ViewMapping; - using src_map_type = ViewMapping; - using dst_handle_type = typename dst_map_type::handle_type; - using dst_offset_type = typename dst_map_type::offset_type; - using src_offset_type = typename src_map_type::offset_type; - - dst = dst_map_type( - dst_handle_type( - src.m_impl_handle + - (is_outer_left - ? ((i_tile0 + - src.m_impl_offset.m_tile_N0 * - (i_tile1 + - src.m_impl_offset.m_tile_N1 * - (i_tile2 + - src.m_impl_offset.m_tile_N2 * - (i_tile3 + - src.m_impl_offset.m_tile_N3 * - (i_tile4 + - src.m_impl_offset.m_tile_N4 * - (i_tile5 + - src.m_impl_offset.m_tile_N5 * - (i_tile6 + - src.m_impl_offset.m_tile_N6 * - i_tile7))))))) - << src_offset_type::SHIFT_8T) - : ((src.m_impl_offset.m_tile_N7 * - (src.m_impl_offset.m_tile_N6 * - (src.m_impl_offset.m_tile_N5 * - (src.m_impl_offset.m_tile_N4 * - (src.m_impl_offset.m_tile_N3 * - (src.m_impl_offset.m_tile_N2 * - (src.m_impl_offset.m_tile_N1 * - i_tile0 + - i_tile1) + - i_tile2) + - i_tile3) + - i_tile4) + - i_tile5) + - i_tile6) + - i_tile7) - << src_offset_type::SHIFT_8T))) // offset to start of the - // tile - , - dst_offset_type()); - } -}; - -} /* namespace Impl */ -} /* namespace Kokkos */ - -//---------------------------------------- - -namespace Kokkos { - -// Rank 2 -template -KOKKOS_INLINE_FUNCTION - Kokkos::View, - P...> - tile_subview(const Kokkos::View< - T**, - Kokkos::Experimental::LayoutTiled< - OuterP, InnerP, N0, N1, N2, N3, N4, N5, N6, N7, true>, - P...>& src, - const size_t i_tile0, const size_t i_tile1) { - // Force the specialized ViewMapping for extracting a tile - // by using the first subview argument as the layout. - using array_layout = - std::conditional_t<(InnerP == Kokkos::Iterate::Left), Kokkos::LayoutLeft, - Kokkos::LayoutRight>; - using SrcLayout = - Kokkos::Experimental::LayoutTiled; - - return Kokkos::View(src, SrcLayout(), i_tile0, - i_tile1); -} - -// Rank 3 -template -KOKKOS_INLINE_FUNCTION - Kokkos::View, - P...> - tile_subview(const Kokkos::View< - T***, - Kokkos::Experimental::LayoutTiled< - OuterP, InnerP, N0, N1, N2, N3, N4, N5, N6, N7, true>, - P...>& src, - const size_t i_tile0, const size_t i_tile1, - const size_t i_tile2) { - // Force the specialized ViewMapping for extracting a tile - // by using the first subview argument as the layout. - using array_layout = - std::conditional_t<(InnerP == Kokkos::Iterate::Left), Kokkos::LayoutLeft, - Kokkos::LayoutRight>; - using SrcLayout = - Kokkos::Experimental::LayoutTiled; - - return Kokkos::View( - src, SrcLayout(), i_tile0, i_tile1, i_tile2); -} - -// Rank 4 -template -KOKKOS_INLINE_FUNCTION - Kokkos::View, - P...> - tile_subview(const Kokkos::View< - T****, - Kokkos::Experimental::LayoutTiled< - OuterP, InnerP, N0, N1, N2, N3, N4, N5, N6, N7, true>, - P...>& src, - const size_t i_tile0, const size_t i_tile1, - const size_t i_tile2, const size_t i_tile3) { - // Force the specialized ViewMapping for extracting a tile - // by using the first subview argument as the layout. - using array_layout = - std::conditional_t<(InnerP == Kokkos::Iterate::Left), Kokkos::LayoutLeft, - Kokkos::LayoutRight>; - using SrcLayout = - Kokkos::Experimental::LayoutTiled; - - return Kokkos::View( - src, SrcLayout(), i_tile0, i_tile1, i_tile2, i_tile3); -} - -// Rank 5 -template -KOKKOS_INLINE_FUNCTION - Kokkos::View, - P...> - tile_subview(const Kokkos::View< - T*****, - Kokkos::Experimental::LayoutTiled< - OuterP, InnerP, N0, N1, N2, N3, N4, N5, N6, N7, true>, - P...>& src, - const size_t i_tile0, const size_t i_tile1, - const size_t i_tile2, const size_t i_tile3, - const size_t i_tile4) { - // Force the specialized ViewMapping for extracting a tile - // by using the first subview argument as the layout. - using array_layout = - std::conditional_t<(InnerP == Kokkos::Iterate::Left), Kokkos::LayoutLeft, - Kokkos::LayoutRight>; - using SrcLayout = - Kokkos::Experimental::LayoutTiled; - - return Kokkos::View( - src, SrcLayout(), i_tile0, i_tile1, i_tile2, i_tile3, i_tile4); -} - -// Rank 6 -template -KOKKOS_INLINE_FUNCTION - Kokkos::View, - P...> - tile_subview(const Kokkos::View< - T******, - Kokkos::Experimental::LayoutTiled< - OuterP, InnerP, N0, N1, N2, N3, N4, N5, N6, N7, true>, - P...>& src, - const size_t i_tile0, const size_t i_tile1, - const size_t i_tile2, const size_t i_tile3, - const size_t i_tile4, const size_t i_tile5) { - // Force the specialized ViewMapping for extracting a tile - // by using the first subview argument as the layout. - using array_layout = - std::conditional_t<(InnerP == Kokkos::Iterate::Left), Kokkos::LayoutLeft, - Kokkos::LayoutRight>; - using SrcLayout = - Kokkos::Experimental::LayoutTiled; - - return Kokkos::View( - src, SrcLayout(), i_tile0, i_tile1, i_tile2, i_tile3, i_tile4, i_tile5); -} - -// Rank 7 -template -KOKKOS_INLINE_FUNCTION - Kokkos::View, - P...> - tile_subview(const Kokkos::View< - T*******, - Kokkos::Experimental::LayoutTiled< - OuterP, InnerP, N0, N1, N2, N3, N4, N5, N6, N7, true>, - P...>& src, - const size_t i_tile0, const size_t i_tile1, - const size_t i_tile2, const size_t i_tile3, - const size_t i_tile4, const size_t i_tile5, - const size_t i_tile6) { - // Force the specialized ViewMapping for extracting a tile - // by using the first subview argument as the layout. - using array_layout = - std::conditional_t<(InnerP == Kokkos::Iterate::Left), Kokkos::LayoutLeft, - Kokkos::LayoutRight>; - using SrcLayout = - Kokkos::Experimental::LayoutTiled; - - return Kokkos::View( - src, SrcLayout(), i_tile0, i_tile1, i_tile2, i_tile3, i_tile4, i_tile5, - i_tile6); -} - -// Rank 8 -template -KOKKOS_INLINE_FUNCTION - Kokkos::View, - P...> - tile_subview(const Kokkos::View< - T********, - Kokkos::Experimental::LayoutTiled< - OuterP, InnerP, N0, N1, N2, N3, N4, N5, N6, N7, true>, - P...>& src, - const size_t i_tile0, const size_t i_tile1, - const size_t i_tile2, const size_t i_tile3, - const size_t i_tile4, const size_t i_tile5, - const size_t i_tile6, const size_t i_tile7) { - // Force the specialized ViewMapping for extracting a tile - // by using the first subview argument as the layout. - using array_layout = - std::conditional_t<(InnerP == Kokkos::Iterate::Left), Kokkos::LayoutLeft, - Kokkos::LayoutRight>; - using SrcLayout = - Kokkos::Experimental::LayoutTiled; - - return Kokkos::View( - src, SrcLayout(), i_tile0, i_tile1, i_tile2, i_tile3, i_tile4, i_tile5, - i_tile6, i_tile7); -} - -} /* namespace Kokkos */ -//---------------------------------------------------------------------------- -//---------------------------------------------------------------------------- - -#endif /* #ifndef KOKKOS_EXPERIENTAL_VIEWLAYOUTTILE_HPP */ diff --git a/lib/kokkos/core/src/impl/Kokkos_ViewMapping.hpp b/lib/kokkos/core/src/impl/Kokkos_ViewMapping.hpp index 3217c76e38..8919dccdb7 100644 --- a/lib/kokkos/core/src/impl/Kokkos_ViewMapping.hpp +++ b/lib/kokkos/core/src/impl/Kokkos_ViewMapping.hpp @@ -17,6 +17,7 @@ #ifndef KOKKOS_EXPERIMENTAL_VIEW_MAPPING_HPP #define KOKKOS_EXPERIMENTAL_VIEW_MAPPING_HPP +#include #include #include @@ -34,6 +35,7 @@ #include #include #include +#include //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- @@ -647,34 +649,60 @@ struct ViewOffset< m_dim.N5 * m_dim.N6; } - // Stride with [ rank ] value is the total length + // Fill the target unbounded array s with the stride. + // This method differs from stride() in that it does not write the total + // length to the last index of the array. Preconditions: s must be an array of + // dimension_type::rank elements + // FIXME: The version of clang-format in CI fails from maybe_unused + // clang-format off + template + KOKKOS_INLINE_FUNCTION iType + stride_fill([[maybe_unused]] iType* const s) const { + iType n = 1; + if constexpr (0 < dimension_type::rank) { + s[0] = n; + n *= m_dim.N0; + } + if constexpr (1 < dimension_type::rank) { + s[1] = n; + n *= m_dim.N1; + } + if constexpr (2 < dimension_type::rank) { + s[2] = n; + n *= m_dim.N2; + } + if constexpr (3 < dimension_type::rank) { + s[3] = n; + n *= m_dim.N3; + } + if constexpr (4 < dimension_type::rank) { + s[4] = n; + n *= m_dim.N4; + } + if constexpr (5 < dimension_type::rank) { + s[5] = n; + n *= m_dim.N5; + } + if constexpr (6 < dimension_type::rank) { + s[6] = n; + n *= m_dim.N6; + } + if constexpr (7 < dimension_type::rank) { + s[7] = n; + n *= m_dim.N7; + } + return n; + } + // clang-format on + + // Fill the target unbounded array s with the stride and the total spanned + // size. This method differs from stride_fill() in that it writes the total + // spanned size to the last index of the array. Preconditions: s must be an + // array of dimension_type::rank + 1 elements Stride with [ rank ] value is + // the total length template KOKKOS_INLINE_FUNCTION void stride(iType* const s) const { - s[0] = 1; - if (0 < dimension_type::rank) { - s[1] = m_dim.N0; - } - if (1 < dimension_type::rank) { - s[2] = s[1] * m_dim.N1; - } - if (2 < dimension_type::rank) { - s[3] = s[2] * m_dim.N2; - } - if (3 < dimension_type::rank) { - s[4] = s[3] * m_dim.N3; - } - if (4 < dimension_type::rank) { - s[5] = s[4] * m_dim.N4; - } - if (5 < dimension_type::rank) { - s[6] = s[5] * m_dim.N5; - } - if (6 < dimension_type::rank) { - s[7] = s[6] * m_dim.N6; - } - if (7 < dimension_type::rank) { - s[8] = s[7] * m_dim.N7; - } + s[dimension_type::rank] = stride_fill(s); } //---------------------------------------- @@ -935,34 +963,59 @@ struct ViewOffset< m_dim.N6; } - // Stride with [ rank ] value is the total length + // Fill the target unbounded array s with the stride. + // This method differs from stride() in that it does not write the total + // length to the last index of the array. Preconditions: s must be an array of + // dimension_type::rank elements + // The version of clang-format in CI fails from maybe_unused + // clang-format off + template + KOKKOS_INLINE_FUNCTION iType + stride_fill([[maybe_unused]] iType* const s) const { + iType n = 1; + if constexpr (0 < dimension_type::rank) { + s[0] = n; + n *= m_stride; + } + if constexpr (1 < dimension_type::rank) { + s[1] = n; + n *= m_dim.N1; + } + if constexpr (2 < dimension_type::rank) { + s[2] = n; + n *= m_dim.N2; + } + if constexpr (3 < dimension_type::rank) { + s[3] = n; + n *= m_dim.N3; + } + if constexpr (4 < dimension_type::rank) { + s[4] = n; + n *= m_dim.N4; + } + if constexpr (5 < dimension_type::rank) { + s[5] = n; + n *= m_dim.N5; + } + if constexpr (6 < dimension_type::rank) { + s[6] = n; + n *= m_dim.N6; + } + if constexpr (7 < dimension_type::rank) { + s[7] = n; + n *= m_dim.N7; + } + return n; + } + // clang-format on + + // Fill the target unbounded array s with the stride and the total spanned + // size. This method differs from stride_fill() in that it writes the total + // spanned size to the last index of the array. Preconditions: s must be an + // array of dimension_type::rank + 1 elements template KOKKOS_INLINE_FUNCTION void stride(iType* const s) const { - s[0] = 1; - if (0 < dimension_type::rank) { - s[1] = m_stride; - } - if (1 < dimension_type::rank) { - s[2] = s[1] * m_dim.N1; - } - if (2 < dimension_type::rank) { - s[3] = s[2] * m_dim.N2; - } - if (3 < dimension_type::rank) { - s[4] = s[3] * m_dim.N3; - } - if (4 < dimension_type::rank) { - s[5] = s[4] * m_dim.N4; - } - if (5 < dimension_type::rank) { - s[6] = s[5] * m_dim.N5; - } - if (6 < dimension_type::rank) { - s[7] = s[6] * m_dim.N6; - } - if (7 < dimension_type::rank) { - s[8] = s[7] * m_dim.N7; - } + s[dimension_type::rank] = stride_fill(s); } //---------------------------------------- @@ -1286,42 +1339,58 @@ struct ViewOffset< m_dim.N1; } - // Stride with [ rank ] value is the total length + // Fill the target unbounded array s with the stride. + // This method differs from stride() in that it does not write the total + // length to the last index of the array. Preconditions: s must be an array of + // dimension_type::rank elements + // The version of clang-format in CI fails from maybe_unused + // clang-format off template - KOKKOS_INLINE_FUNCTION void stride(iType* const s) const { + KOKKOS_INLINE_FUNCTION iType + stride_fill([[maybe_unused]] iType* const s) const { size_type n = 1; - if (7 < dimension_type::rank) { + if constexpr (7 < dimension_type::rank) { s[7] = n; n *= m_dim.N7; } - if (6 < dimension_type::rank) { + if constexpr (6 < dimension_type::rank) { s[6] = n; n *= m_dim.N6; } - if (5 < dimension_type::rank) { + if constexpr (5 < dimension_type::rank) { s[5] = n; n *= m_dim.N5; } - if (4 < dimension_type::rank) { + if constexpr (4 < dimension_type::rank) { s[4] = n; n *= m_dim.N4; } - if (3 < dimension_type::rank) { + if constexpr (3 < dimension_type::rank) { s[3] = n; n *= m_dim.N3; } - if (2 < dimension_type::rank) { + if constexpr (2 < dimension_type::rank) { s[2] = n; n *= m_dim.N2; } - if (1 < dimension_type::rank) { + if constexpr (1 < dimension_type::rank) { s[1] = n; n *= m_dim.N1; } - if (0 < dimension_type::rank) { + if constexpr (0 < dimension_type::rank) { s[0] = n; } - s[dimension_type::rank] = n * m_dim.N0; + return n * m_dim.N0; + } + // clang-format on + + // Fill the target unbounded array s with the stride and the total spanned + // size. This method differs from stride_fill() in that it writes the total + // spanned size to the last index of the array. Preconditions: s must be an + // array of dimension_type::rank + 1 elements + template + KOKKOS_INLINE_FUNCTION void stride(iType* const s) const { + s[dimension_type::rank] = stride_fill(s); } //---------------------------------------- @@ -1573,41 +1642,57 @@ struct ViewOffset< return m_stride; } - // Stride with [ rank ] value is the total length + // Fill the target unbounded array s with the stride. + // This method differs from stride() in that it does not write the total + // length to the last index of the array. Preconditions: s must be an array of + // dimension_type::rank elements + // The version of clang-format in CI fails from maybe_unused + // clang-format off template - KOKKOS_INLINE_FUNCTION void stride(iType* const s) const { + KOKKOS_INLINE_FUNCTION iType + stride_fill([[maybe_unused]] iType* const s) const { size_type n = 1; - if (7 < dimension_type::rank) { + if constexpr (7 < dimension_type::rank) { s[7] = n; n *= m_dim.N7; } - if (6 < dimension_type::rank) { + if constexpr (6 < dimension_type::rank) { s[6] = n; n *= m_dim.N6; } - if (5 < dimension_type::rank) { + if constexpr (5 < dimension_type::rank) { s[5] = n; n *= m_dim.N5; } - if (4 < dimension_type::rank) { + if constexpr (4 < dimension_type::rank) { s[4] = n; n *= m_dim.N4; } - if (3 < dimension_type::rank) { + if constexpr (3 < dimension_type::rank) { s[3] = n; n *= m_dim.N3; } - if (2 < dimension_type::rank) { + if constexpr (2 < dimension_type::rank) { s[2] = n; n *= m_dim.N2; } - if (1 < dimension_type::rank) { + if constexpr (1 < dimension_type::rank) { s[1] = n; } - if (0 < dimension_type::rank) { + if constexpr (0 < dimension_type::rank) { s[0] = m_stride; } - s[dimension_type::rank] = m_stride * m_dim.N0; + return m_stride * m_dim.N0; + } + // clang-format on + + // Fill the target unbounded array s with the stride and the total spanned + // size. This method differs from stride_fill() in that it writes the total + // spanned size to the last index of the array. Preconditions: s must be an + // array of dimension_type::rank + 1 elements + template + KOKKOS_INLINE_FUNCTION void stride(iType* const s) const { + s[dimension_type::rank] = stride_fill(s); } //---------------------------------------- @@ -2133,34 +2218,50 @@ struct ViewOffset { return m_stride.S7; } - // Stride with [ rank ] value is the total length + // Fill the target unbounded array s with the stride. + // This method differs from stride() in that it does not write the total + // length to the last index of the array. Preconditions: s must be an array of + // dimension_type::rank elements + // The version of clang-format in CI fails from maybe_unused + // clang-format off template - KOKKOS_INLINE_FUNCTION void stride(iType* const s) const { - if (0 < dimension_type::rank) { + KOKKOS_INLINE_FUNCTION iType + stride_fill([[maybe_unused]] iType* const s) const { + if constexpr (0 < dimension_type::rank) { s[0] = m_stride.S0; } - if (1 < dimension_type::rank) { + if constexpr (1 < dimension_type::rank) { s[1] = m_stride.S1; } - if (2 < dimension_type::rank) { + if constexpr (2 < dimension_type::rank) { s[2] = m_stride.S2; } - if (3 < dimension_type::rank) { + if constexpr (3 < dimension_type::rank) { s[3] = m_stride.S3; } - if (4 < dimension_type::rank) { + if constexpr (4 < dimension_type::rank) { s[4] = m_stride.S4; } - if (5 < dimension_type::rank) { + if constexpr (5 < dimension_type::rank) { s[5] = m_stride.S5; } - if (6 < dimension_type::rank) { + if constexpr (6 < dimension_type::rank) { s[6] = m_stride.S6; } - if (7 < dimension_type::rank) { + if constexpr (7 < dimension_type::rank) { s[7] = m_stride.S7; } - s[dimension_type::rank] = span(); + return span(); + } + // clang-format on + + // Fill the target unbounded array s with the stride and the total spanned + // size. This method differs from stride_fill() in that it writes the total + // spanned size to the last index of the array. Preconditions: s must be an + // array of dimension_type::rank + 1 elements + template + KOKKOS_INLINE_FUNCTION void stride(iType* const s) const { + s[dimension_type::rank] = stride_fill(s); } //---------------------------------------- @@ -2428,288 +2529,6 @@ struct ViewDataHandle< namespace Kokkos { namespace Impl { - -template -inline bool is_zero_byte(const T& t) { - using comparison_type = std::conditional_t< - sizeof(T) % sizeof(long long int) == 0, long long int, - std::conditional_t< - sizeof(T) % sizeof(long int) == 0, long int, - std::conditional_t< - sizeof(T) % sizeof(int) == 0, int, - std::conditional_t>>>; - const auto* const ptr = reinterpret_cast(&t); - for (std::size_t i = 0; i < sizeof(T) / sizeof(comparison_type); ++i) - if (ptr[i] != 0) return false; - return true; -} - -//---------------------------------------------------------------------------- - -/* - * The construction, assignment to default, and destruction - * are merged into a single functor. - * Primarily to work around an unresolved CUDA back-end bug - * that would lose the destruction cuda device function when - * called from the shared memory tracking destruction. - * Secondarily to have two fewer partial specializations. - */ -template ::value> -struct ViewValueFunctor; - -template -struct ViewValueFunctor { - using ExecSpace = typename DeviceType::execution_space; - - struct DestroyTag {}; - struct ConstructTag {}; - - ExecSpace space; - ValueType* ptr; - size_t n; - std::string name; - bool default_exec_space; - - template - KOKKOS_INLINE_FUNCTION - std::enable_if_t::value> - operator()(ConstructTag const&, const size_t i) const { - new (ptr + i) ValueType(); - } - - KOKKOS_INLINE_FUNCTION void operator()(DestroyTag const&, - const size_t i) const { - (ptr + i)->~ValueType(); - } - - ViewValueFunctor() = default; - ViewValueFunctor(const ViewValueFunctor&) = default; - ViewValueFunctor& operator=(const ViewValueFunctor&) = default; - - ViewValueFunctor(ExecSpace const& arg_space, ValueType* const arg_ptr, - size_t const arg_n, std::string arg_name) - : space(arg_space), - ptr(arg_ptr), - n(arg_n), - name(std::move(arg_name)), - default_exec_space(false) { - functor_instantiate_workaround(); - } - - ViewValueFunctor(ValueType* const arg_ptr, size_t const arg_n, - std::string arg_name) - : space(ExecSpace{}), - ptr(arg_ptr), - n(arg_n), - name(std::move(arg_name)), - default_exec_space(true) { - functor_instantiate_workaround(); - } - - template - std::enable_if_t::value && - std::is_trivially_copy_assignable::value> - construct_dispatch() { - ValueType value{}; -// On A64FX memset seems to do the wrong thing with regards to first touch -// leading to the significant performance issues -#ifndef KOKKOS_ARCH_A64FX - if (Impl::is_zero_byte(value)) { - uint64_t kpID = 0; - if (Kokkos::Profiling::profileLibraryLoaded()) { - // We are not really using parallel_for here but using beginParallelFor - // instead of begin_parallel_for (and adding "via memset") is the best - // we can do to indicate that this is not supposed to be tunable (and - // doesn't really execute a parallel_for). - Kokkos::Profiling::beginParallelFor( - "Kokkos::View::initialization [" + name + "] via memset", - Kokkos::Profiling::Experimental::device_id(space), &kpID); - } - (void)ZeroMemset( - space, Kokkos::View>(ptr, n)); - - if (Kokkos::Profiling::profileLibraryLoaded()) { - Kokkos::Profiling::endParallelFor(kpID); - } - if (default_exec_space) - space.fence("Kokkos::Impl::ViewValueFunctor: View init/destroy fence"); - } else { -#endif - parallel_for_implementation(); -#ifndef KOKKOS_ARCH_A64FX - } -#endif - } - - template - std::enable_if_t::value && - std::is_trivially_copy_assignable::value)> - construct_dispatch() { - parallel_for_implementation(); - } - - template - void parallel_for_implementation() { - using PolicyType = - Kokkos::RangePolicy, Tag>; - PolicyType policy(space, 0, n); - uint64_t kpID = 0; - if (Kokkos::Profiling::profileLibraryLoaded()) { - const std::string functor_name = - (std::is_same_v - ? "Kokkos::View::destruction [" + name + "]" - : "Kokkos::View::initialization [" + name + "]"); - Kokkos::Profiling::beginParallelFor( - functor_name, Kokkos::Profiling::Experimental::device_id(space), - &kpID); - } - -#ifdef KOKKOS_ENABLE_CUDA - if (std::is_same::value) { - Kokkos::Impl::cuda_prefetch_pointer(space, ptr, sizeof(ValueType) * n, - true); - } -#endif - const Kokkos::Impl::ParallelFor closure( - *this, policy); - closure.execute(); - if (default_exec_space || std::is_same_v) - space.fence("Kokkos::Impl::ViewValueFunctor: View init/destroy fence"); - if (Kokkos::Profiling::profileLibraryLoaded()) { - Kokkos::Profiling::endParallelFor(kpID); - } - } - - void construct_shared_allocation() { construct_dispatch(); } - - void destroy_shared_allocation() { - parallel_for_implementation(); - } - - // This function is to ensure that the functor with DestroyTag is instantiated - // This is a workaround to avoid "cudaErrorInvalidDeviceFunction" error later - // when the function is queried with cudaFuncGetAttributes - void functor_instantiate_workaround() { -#if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP) || \ - defined(KOKKOS_ENABLE_SYCL) || defined(KOKKOS_ENABLE_OPENMPTARGET) - if (false) { - parallel_for_implementation(); - } -#endif - } -}; - -template -struct ViewValueFunctor { - using ExecSpace = typename DeviceType::execution_space; - using PolicyType = Kokkos::RangePolicy>; - - ExecSpace space; - ValueType* ptr; - size_t n; - std::string name; - bool default_exec_space; - - KOKKOS_INLINE_FUNCTION - void operator()(const size_t i) const { ptr[i] = ValueType(); } - - ViewValueFunctor() = default; - ViewValueFunctor(const ViewValueFunctor&) = default; - ViewValueFunctor& operator=(const ViewValueFunctor&) = default; - - ViewValueFunctor(ExecSpace const& arg_space, ValueType* const arg_ptr, - size_t const arg_n, std::string arg_name) - : space(arg_space), - ptr(arg_ptr), - n(arg_n), - name(std::move(arg_name)), - default_exec_space(false) {} - - ViewValueFunctor(ValueType* const arg_ptr, size_t const arg_n, - std::string arg_name) - : space(ExecSpace{}), - ptr(arg_ptr), - n(arg_n), - name(std::move(arg_name)), - default_exec_space(true) {} - - template - std::enable_if_t::value && - std::is_trivially_copy_assignable::value> - construct_shared_allocation() { - // Shortcut for zero initialization -// On A64FX memset seems to do the wrong thing with regards to first touch -// leading to the significant performance issues -#ifndef KOKKOS_ARCH_A64FX - ValueType value{}; - if (Impl::is_zero_byte(value)) { - uint64_t kpID = 0; - if (Kokkos::Profiling::profileLibraryLoaded()) { - // We are not really using parallel_for here but using beginParallelFor - // instead of begin_parallel_for (and adding "via memset") is the best - // we can do to indicate that this is not supposed to be tunable (and - // doesn't really execute a parallel_for). - Kokkos::Profiling::beginParallelFor( - "Kokkos::View::initialization [" + name + "] via memset", - Kokkos::Profiling::Experimental::device_id(space), &kpID); - } - - (void)ZeroMemset( - space, Kokkos::View>(ptr, n)); - - if (Kokkos::Profiling::profileLibraryLoaded()) { - Kokkos::Profiling::endParallelFor(kpID); - } - if (default_exec_space) - space.fence("Kokkos::Impl::ViewValueFunctor: View init/destroy fence"); - } else { -#endif - parallel_for_implementation(); -#ifndef KOKKOS_ARCH_A64FX - } -#endif - } - - template - std::enable_if_t::value && - std::is_trivially_copy_assignable::value)> - construct_shared_allocation() { - parallel_for_implementation(); - } - - void parallel_for_implementation() { - PolicyType policy(0, n); - uint64_t kpID = 0; - if (Kokkos::Profiling::profileLibraryLoaded()) { - Kokkos::Profiling::beginParallelFor( - "Kokkos::View::initialization [" + name + "]", - Kokkos::Profiling::Experimental::device_id(space), &kpID); - } -#ifdef KOKKOS_ENABLE_CUDA - if (std::is_same::value) { - Kokkos::Impl::cuda_prefetch_pointer(space, ptr, sizeof(ValueType) * n, - true); - } -#endif - const Kokkos::Impl::ParallelFor closure( - *this, PolicyType(0, n)); - closure.execute(); - if (default_exec_space) - space.fence( - "Kokkos::Impl::ViewValueFunctor: Fence after setting values in " - "view"); - if (Kokkos::Profiling::profileLibraryLoaded()) { - Kokkos::Profiling::endParallelFor(kpID); - } - } - - void destroy_shared_allocation() {} -}; - //---------------------------------------------------------------------------- /** \brief View mapping for non-specialized data type and standard layout */ template @@ -2814,11 +2633,24 @@ class ViewMapping< return m_impl_offset.stride_7(); } + // Fill the target unbounded array s with the stride and the total spanned + // size. This method differs from stride_fill() in that it writes the total + // spanned size to the last index of the array. Preconditions: s must be an + // array of dimension_type::rank + 1 elements template KOKKOS_INLINE_FUNCTION void stride(iType* const s) const { m_impl_offset.stride(s); } + // Fill the target unbounded array s with the stride. + // This method differs from stride() in that it does not write the total + // length to the last index of the array. Preconditions: s must be an array of + // dimension_type::rank elements + template + KOKKOS_INLINE_FUNCTION iType stride_fill(iType* const s) const { + return m_impl_offset.stride_fill(s); + } + //---------------------------------------- // Range span @@ -3360,7 +3192,7 @@ struct SubViewDataTypeImpl> { }; /* for integral args, subview doesn't have that dimension */ -template struct SubViewDataTypeImpl< std::enable_if_t>::value>, @@ -3369,7 +3201,7 @@ struct SubViewDataTypeImpl< Kokkos::Experimental::Extents, Args...> {}; /* for ALL slice, subview has the same dimension */ -template +template struct SubViewDataTypeImpl, Kokkos::ALL_t, Args...> @@ -3380,7 +3212,7 @@ struct SubViewDataTypeImpl struct SubViewDataTypeImpl< std::enable_if_t::value>, ValueType, diff --git a/lib/kokkos/core/src/setup/Kokkos_Setup_Cuda.hpp b/lib/kokkos/core/src/setup/Kokkos_Setup_Cuda.hpp index 1130485e84..b2faccc527 100644 --- a/lib/kokkos/core/src/setup/Kokkos_Setup_Cuda.hpp +++ b/lib/kokkos/core/src/setup/Kokkos_Setup_Cuda.hpp @@ -56,6 +56,8 @@ #define KOKKOS_LAMBDA [=] __host__ __device__ #define KOKKOS_CLASS_LAMBDA [ =, *this ] __host__ __device__ +#define KOKKOS_DEDUCTION_GUIDE __host__ __device__ + #define KOKKOS_IMPL_FORCEINLINE_FUNCTION __device__ __host__ __forceinline__ #define KOKKOS_IMPL_FORCEINLINE __forceinline__ #define KOKKOS_IMPL_INLINE_FUNCTION __device__ __host__ inline diff --git a/lib/kokkos/core/src/setup/Kokkos_Setup_HIP.hpp b/lib/kokkos/core/src/setup/Kokkos_Setup_HIP.hpp index 7b01866107..a3c5000b33 100644 --- a/lib/kokkos/core/src/setup/Kokkos_Setup_HIP.hpp +++ b/lib/kokkos/core/src/setup/Kokkos_Setup_HIP.hpp @@ -27,6 +27,8 @@ #define KOKKOS_LAMBDA [=] __host__ __device__ #define KOKKOS_CLASS_LAMBDA [ =, *this ] __host__ __device__ +#define KOKKOS_DEDUCTION_GUIDE __host__ __device__ + #define KOKKOS_IMPL_FORCEINLINE_FUNCTION __device__ __host__ __forceinline__ #define KOKKOS_IMPL_INLINE_FUNCTION __device__ __host__ inline #define KOKKOS_IMPL_FUNCTION __device__ __host__ diff --git a/lib/kokkos/core/src/setup/Kokkos_Setup_SYCL.hpp b/lib/kokkos/core/src/setup/Kokkos_Setup_SYCL.hpp index 30f6fa2ad2..b117d75acb 100644 --- a/lib/kokkos/core/src/setup/Kokkos_Setup_SYCL.hpp +++ b/lib/kokkos/core/src/setup/Kokkos_Setup_SYCL.hpp @@ -45,4 +45,21 @@ #define KOKKOS_IMPL_SYCL_GET_MULTI_PTR(accessor) accessor.get_pointer() #endif +// FIXME_SYCL Use type directly once it has stabilized in SYCL. +namespace Kokkos::Impl { +#ifndef SYCL_EXT_INTEL_USM_ADDRESS_SPACES +#error SYCL_EXT_INTEL_USM_ADDRESS_SPACES undefined! +#elif SYCL_EXT_INTEL_USM_ADDRESS_SPACES >= 2 +template +using sycl_device_ptr = sycl::ext::intel::device_ptr; +template +using sycl_host_ptr = sycl::ext::intel::host_ptr; +#else +template +using sycl_device_ptr = sycl::device_ptr; +template +using sycl_host_ptr = sycl::host_ptr; +#endif +} // namespace Kokkos::Impl + #endif diff --git a/lib/kokkos/core/unit_test/CMakeLists.txt b/lib/kokkos/core/unit_test/CMakeLists.txt index 6dfb7505c5..f821581872 100644 --- a/lib/kokkos/core/unit_test/CMakeLists.txt +++ b/lib/kokkos/core/unit_test/CMakeLists.txt @@ -93,6 +93,9 @@ SET(COMPILE_ONLY_SOURCES TestViewTypeTraits.cpp TestTypeList.cpp TestMDRangePolicyCTAD.cpp + TestTeamPolicyCTAD.cpp + TestTeamMDRangePolicyCTAD.cpp + TestNestedReducerCTAD.cpp view/TestExtentsDatatypeConversion.cpp ) @@ -105,6 +108,9 @@ endif() IF(KOKKOS_HAS_TRILINOS) LIST(REMOVE_ITEM COMPILE_ONLY_SOURCES TestInterOp.cpp) ENDIF() +if(Kokkos_ENABLE_OPENMPTARGET) + list(REMOVE_ITEM COMPILE_ONLY_SOURCES TestNestedReducerCTAD.cpp) +endif() KOKKOS_ADD_EXECUTABLE( CoreTestCompileOnly SOURCES @@ -148,8 +154,10 @@ foreach(Tag Threads;Serial;OpenMP;Cuda;HPX;OpenMPTarget;OpenACC;HIP;SYCL) Crs DeepCopyAlignment ExecSpacePartitioning + ExecSpaceThreadSafety ExecutionSpace FunctorAnalysis + Graph HostSharedPtr HostSharedPtrAccessOnDevice Init @@ -173,7 +181,7 @@ foreach(Tag Threads;Serial;OpenMP;Cuda;HPX;OpenMPTarget;OpenACC;HIP;SYCL) endforeach() set(${Tag}_SOURCES1B) - foreach(Name + set(${Tag}_TESTNAMES1B MDRange_a MDRange_b MDRange_c @@ -184,6 +192,8 @@ foreach(Tag Threads;Serial;OpenMP;Cuda;HPX;OpenMPTarget;OpenACC;HIP;SYCL) MDRangePolicyConstructors MDRangeReduce MDSpan + MDSpanAtomicAccessor + MDSpanConversion MinMaxClamp NumericTraits OccupancyControlTrait @@ -203,8 +213,19 @@ foreach(Tag Threads;Serial;OpenMP;Cuda;HPX;OpenMPTarget;OpenACC;HIP;SYCL) Reductions Reductions_DeviceView SharedAlloc + SpaceAwareAccessorAccessViolation + SpaceAwareAccessor Swap ) + IF (NOT Kokkos_ENABLE_IMPL_MDSPAN) + LIST(REMOVE_ITEM ${Tag}_TESTNAMES1B + MDSpanAtomicAccessor + MDSpanConversion + SpaceAwareAccessorAccessViolation + SpaceAwareAccessor + ) + ENDIF() + foreach(Name IN LISTS ${Tag}_TESTNAMES1B) set(file ${dir}/Test${Tag}_${Name}.cpp) # Write to a temporary intermediate file and call configure_file to avoid # updating timestamps triggering unnecessary rebuilds on subsequent cmake runs. @@ -217,7 +238,7 @@ foreach(Tag Threads;Serial;OpenMP;Cuda;HPX;OpenMPTarget;OpenACC;HIP;SYCL) endforeach() SET(${Tag}_SOURCES2A) - foreach(Name + SET(${TAG}_TESTNAMES2A TeamBasic TeamCombinedReducers TeamMDRange @@ -234,8 +255,10 @@ foreach(Tag Threads;Serial;OpenMP;Cuda;HPX;OpenMPTarget;OpenACC;HIP;SYCL) ViewAPI_c ViewAPI_d ViewAPI_e + ViewBadAlloc ViewCopy_a ViewCopy_b + ViewCopy_c ViewCtorDimMatch ViewEmptyRuntimeUnmanaged ViewHooks @@ -245,11 +268,21 @@ foreach(Tag Threads;Serial;OpenMP;Cuda;HPX;OpenMPTarget;OpenACC;HIP;SYCL) ViewMapping_subview ViewMemoryAccessViolation ViewOfClass + ViewOfViews ViewOutOfBoundsAccess ViewResize WorkGraph WithoutInitializing ) + # Workaround to internal compiler error with intel classic compilers + # when using -no-ip flag in ViewCopy_c + # See issue: https://github.com/kokkos/kokkos/issues/7084 + IF(KOKKOS_CXX_COMPILER_ID STREQUAL Intel) + LIST(REMOVE_ITEM ${Tag}_TESTNAMES2A + ViewCopy_c + ) + endif() + foreach(Name IN LISTS ${Tag}_TESTNAMES2A) set(file ${dir}/Test${Tag}_${Name}.cpp) # Write to a temporary intermediate file and call configure_file to avoid # updating timestamps triggering unnecessary rebuilds on subsequent cmake runs. @@ -353,6 +386,7 @@ foreach(PairDeviceSpace HIP-HostPinned;HIP-Managed;Cuda-HostPinned;Cuda-UVM;SYCL ViewAPI_e ViewCopy_a ViewCopy_b + ViewCopy_c ViewMapping_a ViewMapping_b ViewMapping_subview @@ -648,12 +682,6 @@ if(Kokkos_ENABLE_SERIAL) UnitTestMainInit.cpp ${Serial_SOURCES2} ) - KOKKOS_ADD_EXECUTABLE_AND_TEST( - CoreUnitTest_SerialGraph - SOURCES - UnitTestMainInit.cpp - serial/TestSerial_Graph.cpp - ) endif() if(Kokkos_ENABLE_THREADS) @@ -681,12 +709,6 @@ if (Kokkos_ENABLE_OPENMP) UnitTestMain.cpp openmp/TestOpenMP_InterOp.cpp ) - KOKKOS_ADD_EXECUTABLE_AND_TEST( - CoreUnitTest_OpenMPGraph - SOURCES - UnitTestMainInit.cpp - openmp/TestOpenMP_Graph.cpp - ) endif() if(Kokkos_ENABLE_HPX) @@ -794,12 +816,6 @@ if(Kokkos_ENABLE_CUDA) UnitTestMainInit.cpp cuda/TestCuda_InterOp_StreamsMultiGPU.cpp ) - KOKKOS_ADD_EXECUTABLE_AND_TEST( - CoreUnitTest_CudaGraph - SOURCES - UnitTestMainInit.cpp - cuda/TestCuda_Graph.cpp - ) endif() if(Kokkos_ENABLE_HIP) @@ -827,12 +843,6 @@ if(Kokkos_ENABLE_HIP) UnitTestMain.cpp hip/TestHIP_InterOp_Streams.cpp ) - KOKKOS_ADD_EXECUTABLE_AND_TEST( - UnitTest_HIPGraph - SOURCES - UnitTestMainInit.cpp - hip/TestHIP_Graph.cpp - ) endif() if(Kokkos_ENABLE_SYCL) @@ -902,15 +912,21 @@ if(Kokkos_ENABLE_SYCL) KOKKOS_ADD_EXECUTABLE_AND_TEST( CoreUnitTest_SYCLInterOpInit_Context SOURCES - UnitTestMainInit.cpp + UnitTestMainInit.cpp sycl/TestSYCL_InterOp_Init_Context.cpp ) KOKKOS_ADD_EXECUTABLE_AND_TEST( CoreUnitTest_SYCLInterOpStreams SOURCES - UnitTestMain.cpp + UnitTestMain.cpp sycl/TestSYCL_InterOp_Streams.cpp ) + KOKKOS_ADD_EXECUTABLE_AND_TEST( + CoreUnitTest_SYCLInterOpStreamsMultiGPU + SOURCES + UnitTestMainInit.cpp + sycl/TestSYCL_InterOp_StreamsMultiGPU.cpp + ) endif() SET(DEFAULT_DEVICE_SOURCES @@ -993,6 +1009,13 @@ KOKKOS_ADD_EXECUTABLE_AND_TEST( UnitTest_PushFinalizeHook.cpp ) +KOKKOS_ADD_EXECUTABLE_AND_TEST( + CoreUnitTest_ScopeGuard + SOURCES + UnitTestMain.cpp + UnitTest_ScopeGuard.cpp +) + # This test is intended for development and debugging by putting code # into TestDefaultDeviceDevelop.cpp. By default its empty. KOKKOS_ADD_EXECUTABLE_AND_TEST( @@ -1002,23 +1025,35 @@ KOKKOS_ADD_EXECUTABLE_AND_TEST( default/TestDefaultDeviceDevelop.cpp ) -# This test is special, because it passes exactly when it prints the -# message "PASSED: I am the custom std::terminate handler.", AND calls -# std::terminate. This means that we can't use -# KOKKOS_ADD_EXECUTABLE_AND_TEST. See GitHub issue #2147. +# With MSVC, the terminate handler is called and prints the message but the +# program does not seem to exit and we get a timeout with ctest. +if (NOT WIN32) + # This test is special, because it passes exactly when it prints the + # message "PASSED: I am the custom std::terminate handler.", AND calls + # std::terminate. This means that we can't use + # KOKKOS_ADD_EXECUTABLE_AND_TEST. See GitHub issue #2147. + KOKKOS_ADD_TEST_EXECUTABLE( + CoreUnitTest_PushFinalizeHookTerminate + SOURCES UnitTest_PushFinalizeHook_terminate.cpp + ) + add_test( + NAME Kokkos_CoreUnitTest_PushFinalizeHookTerminateRegex + COMMAND ${CMAKE_COMMAND} -E env $ + ) + set_property( + TEST Kokkos_CoreUnitTest_PushFinalizeHookTerminateRegex + PROPERTY PASS_REGULAR_EXPRESSION "PASSED: I am the custom std::terminate handler." + ) + add_test( + NAME Kokkos_CoreUnitTest_PushFinalizeHookTerminateFails + COMMAND ${CMAKE_COMMAND} -E env $ + ) + set_property( + TEST Kokkos_CoreUnitTest_PushFinalizeHookTerminateFails + PROPERTY WILL_FAIL TRUE + ) +endif() -KOKKOS_ADD_TEST_EXECUTABLE( push_finalize_hook_terminate - SOURCES UnitTest_PushFinalizeHook_terminate.cpp -) - -KOKKOS_ADD_ADVANCED_TEST( CoreUnitTest_PushFinalizeHook_terminate - TEST_0 - EXEC push_finalize_hook_terminate - NUM_MPI_PROCS 1 - PASS_REGULAR_EXPRESSION - "PASSED: I am the custom std::terminate handler." - ALWAYS_FAIL_ON_ZERO_RETURN -) if(KOKKOS_ENABLE_TUNING) KOKKOS_ADD_EXECUTABLE_AND_TEST( CoreUnitTest_TuningBuiltins @@ -1243,7 +1278,7 @@ if (NOT KOKKOS_HAS_TRILINOS) ) add_test( NAME Kokkos_CoreUnitTest_DeviceAndThreads - COMMAND ${Python3_EXECUTABLE} -m unittest -v $/TestDeviceAndThreads.py + COMMAND ${Python3_EXECUTABLE} $/TestDeviceAndThreads.py -v ) endif() endif() diff --git a/lib/kokkos/core/unit_test/Makefile b/lib/kokkos/core/unit_test/Makefile index 202809d3fc..a4d65687e5 100644 --- a/lib/kokkos/core/unit_test/Makefile +++ b/lib/kokkos/core/unit_test/Makefile @@ -62,7 +62,7 @@ else STACK_TRACE_TERMINATE_FILTER := endif -TESTS = AtomicOperations_int AtomicOperations_unsignedint AtomicOperations_longint AtomicOperations_unsignedlongint AtomicOperations_longlongint AtomicOperations_double AtomicOperations_float AtomicOperations_complexdouble AtomicOperations_complexfloat AtomicViews Atomics BlockSizeDeduction Concepts Complex Crs DeepCopyAlignment FunctorAnalysis Init LocalDeepCopy MDRange_a MDRange_b MDRange_c MDRange_d MDRange_e MDRange_f Other ParallelScanRangePolicy RangePolicy RangePolicyRequire Reductions Reducers_a Reducers_b Reducers_c Reducers_d Reducers_e Reductions_DeviceView SharedAlloc TeamBasic TeamReductionScan TeamScratch TeamTeamSize TeamVectorRange UniqueToken ViewAPI_a ViewAPI_b ViewAPI_c ViewAPI_d ViewAPI_e ViewCopy_a ViewCopy_b ViewLayoutStrideAssignment ViewMapping_a ViewMapping_b ViewMapping_subview ViewOfClass WorkGraph View_64bit ViewResize +TESTS = AtomicOperations_int AtomicOperations_unsignedint AtomicOperations_longint AtomicOperations_unsignedlongint AtomicOperations_longlongint AtomicOperations_double AtomicOperations_float AtomicOperations_complexdouble AtomicOperations_complexfloat AtomicViews Atomics BlockSizeDeduction Concepts Complex Crs DeepCopyAlignment FunctorAnalysis Init LocalDeepCopy MDRange_a MDRange_b MDRange_c MDRange_d MDRange_e MDRange_f Other ParallelScanRangePolicy RangePolicy RangePolicyRequire Reductions Reducers_a Reducers_b Reducers_c Reducers_d Reducers_e Reductions_DeviceView SharedAlloc TeamBasic TeamReductionScan TeamScratch TeamTeamSize TeamVectorRange UniqueToken ViewAPI_a ViewAPI_b ViewAPI_c ViewAPI_d ViewAPI_e ViewCopy_a ViewCopy_b ViewCopy_c ViewLayoutStrideAssignment ViewMapping_a ViewMapping_b ViewMapping_subview ViewOfClass WorkGraph View_64bit ViewResize tmp := $(foreach device, $(KOKKOS_DEVICELIST), \ tmp2 := $(foreach test, $(TESTS), \ @@ -73,7 +73,7 @@ tmp := $(foreach device, $(KOKKOS_DEVICELIST), \ ) \ ) -GPU_SPACE_TESTS = SharedAlloc ViewAPI_a ViewAPI_b ViewAPI_c ViewAPI_d ViewAPI_e ViewCopy_a ViewCopy_b ViewMapping_a ViewMapping_b ViewMapping_subview +GPU_SPACE_TESTS = SharedAlloc ViewAPI_a ViewAPI_b ViewAPI_c ViewAPI_d ViewAPI_e ViewCopy_a ViewCopy_b ViewCopy_c ViewMapping_a ViewMapping_b ViewMapping_subview SUBVIEW_TESTS = SubView_a SubView_b SubView_c01 SubView_c02 SubView_c03 SubView_c04 SubView_c05 SubView_c06 SubView_c07 SubView_c08 SubView_c09 SubView_c10 SubView_c11 SubView_c12 SubView_c13 @@ -110,14 +110,14 @@ ifeq ($(KOKKOS_INTERNAL_USE_CUDA), 1) OBJ_CUDA += TestCuda_Init.o OBJ_CUDA += TestCuda_SharedAlloc.o TestCudaUVM_SharedAlloc.o TestCudaHostPinned_SharedAlloc.o OBJ_CUDA += TestCuda_RangePolicy.o TestCuda_RangePolicyRequire.o - OBJ_CUDA += TestCuda_ViewAPI_a.o TestCuda_ViewAPI_b.o TestCuda_ViewAPI_c.o TestCuda_ViewAPI_d.o TestCuda_ViewAPI_e.o TestCuda_ViewCopy_a.o TestCuda_ViewCopy_b.o + OBJ_CUDA += TestCuda_ViewAPI_a.o TestCuda_ViewAPI_b.o TestCuda_ViewAPI_c.o TestCuda_ViewAPI_d.o TestCuda_ViewAPI_e.o TestCuda_ViewCopy_a.o TestCuda_ViewCopy_b.o TestCuda_ViewCopy_c.o OBJ_CUDA += TestCuda_DeepCopyAlignment.o OBJ_CUDA += TestCuda_ViewMapping_a.o TestCuda_ViewMapping_b.o TestCuda_ViewMapping_subview.o TestCuda_ViewResize.o TestCuda_ViewLayoutStrideAssignment.o OBJ_CUDA += TestCudaUVM_ViewAPI_a.o TestCudaUVM_ViewAPI_b.o TestCudaUVM_ViewAPI_c.o TestCudaUVM_ViewAPI_d.o TestCudaUVM_ViewAPI_e.o - OBJ_CUDA += TestCudaUVM_ViewCopy_a.o TestCudaUVM_ViewCopy_b.o + OBJ_CUDA += TestCudaUVM_ViewCopy_a.o TestCudaUVM_ViewCopy_b.o TestCudaUVM_ViewCopy_c.o OBJ_CUDA += TestCudaUVM_ViewMapping_a.o TestCudaUVM_ViewMapping_b.o TestCudaUVM_ViewMapping_subview.o OBJ_CUDA += TestCudaHostPinned_ViewAPI_a.o TestCudaHostPinned_ViewAPI_b.o TestCudaHostPinned_ViewAPI_c.o TestCudaHostPinned_ViewAPI_d.o TestCudaHostPinned_ViewAPI_e.o - OBJ_CUDA += TestCudaHostPinned_ViewCopy_a.o TestCudaHostPinned_ViewCopy_b.o + OBJ_CUDA += TestCudaHostPinned_ViewCopy_a.o TestCudaHostPinned_ViewCopy_b.o TestCudaHostPinned_ViewCopy_c.o OBJ_CUDA += TestCudaHostPinned_ViewMapping_a.o TestCudaHostPinned_ViewMapping_b.o TestCudaHostPinned_ViewMapping_subview.o OBJ_CUDA += TestCuda_View_64bit.o OBJ_CUDA += TestCuda_ViewOfClass.o @@ -162,7 +162,7 @@ ifeq ($(KOKKOS_INTERNAL_USE_THREADS), 1) OBJ_THREADS += TestThreads_RangePolicy.o TestThreads_RangePolicyRequire.o OBJ_THREADS += TestThreads_View_64bit.o OBJ_THREADS += TestThreads_ViewAPI_a.o TestThreads_ViewAPI_b.o TestThreads_ViewAPI_c.o TestThreads_ViewAPI_d.o TestThreads_ViewAPI_e.o - OBJ_THREADS += TestThreads_ViewCopy_a.o TestThreads_ViewCopy_b.o + OBJ_THREADS += TestThreads_ViewCopy_a.o TestThreads_ViewCopy_b.o TestThreads_ViewCopy_c.o OBJ_THREADS += TestThreads_DeepCopyAlignment.o OBJ_THREADS += TestThreads_ViewMapping_a.o TestThreads_ViewMapping_b.o TestThreads_ViewMapping_subview.o TestThreads_ViewResize.o TestThreads_ViewLayoutStrideAssignment.o OBJ_THREADS += TestThreads_ViewOfClass.o @@ -198,7 +198,7 @@ ifeq ($(KOKKOS_INTERNAL_USE_OPENMP), 1) OBJ_OPENMP += TestOpenMP_RangePolicy.o TestOpenMP_RangePolicyRequire.o OBJ_OPENMP += TestOpenMP_View_64bit.o OBJ_OPENMP += TestOpenMP_ViewAPI_a.o TestOpenMP_ViewAPI_b.o TestOpenMP_ViewAPI_c.o TestOpenMP_ViewAPI_d.o TestOpenMP_ViewAPI_e.o - OBJ_OPENMP += TestOpenMP_DeepCopyAlignment.o TestOpenMP_ViewCopy_a.o TestOpenMP_ViewCopy_b.o + OBJ_OPENMP += TestOpenMP_DeepCopyAlignment.o TestOpenMP_ViewCopy_a.o TestOpenMP_ViewCopy_b.o TestOpenMP_ViewCopy_c.o OBJ_OPENMP += TestOpenMP_ViewMapping_a.o TestOpenMP_ViewMapping_b.o TestOpenMP_ViewMapping_subview.o TestOpenMP_ViewResize.o TestOpenMP_ViewLayoutStrideAssignment.o OBJ_OPENMP += TestOpenMP_ViewOfClass.o OBJ_OPENMP += TestOpenMP_SubView_a.o TestOpenMP_SubView_b.o @@ -237,7 +237,7 @@ ifeq ($(KOKKOS_INTERNAL_USE_OPENMPTARGET), 1) #OBJ_OPENMPTARGET += TestOpenMPTarget_SharedAlloc.o OBJ_OPENMPTARGET += TestOpenMPTarget_RangePolicy.o OBJ_OPENMPTARGET += TestOpenMPTarget_ViewAPI_a.o TestOpenMPTarget_ViewAPI_b.o TestOpenMPTarget_ViewAPI_c.o TestOpenMPTarget_ViewAPI_d.o #Some commented out code - #OBJ_OPENMPTARGET += TestOpenMPTarget_ViewAPI_e.o TestOpenMPTarget_ViewCopy_a.o TestOpenMPTarget_ViewCopy_b.o + #OBJ_OPENMPTARGET += TestOpenMPTarget_ViewAPI_e.o TestOpenMPTarget_ViewCopy_a.o TestOpenMPTarget_ViewCopy_b.o TestOpenMPTarget_ViewCopy_c.o OBJ_OPENMPTARGET += TestOpenMPTarget_DeepCopyAlignment.o OBJ_OPENMPTARGET += TestOpenMPTarget_ViewMapping_a.o OBJ_OPENMPTARGET += TestOpenMPTarget_ViewMapping_b.o @@ -292,7 +292,7 @@ ifeq ($(KOKKOS_INTERNAL_USE_HIP), 1) OBJ_HIP += TestHIP_Memory_Requirements.o OBJ_HIP += TestHIP_ParallelScanRangePolicy.o OBJ_HIP += TestHIPHostPinned_ViewAPI_a.o TestHIPHostPinned_ViewAPI_b.o TestHIPHostPinned_ViewAPI_c.o TestHIPHostPinned_ViewAPI_d.o TestHIPHostPinned_ViewAPI_e.o - OBJ_HIP += TestHIPHostPinned_ViewCopy_a.o TestHIPHostPinned_ViewCopy_b.o + OBJ_HIP += TestHIPHostPinned_ViewCopy_a.o TestHIPHostPinned_ViewCopy_b.o TestHIPHostPinned_ViewCopy_c.o OBJ_HIP += TestHIPHostPinned_ViewMapping_a.o TestHIPHostPinned_ViewMapping_b.o TestHIPHostPinned_ViewMapping_subview.o TARGETS += KokkosCore_UnitTest_HIP @@ -307,7 +307,7 @@ ifeq ($(KOKKOS_INTERNAL_USE_HPX), 1) OBJ_HPX += TestHPX_RangePolicy.o TestHPX_RangePolicyRequire.o OBJ_HPX += TestHPX_View_64bit.o OBJ_HPX += TestHPX_ViewAPI_a.o TestHPX_ViewAPI_b.o TestHPX_ViewAPI_c.o TestHPX_ViewAPI_d.o TestHPX_ViewAPI_e.o - OBJ_HPX += TestHPX_ViewCopy_a.o TestHPX_ViewCopy_b.o + OBJ_HPX += TestHPX_ViewCopy_a.o TestHPX_ViewCopy_b.o TestHPX_ViewCopy_c.o OBJ_HPX += TestHPX_ViewMapping_a.o TestHPX_ViewMapping_b.o TestHPX_ViewMapping_subview.o TestHPX_ViewResize.o OBJ_HPX += TestHPX_ViewOfClass.o OBJ_HPX += TestHPX_SubView_a.o TestHPX_SubView_b.o @@ -347,7 +347,7 @@ ifeq ($(KOKKOS_INTERNAL_USE_SERIAL), 1) OBJ_SERIAL += TestSerial_RangePolicy.o TestSerial_RangePolicyRequire.o OBJ_SERIAL += TestSerial_View_64bit.o OBJ_SERIAL += TestSerial_ViewAPI_a.o TestSerial_ViewAPI_b.o TestSerial_ViewAPI_c.o TestSerial_ViewAPI_d.o TestSerial_ViewAPI_e.o - OBJ_SERIAL += TestSerial_DeepCopyAlignment.o TestSerial_ViewCopy_a.o TestSerial_ViewCopy_b.o + OBJ_SERIAL += TestSerial_DeepCopyAlignment.o TestSerial_ViewCopy_a.o TestSerial_ViewCopy_b.o TestSerial_ViewCopy_c.o OBJ_SERIAL += TestSerial_ViewMapping_a.o TestSerial_ViewMapping_b.o TestSerial_ViewMapping_subview.o TestSerial_ViewResize.o TestSerial_ViewLayoutStrideAssignment.o OBJ_SERIAL += TestSerial_ViewOfClass.o OBJ_SERIAL += TestSerial_SubView_a.o TestSerial_SubView_b.o diff --git a/lib/kokkos/core/unit_test/TestAggregate.hpp b/lib/kokkos/core/unit_test/TestAggregate.hpp deleted file mode 100644 index f1316a7426..0000000000 --- a/lib/kokkos/core/unit_test/TestAggregate.hpp +++ /dev/null @@ -1,108 +0,0 @@ -//@HEADER -// ************************************************************************ -// -// Kokkos v. 4.0 -// Copyright (2022) National Technology & Engineering -// Solutions of Sandia, LLC (NTESS). -// -// Under the terms of Contract DE-NA0003525 with NTESS, -// the U.S. Government retains certain rights in this software. -// -// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. -// See https://kokkos.org/LICENSE for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//@HEADER - -#ifndef TEST_AGGREGATE_HPP -#define TEST_AGGREGATE_HPP - -#include - -namespace Test { - -template -void TestViewAggregate() { - using value_type = Kokkos::Array; - using analysis_1d = - Kokkos::Impl::ViewDataAnalysis; - - static_assert( - std::is_same >::value); - - using a32_traits = Kokkos::ViewTraits; - using flat_traits = - Kokkos::ViewTraits; - - static_assert( - std::is_same >::value); - static_assert( - std::is_same::value); - static_assert(a32_traits::rank == 2); - static_assert(a32_traits::rank_dynamic == 2); - - static_assert(std::is_void::value); - static_assert(flat_traits::rank == 3); - static_assert(flat_traits::rank_dynamic == 2); - static_assert(flat_traits::dimension::N2 == 32); - - using a32_type = Kokkos::View **, DeviceType>; - using a32_flat_type = typename a32_type::array_type; - - static_assert(std::is_same::value); - static_assert(std::is_same::value); - static_assert(a32_type::rank == 2); - static_assert(a32_flat_type::rank == 3); - - a32_type x("test", 4, 5); - a32_flat_type y(x); - - ASSERT_EQ(x.extent(0), 4u); - ASSERT_EQ(x.extent(1), 5u); - ASSERT_EQ(y.extent(0), 4u); - ASSERT_EQ(y.extent(1), 5u); - ASSERT_EQ(y.extent(2), 32u); - - // Initialize arrays from brace-init-list as for std::array. - // - // Comment: Clang will issue the following warning if we don't use double - // braces here (one for initializing the Kokkos::Array and one for - // initializing the sub-aggreagate C-array data member), - // - // warning: suggest braces around initialization of subobject - // - // but single brace syntax would be valid as well. - Kokkos::Array aggregate_initialization_syntax_1 = {{1.41, 3.14}}; - ASSERT_FLOAT_EQ(aggregate_initialization_syntax_1[0], 1.41); - ASSERT_FLOAT_EQ(aggregate_initialization_syntax_1[1], 3.14); - - Kokkos::Array aggregate_initialization_syntax_2{ - {0, 1, 2}}; // since C++11 - for (int i = 0; i < 3; ++i) { - ASSERT_EQ(aggregate_initialization_syntax_2[i], i); - } - - // Note that this is a valid initialization. - Kokkos::Array initialized_with_one_argument_missing = {{255, 255}}; - for (int i = 0; i < 2; ++i) { - ASSERT_DOUBLE_EQ(initialized_with_one_argument_missing[i], 255); - } - // But the following line would not compile - // Kokkos::Array< double, 3 > initialized_with_too_many{ { 1, 2, 3, 4 } }; - - // The code below must compile for zero-sized arrays. - using T = float; - - constexpr int N = 0; - Kokkos::Array a; - for (int i = 0; i < N; ++i) { - a[i] = T(); - } -} - -TEST(TEST_CATEGORY, view_aggregate) { TestViewAggregate(); } - -} // namespace Test - -#endif /* #ifndef TEST_AGGREGATE_HPP */ diff --git a/lib/kokkos/core/unit_test/TestArray.cpp b/lib/kokkos/core/unit_test/TestArray.cpp index 673d0036b7..cb713a1782 100644 --- a/lib/kokkos/core/unit_test/TestArray.cpp +++ b/lib/kokkos/core/unit_test/TestArray.cpp @@ -15,9 +15,19 @@ //@HEADER #include +#include namespace { +// nvcc errors on variables only used in static_asserts +// Passing those variables to this function should eliminate the warning +template +KOKKOS_FUNCTION constexpr void maybe_unused(Ts&&...) {} + +template +using equality_comparable = + decltype(std::declval() == std::declval()); + KOKKOS_FUNCTION constexpr bool test_array() { constexpr Kokkos::Array a{{1, 2}}; @@ -49,17 +59,6 @@ KOKKOS_FUNCTION constexpr bool test_array_structured_binding_support() { static_assert(test_array_structured_binding_support()); -template -KOKKOS_FUNCTION constexpr bool is_equal(L const& l, R const& r) { - if (std::size(l) != std::size(r)) return false; - - for (size_t i = 0; i != std::size(l); ++i) { - if (l[i] != r[i]) return false; - } - - return true; -} - // Disable ctad test for intel versions < 2021, see issue #6702 #if !defined(KOKKOS_COMPILER_INTEL) || KOKKOS_COMPILER_INTEL >= 2021 KOKKOS_FUNCTION constexpr bool test_array_ctad() { @@ -67,10 +66,180 @@ KOKKOS_FUNCTION constexpr bool test_array_ctad() { constexpr Kokkos::Array a{1, 2, 3, 5, x}; constexpr Kokkos::Array b{1, 2, 3, 5, x}; - return std::is_same_v && is_equal(a, b); + return std::is_same_v && a == b; } static_assert(test_array_ctad()); #endif +KOKKOS_FUNCTION constexpr bool test_array_aggregate_initialization() { + // Initialize arrays from brace-init-list as for std::array. + + Kokkos::Array aggregate_initialization_syntax_1 = {1.41f, 3.14f}; + if ((aggregate_initialization_syntax_1[0] != 1.41f) || + (aggregate_initialization_syntax_1[1] != 3.14f)) + return false; + + Kokkos::Array aggregate_initialization_syntax_2{ + {0, 1, 2}}; // since C++11 + if ((aggregate_initialization_syntax_2[0] != 0) || + (aggregate_initialization_syntax_2[1] != 1) || + (aggregate_initialization_syntax_2[2] != 2)) + return false; + + // Note that this is a valid initialization. + Kokkos::Array initialized_with_one_argument_missing = {{255, 255}}; + if ((initialized_with_one_argument_missing[0] != 255) || + (initialized_with_one_argument_missing[1] != 255) || + (initialized_with_one_argument_missing[2] != 0)) + return false; + + // But the following line would not compile + // Kokkos::Array< double, 3 > initialized_with_too_many{ { 1, 2, 3, 4 } }; + + return true; +} + +static_assert(test_array_aggregate_initialization()); + +// A few compilers, such as GCC 8.4, were erroring out when the function below +// appeared in a constant expression because +// Kokkos::Array::operator[] is non-constexpr. The issue +// disappears with GCC 9.1 (https://godbolt.org/z/TG4TEef1b). As a workaround, +// the static_assert was dropped and the [[maybe_unused]] is used as an attempt +// to silent warnings that the function is never used. +[[maybe_unused]] KOKKOS_FUNCTION void test_array_zero_sized() { + using T = float; + + // The code below must compile for zero-sized arrays. + constexpr int N = 0; + Kokkos::Array a; + for (int i = 0; i < N; ++i) { + a[i] = T(); + } +} + +constexpr bool test_array_const_qualified_element_type() { + Kokkos::Array a{255}; + return a[0] == 255; +} + +static_assert(test_array_const_qualified_element_type()); + +// User-defined type providing a sepcialization of kokkos_swap +struct MyInt { + int i; + + private: + friend constexpr KOKKOS_FUNCTION void kokkos_swap(MyInt& lhs, + MyInt& rhs) noexcept { + lhs.i = 255; + rhs.i = 127; + } +}; + +constexpr bool test_array_specialization_kokkos_swap() { + Kokkos::Array a{MyInt{1}, MyInt{2}}; + Kokkos::Array b{MyInt{11}, MyInt{22}}; + + // sanity check + if (a[0].i != 1 || a[1].i != 2 || b[0].i != 11 || b[1].i != 22) { + return false; + } + + using Kokkos::kokkos_swap; + kokkos_swap(a, b); + + // check that the user-definied kokkos_swap(MyInt) overload was called + if (a[0].i != 255 || a[1].i != 255 || b[0].i != 127 || b[1].i != 127) { + return false; + } + + return true; +} + +static_assert(test_array_specialization_kokkos_swap()); + +constexpr bool test_to_array() { + // copies a string literal + [[maybe_unused]] auto a1 = Kokkos::to_array("foo"); + static_assert(a1.size() == 4); + maybe_unused(a1); + + // deduces both element type and length + [[maybe_unused]] auto a2 = Kokkos::to_array({0, 2, 1, 3}); + static_assert(std::is_same_v>); + maybe_unused(a2); + +// gcc8, icc, and nvcc 11.3 do not support the implicit conversion +#if !(defined(KOKKOS_COMPILER_GNU) && (KOKKOS_COMPILER_GNU < 910)) && \ + !(defined(KOKKOS_COMPILER_INTEL) && (KOKKOS_COMPILER_INTEL < 2021)) && \ + !(defined(KOKKOS_COMPILER_NVCC) && (KOKKOS_COMPILER_NVCC < 1140)) + // deduces length with element type specified + // implicit conversion happens + [[maybe_unused]] auto a3 = Kokkos::to_array({0, 1, 3}); + static_assert(std::is_same_v>); + maybe_unused(a3); +#endif + + return true; +} + +static_assert(test_to_array()); + +constexpr bool test_array_equality_comparable() { + using C0 = Kokkos::Array; + using C2 = Kokkos::Array; + using C3 = Kokkos::Array; + using I0 = Kokkos::Array; + using I2 = Kokkos::Array; + using I3 = Kokkos::Array; + + static_assert(Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + + static_assert(!Kokkos::is_detected_v); + static_assert(Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + + static_assert(!Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + static_assert(Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + + static_assert(!Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + static_assert(Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + + static_assert(!Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + static_assert(Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + + static_assert(!Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + static_assert(!Kokkos::is_detected_v); + static_assert(Kokkos::is_detected_v); + + return true; +} + +static_assert(test_array_equality_comparable()); + } // namespace diff --git a/lib/kokkos/core/unit_test/TestArrayOps.hpp b/lib/kokkos/core/unit_test/TestArrayOps.hpp index 0652857271..29a452b660 100644 --- a/lib/kokkos/core/unit_test/TestArrayOps.hpp +++ b/lib/kokkos/core/unit_test/TestArrayOps.hpp @@ -92,6 +92,31 @@ TEST(TEST_CATEGORY, array_element_access) { ASSERT_EQ(ca.data()[index], a[index]); } +TEST(TEST_CATEGORY, array_operator_equal) { + using A = Kokkos::Array; + constexpr A a{{3, 5}}; + constexpr A b{{3, 5}}; + constexpr A c{{5, 3}}; + + static_assert(a == b); + static_assert(!(a == c)); + static_assert(a != c); + + ASSERT_TRUE(a == b); + ASSERT_FALSE(a == c); + ASSERT_TRUE(a != c); + + using E = Kokkos::Array; + constexpr E e; + constexpr E f; + + static_assert(e == f); + static_assert(!(e != f)); + + ASSERT_TRUE(e == f); + ASSERT_FALSE(e != f); +} + TEST(TEST_CATEGORY, array_zero_capacity) { using A = Kokkos::Array; A e; @@ -111,6 +136,8 @@ TEST(TEST_CATEGORY, array_zero_data_nullptr) { ASSERT_EQ(ce.data(), nullptr); } +#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4 +KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_PUSH() TEST(TEST_CATEGORY, array_contiguous_capacity) { using A = Kokkos::Array::contiguous>; @@ -389,5 +416,7 @@ TEST(TEST_CATEGORY, array_strided_assignment) { ASSERT_EQ(e.max_size(), std::size(ee) / eStride); ASSERT_EQ(e[0], ee[0]); } +KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_POP() +#endif } // namespace diff --git a/lib/kokkos/core/unit_test/TestAtomicOperations.hpp b/lib/kokkos/core/unit_test/TestAtomicOperations.hpp index cd7ba47aa1..957ba9a7aa 100644 --- a/lib/kokkos/core/unit_test/TestAtomicOperations.hpp +++ b/lib/kokkos/core/unit_test/TestAtomicOperations.hpp @@ -459,9 +459,11 @@ bool AtomicOperationsTestIntegralType(int old_val_in, int update_in, int test) { case 12: return true; #else case 11: - return update_in >= 0 ? atomic_op_test( - old_val, update) - : true; + return (std::make_signed_t(update_in) >= 0 && + std::make_signed_t(old_val) >= 0) + ? atomic_op_test(old_val, + update) + : true; case 12: return update_in >= 0 ? atomic_op_test( old_val, update) diff --git a/lib/kokkos/core/unit_test/TestBitManipulationBuiltins.hpp b/lib/kokkos/core/unit_test/TestBitManipulationBuiltins.hpp index 2f3bcfe817..fe015404f1 100644 --- a/lib/kokkos/core/unit_test/TestBitManipulationBuiltins.hpp +++ b/lib/kokkos/core/unit_test/TestBitManipulationBuiltins.hpp @@ -827,12 +827,6 @@ struct TestBitCastFunction { } } -#if defined(KOKKOS_ENABLE_CUDA) && \ - defined(KOKKOS_COMPILER_NVHPC) // FIXME_NVHPC 23.7 - if constexpr (std::is_same_v) { - return; - } -#endif struct S { int i; diff --git a/lib/kokkos/core/unit_test/TestComplex.hpp b/lib/kokkos/core/unit_test/TestComplex.hpp index 5501a35b7f..ef6a21cd37 100644 --- a/lib/kokkos/core/unit_test/TestComplex.hpp +++ b/lib/kokkos/core/unit_test/TestComplex.hpp @@ -15,9 +15,26 @@ //@HEADER #include -#include #include +// Suppress "'long double' is treated as 'double' in device code" +#ifdef KOKKOS_COMPILER_NVCC +#ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ +#pragma nv_diagnostic push +#pragma nv_diag_suppress 20208 +#else +#ifdef __CUDA_ARCH__ +#pragma diagnostic push +#pragma diag_suppress 20208 +#endif +#endif +#endif + +namespace { +template +KOKKOS_FUNCTION constexpr void maybe_unused(Ts &&...) noexcept {} +} // namespace + namespace Test { // Test construction and assignment @@ -532,4 +549,151 @@ TEST(TEST_CATEGORY, complex_operations_arithmetic_types_overloads) { Kokkos::complex>::value)); } +template +struct TestComplexStructuredBindings { + using exec_space = ExecSpace; + using value_type = double; + using complex_type = Kokkos::complex; + using device_view_type = Kokkos::View; + using host_view_type = typename device_view_type::HostMirror; + + device_view_type d_results; + host_view_type h_results; + + // tuple_size + static_assert(std::is_same_v::type, + std::integral_constant>); + + // tuple_element + static_assert( + std::is_same_v, value_type>); + static_assert( + std::is_same_v, value_type>); + + static void testgetreturnreferencetypes() { + complex_type m; + const complex_type c; + + // get lvalue + complex_type &ml = m; + static_assert(std::is_same_v(ml)), value_type &>); + static_assert(std::is_same_v(ml)), value_type &>); + + // get rvalue + complex_type &&mr = std::move(m); + static_assert( + std::is_same_v(std::move(mr))), value_type &&>); + static_assert( + std::is_same_v(std::move(mr))), value_type &&>); + + // get const lvalue + const complex_type &cl = c; + static_assert( + std::is_same_v(cl)), value_type const &>); + static_assert( + std::is_same_v(cl)), value_type const &>); + + // get const rvalue + complex_type const &&cr = std::move(c); + static_assert(std::is_same_v(std::move(cr))), + value_type const &&>); + static_assert(std::is_same_v(std::move(cr))), + value_type const &&>); + + maybe_unused(m, c, ml, mr, cl, cr); + } + + void testit() { + testgetreturnreferencetypes(); + + d_results = device_view_type("TestComplexStructuredBindings", 6); + h_results = Kokkos::create_mirror_view(d_results); + + Kokkos::parallel_for(Kokkos::RangePolicy(0, 1), *this); + Kokkos::fence(); + Kokkos::deep_copy(h_results, d_results); + + // get lvalue + ASSERT_FLOAT_EQ(h_results[0].real(), 2.); + ASSERT_FLOAT_EQ(h_results[0].imag(), 3.); + + // get rvalue + ASSERT_FLOAT_EQ(h_results[1].real(), 2.); + ASSERT_FLOAT_EQ(h_results[1].imag(), 3.); + + // get const lvalue + ASSERT_FLOAT_EQ(h_results[2].real(), 5.); + ASSERT_FLOAT_EQ(h_results[2].imag(), 7.); + + // get const rvalue + ASSERT_FLOAT_EQ(h_results[3].real(), 5.); + ASSERT_FLOAT_EQ(h_results[3].imag(), 7.); + + // swap real and imaginary + ASSERT_FLOAT_EQ(h_results[4].real(), 11.); + ASSERT_FLOAT_EQ(h_results[4].imag(), 13.); + ASSERT_FLOAT_EQ(h_results[5].real(), 13.); + ASSERT_FLOAT_EQ(h_results[5].imag(), 11.); + } + + KOKKOS_FUNCTION + void operator()(int) const { + complex_type m(2., 3.); + const complex_type c(5., 7.); + + // get lvalue + { + complex_type &ml = m; + auto &[mlr, mli] = ml; + d_results[0] = complex_type(mlr, mli); + } + + // get rvalue + { + complex_type &&mr = std::move(m); + auto &&[mrr, mri] = std::move(mr); + d_results[1] = complex_type(mrr, mri); + } + + // get const lvalue + { + const complex_type &cl = c; + auto &[clr, cli] = cl; + d_results[2] = complex_type(clr, cli); + } + + // get const rvalue + { + complex_type const &&cr = std::move(c); + auto &&[crr, cri] = std::move(cr); + d_results[3] = complex_type(crr, cri); + } + + // swap real and imaginary + { + complex_type z(11., 13.); + d_results[4] = z; + + auto &[zr, zi] = z; + Kokkos::kokkos_swap(zr, zi); + d_results[5] = z; + } + } +}; + +TEST(TEST_CATEGORY, complex_structured_bindings) { + TestComplexStructuredBindings test; + test.testit(); +} + } // namespace Test + +#ifdef KOKKOS_COMPILER_NVCC +#ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ +#pragma nv_diagnostic pop +#else +#ifdef __CUDA_ARCH__ +#pragma diagnostic pop +#endif +#endif +#endif diff --git a/lib/kokkos/core/unit_test/TestExecSpaceThreadSafety.hpp b/lib/kokkos/core/unit_test/TestExecSpaceThreadSafety.hpp new file mode 100644 index 0000000000..a83355c51f --- /dev/null +++ b/lib/kokkos/core/unit_test/TestExecSpaceThreadSafety.hpp @@ -0,0 +1,327 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#include +#include + +namespace { + +#ifdef KOKKOS_ENABLE_OPENMP +template +void run_threaded_test(const Lambda1 l1, const Lambda2 l2) { +#pragma omp parallel num_threads(2) + { + if (omp_get_thread_num() == 0) l1(); + if (omp_get_thread_num() == 1) l2(); + } +} +// We cannot run the multithreaded test when threads or HPX is enabled because +// we cannot launch a thread from inside another thread +#elif !defined(KOKKOS_ENABLE_THREADS) && !defined(KOKKOS_ENABLE_HPX) +template +void run_threaded_test(const Lambda1 l1, const Lambda2 l2) { + std::thread t1(l1); + std::thread t2(l2); + t1.join(); + t2.join(); +} +#else +template +void run_threaded_test(const Lambda1 l1, const Lambda2 l2) { + l1(); + l2(); +} +#endif + +// The idea for all of these tests is to access a View from kernels submitted by +// two different threads to the same execution space instance. If the kernels +// are executed concurrently, we expect to count too many increments. +void run_exec_space_thread_safety_range() { + constexpr int N = 10000000; + constexpr int M = 10; + + Kokkos::View view("view"); + Kokkos::View error("error"); + + auto lambda = [=]() { + TEST_EXECSPACE exec; + for (int j = 0; j < M; ++j) { + Kokkos::parallel_for( + Kokkos::RangePolicy(exec, 0, 1), KOKKOS_LAMBDA(int) { + Kokkos::atomic_store(view.data(), 0); + for (int i = 0; i < N; ++i) Kokkos::atomic_inc(view.data()); + if (Kokkos::atomic_load(view.data()) != N) + Kokkos::atomic_store(error.data(), 1); + }); + } + }; + + run_threaded_test(lambda, lambda); + + auto host_error = + Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, error); + ASSERT_EQ(host_error(), 0); +} + +TEST(TEST_CATEGORY, exec_space_thread_safety_range) { +#ifdef KOKKOS_ENABLE_OPENACC // FIXME_OPENACC + if (std::is_same_v) + GTEST_SKIP() << "skipping since test is known to fail with OpenACC"; +#endif +#ifdef KOKKOS_ENABLE_OPENMPTARGET + if (std::is_same_v) + GTEST_SKIP() << "skipping since test is known to fail for OpenMPTarget"; +#endif + run_exec_space_thread_safety_range(); +} + +void run_exec_space_thread_safety_mdrange() { + constexpr int N = 1000000; + constexpr int M = 10; + + Kokkos::View view("view"); + Kokkos::View error("error"); + + auto lambda = [=]() { + TEST_EXECSPACE exec; + for (int j = 0; j < M; ++j) { + Kokkos::parallel_for( + Kokkos::MDRangePolicy>(exec, {0, 0}, + {1, 1}), + KOKKOS_LAMBDA(int, int) { + Kokkos::atomic_store(view.data(), 0); + for (int i = 0; i < N; ++i) Kokkos::atomic_inc(view.data()); + if (Kokkos::atomic_load(view.data()) != N) + Kokkos::atomic_store(error.data(), 1); + }); + } + }; + + run_threaded_test(lambda, lambda); + + auto host_error = + Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, error); + ASSERT_EQ(host_error(), 0); +} + +TEST(TEST_CATEGORY, exec_space_thread_safety_mdrange) { +#ifdef KOKKOS_ENABLE_OPENMPTARGET + if (std::is_same_v) + GTEST_SKIP() << "skipping since test is known to fail for OpenMPTarget"; +#endif + run_exec_space_thread_safety_mdrange(); +} + +void run_exec_space_thread_safety_team_policy() { + constexpr int N = 1000000; + constexpr int M = 10; + + Kokkos::View view("view"); + Kokkos::View error("error"); + + auto lambda = [=]() { + TEST_EXECSPACE exec; + for (int j = 0; j < M; ++j) { + Kokkos::parallel_for( + Kokkos::TeamPolicy(exec, 1, 1, 1), + KOKKOS_LAMBDA(const Kokkos::TeamPolicy::member_type + &team_member) { + Kokkos::single(Kokkos::PerTeam(team_member), [=]() { + Kokkos::atomic_store(view.data(), 0); + for (int i = 0; i < N; ++i) Kokkos::atomic_inc(view.data()); + if (Kokkos::atomic_load(view.data()) != N) + Kokkos::atomic_store(error.data(), 1); + }); + }); + } + }; + + run_threaded_test(lambda, lambda); + + auto host_error = + Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, error); + ASSERT_EQ(host_error(), 0); +} + +TEST(TEST_CATEGORY, exec_space_thread_safety_team_policy) { +// FIXME_OPENMPTARGET +#ifdef KOKKOS_ENABLE_OPENMPTARGET + if (std::is_same_v) + GTEST_SKIP() << "skipping for OpenMPTarget since the test is designed to " + "run with vector_length=1"; +#endif + run_exec_space_thread_safety_team_policy(); +} + +void run_exec_space_thread_safety_range_reduce() { + constexpr int N = 1000000; + constexpr int M = 10; + + Kokkos::View view("view"); + Kokkos::View error("error"); + + auto lambda = [=]() { + TEST_EXECSPACE exec; + for (int j = 0; j < M; ++j) { + Kokkos::parallel_reduce( + Kokkos::RangePolicy(exec, 0, 1), + KOKKOS_LAMBDA(int, int &update) { + Kokkos::atomic_store(view.data(), 0); + for (int i = 0; i < N; ++i) Kokkos::atomic_inc(view.data()); + if (Kokkos::atomic_load(view.data()) != N) ++update; + }, + error); + } + exec.fence(); + }; + + run_threaded_test(lambda, lambda); + + auto host_error = + Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, error); + ASSERT_EQ(host_error(), 0); +} + +TEST(TEST_CATEGORY, exec_space_thread_safety_range_reduce) { + run_exec_space_thread_safety_range_reduce(); +} + +void run_exec_space_thread_safety_mdrange_reduce() { + constexpr int N = 1000000; + constexpr int M = 10; + + Kokkos::View view("view"); + Kokkos::View error("error"); + + auto lambda = [=]() { + TEST_EXECSPACE exec; + for (int j = 0; j < M; ++j) { + Kokkos::parallel_reduce( + Kokkos::MDRangePolicy>(exec, {0, 0}, + {1, 1}), + KOKKOS_LAMBDA(int, int, int &update) { + Kokkos::atomic_store(view.data(), 0); + for (int i = 0; i < N; ++i) Kokkos::atomic_inc(view.data()); + if (Kokkos::atomic_load(view.data()) != N) ++update; + }, + error); + } + exec.fence(); + }; + + run_threaded_test(lambda, lambda); + + auto host_error = + Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, error); + ASSERT_EQ(host_error(), 0); +} + +TEST(TEST_CATEGORY, exec_space_thread_safety_mdrange_reduce) { +// FIXME_INTEL +#if defined(KOKKOS_COMPILER_INTEL) && defined(KOKKOS_ENABLE_OPENMP) + if (std::is_same_v) + GTEST_SKIP() << "skipping since test is known to fail for OpenMP using the " + "legacy Intel compiler"; +#endif + run_exec_space_thread_safety_mdrange_reduce(); +} + +void run_exec_space_thread_safety_team_policy_reduce() { + constexpr int N = 1000000; + constexpr int M = 10; + + Kokkos::View view("view"); + Kokkos::View error("error"); + + auto lambda = [=]() { + TEST_EXECSPACE exec; + for (int j = 0; j < M; ++j) { + Kokkos::parallel_reduce( + Kokkos::TeamPolicy(exec, 1, 1, 1), + KOKKOS_LAMBDA(const Kokkos::TeamPolicy::member_type + &team_member, + int &update) { + Kokkos::single(Kokkos::PerTeam(team_member), [=, &update]() { + Kokkos::atomic_store(view.data(), 0); + for (int i = 0; i < N; ++i) Kokkos::atomic_inc(view.data()); + if (Kokkos::atomic_load(view.data()) != N) ++update; + }); + }, + error); + } + }; + run_threaded_test(lambda, lambda); + + auto host_error = + Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, error); + ASSERT_EQ(host_error(), 0); +} + +TEST(TEST_CATEGORY, exec_space_thread_safety_team_policy_reduce) { +// FIXME_OPENMPTARGET +#ifdef KOKKOS_ENABLE_OPENMPTARGET + if (std::is_same_v) + GTEST_SKIP() << "skipping for OpenMPTarget since the test is designed to " + "run with vector_length=1"; +#endif + // FIXME_SYCL +#if defined(KOKKOS_ENABLE_SYCL) && defined(KOKKOS_IMPL_ARCH_NVIDIA_GPU) + if (std::is_same_v) + GTEST_SKIP() << "skipping since test is know to fail with SYCL+Cuda"; +#endif + run_exec_space_thread_safety_team_policy_reduce(); +} + +void run_exec_space_thread_safety_range_scan() { + constexpr int N = 1000000; + constexpr int M = 10; + + Kokkos::View view("view"); + Kokkos::View error("error"); + + auto lambda = [=]() { + TEST_EXECSPACE exec; + for (int j = 0; j < M; ++j) { + Kokkos::parallel_scan( + Kokkos::RangePolicy(exec, 0, 1), + KOKKOS_LAMBDA(int, int &, const bool final) { + if (final) { + Kokkos::atomic_store(view.data(), 0); + for (int i = 0; i < N; ++i) Kokkos::atomic_inc(view.data()); + if (Kokkos::atomic_load(view.data()) != N) + Kokkos::atomic_store(error.data(), 1); + } + }); + } + exec.fence(); + }; + + run_threaded_test(lambda, lambda); + + auto host_error = + Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, error); + ASSERT_EQ(host_error(), 0); +} + +TEST(TEST_CATEGORY, exec_space_thread_safety_range_scan) { +#ifdef KOKKOS_ENABLE_OPENACC // FIXME_OPENACC + if (std::is_same_v) + GTEST_SKIP() << "skipping since test is known to fail with OpenACC"; +#endif + run_exec_space_thread_safety_range_scan(); +} + +} // namespace diff --git a/lib/kokkos/core/unit_test/TestExecutionSpace.hpp b/lib/kokkos/core/unit_test/TestExecutionSpace.hpp index 983a5975af..d4142dee18 100644 --- a/lib/kokkos/core/unit_test/TestExecutionSpace.hpp +++ b/lib/kokkos/core/unit_test/TestExecutionSpace.hpp @@ -44,4 +44,60 @@ TEST(TEST_CATEGORY, execution_space_as_class_data_member) { } #endif +constexpr bool test_execspace_explicit_construction() { +#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4 +#ifdef KOKKOS_ENABLE_SERIAL + static_assert(std::is_convertible_v); +#endif +#ifdef KOKKOS_ENABLE_OPENMP + static_assert(std::is_convertible_v); +#endif +#ifdef KOKKOS_ENABLE_CUDA + static_assert(std::is_convertible_v); +#endif +#ifdef KOKKOS_ENABLE_HIP + static_assert(std::is_convertible_v); +#endif +#ifdef KOKKOS_ENABLE_HPX + static_assert(std::is_convertible_v); + static_assert( + std::is_convertible_v&&, + Kokkos::Experimental::HPX>); +#endif +#else +#ifdef KOKKOS_ENABLE_SERIAL + static_assert(!std::is_convertible_v); +#endif +#ifdef KOKKOS_ENABLE_OPENMP + static_assert(!std::is_convertible_v); +#endif +#ifdef KOKKOS_ENABLE_CUDA + static_assert(!std::is_convertible_v); +#endif +#ifdef KOKKOS_ENABLE_HIP + static_assert(!std::is_convertible_v); +#endif +#ifdef KOKKOS_ENABLE_HPX + static_assert(!std::is_convertible_v); + static_assert(!std::is_convertible_v< + hpx::execution::experimental::unique_any_sender<>&&, + Kokkos::Experimental::HPX>); +#endif +#endif + +#ifdef KOKKOS_ENABLE_OPENACC + static_assert(!std::is_convertible_v); +#endif +#ifdef KOKKOS_ENABLE_SYCL + static_assert( + !std::is_convertible_v); +#endif + + return true; +} + +static_assert(test_execspace_explicit_construction()); + } // namespace diff --git a/lib/kokkos/core/unit_test/TestGraph.hpp b/lib/kokkos/core/unit_test/TestGraph.hpp index 9a36d08f44..f9dc63d30c 100644 --- a/lib/kokkos/core/unit_test/TestGraph.hpp +++ b/lib/kokkos/core/unit_test/TestGraph.hpp @@ -21,6 +21,21 @@ namespace Test { +template +struct NoOpReduceFunctor { + KOKKOS_FUNCTION void operator()(int, ValueType&) const { + Kokkos::abort("Should never be called!"); + } + KOKKOS_FUNCTION void operator()(int, int, ValueType&) const { + Kokkos::abort("Should never be called!"); + } + KOKKOS_FUNCTION void operator()( + const typename Kokkos::TeamPolicy::member_type&, + ValueType&) const { + Kokkos::abort("Should never be called!"); + } +}; + template struct CountTestFunctor { using value_type = int; @@ -66,7 +81,7 @@ struct SetResultToViewFunctor { } }; -struct TEST_CATEGORY_FIXTURE(count_bugs) : public ::testing::Test { +struct TEST_CATEGORY_FIXTURE(graph) : public ::testing::Test { public: using count_functor = CountTestFunctor; using set_functor = SetViewToValueFunctor; @@ -88,7 +103,7 @@ struct TEST_CATEGORY_FIXTURE(count_bugs) : public ::testing::Test { } }; -TEST_F(TEST_CATEGORY_FIXTURE(count_bugs), launch_one) { +TEST_F(TEST_CATEGORY_FIXTURE(graph), launch_one) { auto graph = Kokkos::Experimental::create_graph([&](auto root) { root.then_parallel_for(1, count_functor{count, bugs, 0, 0}); @@ -101,7 +116,7 @@ TEST_F(TEST_CATEGORY_FIXTURE(count_bugs), launch_one) { ASSERT_EQ(0, bugs_host()); } -TEST_F(TEST_CATEGORY_FIXTURE(count_bugs), launch_one_rvalue) { +TEST_F(TEST_CATEGORY_FIXTURE(graph), launch_one_rvalue) { Kokkos::Experimental::create_graph(ex, [&](auto root) { root.then_parallel_for(1, count_functor{count, bugs, 0, 0}); }).submit(); @@ -112,7 +127,17 @@ TEST_F(TEST_CATEGORY_FIXTURE(count_bugs), launch_one_rvalue) { ASSERT_EQ(0, bugs_host()); } -TEST_F(TEST_CATEGORY_FIXTURE(count_bugs), launch_six) { +TEST_F(TEST_CATEGORY_FIXTURE(graph), launch_six) { +#ifdef KOKKOS_ENABLE_OPENMPTARGET // FIXME_OPENMPTARGET team_size incompatible + if (std::is_same_v) + GTEST_SKIP() << "skipping since OpenMPTarget can't use team_size 1"; +#endif +#if defined(KOKKOS_ENABLE_SYCL) && \ + !defined(SYCL_EXT_ONEAPI_GRAPH) // FIXME_SYCL + if (std::is_same_v) + GTEST_SKIP() << "skipping since test case is known to fail with SYCL"; +#endif + auto graph = Kokkos::Experimental::create_graph(ex, [&](auto root) { auto f_setup_count = root.then_parallel_for(1, set_functor{count, 0}); auto f_setup_bugs = root.then_parallel_for(1, set_functor{bugs, 0}); @@ -145,7 +170,7 @@ TEST_F(TEST_CATEGORY_FIXTURE(count_bugs), launch_six) { ASSERT_EQ(0, bugs_host()); } -TEST_F(TEST_CATEGORY_FIXTURE(count_bugs), when_all_cycle) { +TEST_F(TEST_CATEGORY_FIXTURE(graph), when_all_cycle) { view_type reduction_out{"reduction_out"}; view_host reduction_host{"reduction_host"}; Kokkos::Experimental::create_graph(ex, [&](auto root) { @@ -172,7 +197,7 @@ TEST_F(TEST_CATEGORY_FIXTURE(count_bugs), when_all_cycle) { // This test is disabled because we don't currently support copying to host, // even asynchronously. We _may_ want to do that eventually? -TEST_F(TEST_CATEGORY_FIXTURE(count_bugs), DISABLED_repeat_chain) { +TEST_F(TEST_CATEGORY_FIXTURE(graph), DISABLED_repeat_chain) { auto graph = Kokkos::Experimental::create_graph( ex, [&, count_host = count_host](auto root) { //---------------------------------------- @@ -198,10 +223,27 @@ TEST_F(TEST_CATEGORY_FIXTURE(count_bugs), DISABLED_repeat_chain) { //---------------------------------------- } -TEST_F(TEST_CATEGORY_FIXTURE(count_bugs), zero_work_reduce) { - auto graph = Kokkos::Experimental::create_graph(ex, [&](auto root) { - root.then_parallel_reduce(0, set_result_functor{bugs}, count); - }); +TEST_F(TEST_CATEGORY_FIXTURE(graph), zero_work_reduce) { + auto graph = Kokkos::Experimental::create_graph( + ex, [&](Kokkos::Experimental::GraphNodeRef root) { + NoOpReduceFunctor no_op_functor; + root.then_parallel_reduce(Kokkos::RangePolicy(0, 0), + no_op_functor, count) +#if !defined(KOKKOS_ENABLE_SYCL) || \ + defined(SYCL_EXT_ONEAPI_GRAPH) // FIXME_SYCL +#if !defined(KOKKOS_ENABLE_CUDA) && \ + !defined(KOKKOS_ENABLE_HIP) // FIXME_CUDA FIXME_HIP + .then_parallel_reduce( + Kokkos::MDRangePolicy>{{0, 0}, + {0, 0}}, + no_op_functor, count) +#endif + .then_parallel_reduce( + Kokkos::TeamPolicy{0, Kokkos::AUTO}, + no_op_functor, count) +#endif + ; + }); // These fences are only necessary because of the weirdness of how CUDA // UVM works on pre pascal cards. #if defined(KOKKOS_ENABLE_CUDA) && defined(KOKKOS_ENABLE_CUDA_UVM) && \ @@ -214,12 +256,15 @@ TEST_F(TEST_CATEGORY_FIXTURE(count_bugs), zero_work_reduce) { // UVM works on pre pascal cards. #if defined(KOKKOS_ENABLE_CUDA) && defined(KOKKOS_ENABLE_CUDA_UVM) && \ (defined(KOKKOS_ARCH_KEPLER) || defined(KOKKOS_ARCH_MAXWELL)) - Kokkos::fence(); + if constexpr (std::is_same_v) Kokkos::fence(); #endif - graph.submit(); // should reset to 0, but doesn't +#ifdef KOKKOS_ENABLE_HPX // FIXME_HPX graph.submit() isn't properly enqueued + if constexpr (std::is_same_v) + Kokkos::fence(); +#endif + graph.submit(); Kokkos::deep_copy(ex, count_host, count); ex.fence(); ASSERT_EQ(count_host(), 0); } - } // end namespace Test diff --git a/lib/kokkos/core/unit_test/TestLocalDeepCopy.hpp b/lib/kokkos/core/unit_test/TestLocalDeepCopy.hpp index 1ee23a47c4..c6ee687cf9 100644 --- a/lib/kokkos/core/unit_test/TestLocalDeepCopy.hpp +++ b/lib/kokkos/core/unit_test/TestLocalDeepCopy.hpp @@ -907,13 +907,7 @@ void impl_test_local_deepcopy_rangepolicy_rank_7(const int N) { #if defined(KOKKOS_ENABLE_CXX11_DISPATCH_LAMBDA) TEST(TEST_CATEGORY, local_deepcopy_teampolicy_layoutleft) { using ExecSpace = TEST_EXECSPACE; -#if defined(KOKKOS_ENABLE_CUDA) && \ - defined(KOKKOS_COMPILER_NVHPC) // FIXME_NVHPC 23.7 - if (std::is_same_v) - GTEST_SKIP() - << "FIXME_NVHPC : Compiler bug affecting subviews of high rank Views"; -#endif - using ViewType = Kokkos::View; + using ViewType = Kokkos::View; { // Rank-1 impl_test_local_deepcopy_teampolicy_rank_1(8); @@ -940,13 +934,7 @@ TEST(TEST_CATEGORY, local_deepcopy_teampolicy_layoutleft) { //------------------------------------------------------------------------------------------------------------- TEST(TEST_CATEGORY, local_deepcopy_rangepolicy_layoutleft) { using ExecSpace = TEST_EXECSPACE; -#if defined(KOKKOS_ENABLE_CUDA) && \ - defined(KOKKOS_COMPILER_NVHPC) // FIXME_NVHPC 23.7 - if (std::is_same_v) - GTEST_SKIP() - << "FIXME_NVHPC : Compiler bug affecting subviews of high rank Views"; -#endif - using ViewType = Kokkos::View; + using ViewType = Kokkos::View; { // Rank-1 impl_test_local_deepcopy_rangepolicy_rank_1(8); @@ -973,12 +961,6 @@ TEST(TEST_CATEGORY, local_deepcopy_rangepolicy_layoutleft) { //------------------------------------------------------------------------------------------------------------- TEST(TEST_CATEGORY, local_deepcopy_teampolicy_layoutright) { using ExecSpace = TEST_EXECSPACE; -#if defined(KOKKOS_ENABLE_CUDA) && \ - defined(KOKKOS_COMPILER_NVHPC) // FIXME_NVHPC 23.7 - if (std::is_same_v) - GTEST_SKIP() - << "FIXME_NVHPC : Compiler bug affecting subviews of high rank Views"; -#endif using ViewType = Kokkos::View; { // Rank-1 @@ -1006,12 +988,6 @@ TEST(TEST_CATEGORY, local_deepcopy_teampolicy_layoutright) { //------------------------------------------------------------------------------------------------------------- TEST(TEST_CATEGORY, local_deepcopy_rangepolicy_layoutright) { using ExecSpace = TEST_EXECSPACE; -#if defined(KOKKOS_ENABLE_CUDA) && \ - defined(KOKKOS_COMPILER_NVHPC) // FIXME_NVHPC 23.7 - if (std::is_same_v) - GTEST_SKIP() - << "FIXME_NVHPC : Compiler bug affecting subviews of high rank Views"; -#endif using ViewType = Kokkos::View; diff --git a/lib/kokkos/core/unit_test/TestMDSpan.hpp b/lib/kokkos/core/unit_test/TestMDSpan.hpp index ef0bea1394..fa88b547a5 100644 --- a/lib/kokkos/core/unit_test/TestMDSpan.hpp +++ b/lib/kokkos/core/unit_test/TestMDSpan.hpp @@ -35,13 +35,19 @@ void test_mdspan_minimal_functional() { Kokkos::parallel_reduce( "CheckMinimalMDSpan", Kokkos::RangePolicy(0, N), KOKKOS_LAMBDA(int i, int& err) { +#if !defined(KOKKOS_ENABLE_OPENACC) Kokkos::mdspan> b_mds(a.data(), N); -#ifdef KOKKOS_ENABLE_CXX23 +#endif +#if !defined(KOKKOS_ENABLE_CXX17) && !defined(KOKKOS_ENABLE_CXX20) if (a_mds[i] != i) err++; +#if !defined(KOKKOS_ENABLE_OPENACC) if (b_mds[i] != i) err++; +#endif #else if (a_mds(i) != i) err++; +#if !defined(KOKKOS_ENABLE_OPENACC) if (b_mds(i) != i) err++; +#endif #endif }, errors); diff --git a/lib/kokkos/core/unit_test/TestMDSpanAtomicAccessor.hpp b/lib/kokkos/core/unit_test/TestMDSpanAtomicAccessor.hpp new file mode 100644 index 0000000000..04460e6419 --- /dev/null +++ b/lib/kokkos/core/unit_test/TestMDSpanAtomicAccessor.hpp @@ -0,0 +1,112 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#include +#include + +#include +#ifndef KOKKOS_ENABLE_CXX17 +#include +#endif + +template +void test_atomic_accessor() { + using value_type = std::remove_const_t; + Kokkos::View v("V", 100); + + Kokkos::parallel_for( + Kokkos::RangePolicy(0, v.extent(0)), + KOKKOS_LAMBDA(int i) { v(i) = i; }); + + int errors; + using acc_t = Kokkos::Impl::AtomicAccessorRelaxed; + acc_t acc{}; + typename acc_t::data_handle_type ptr = v.data(); + + Kokkos::parallel_reduce( + Kokkos::RangePolicy(0, v.extent(0)), + KOKKOS_LAMBDA(int i, int& error) { + if (acc.access(ptr, i) != ptr[i]) error++; + if (acc.offset(ptr, i) != ptr + i) error++; + static_assert(std::is_same_v); + static_assert( + std::is_same_v>); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_nothrow_move_constructible_v); + static_assert(std::is_nothrow_move_assignable_v); + static_assert(std::is_nothrow_swappable_v); + static_assert(std::is_trivially_copyable_v); + static_assert(std::is_trivially_default_constructible_v); + static_assert(std::is_trivially_constructible_v); + static_assert(std::is_trivially_move_constructible_v); + static_assert(std::is_trivially_assignable_v); + static_assert(std::is_trivially_move_assignable_v); +#ifndef KOKKOS_ENABLE_CXX17 + static_assert(std::copyable); + static_assert(std::is_empty_v); +#endif + }, + errors); + ASSERT_EQ(errors, 0); +} + +void test_atomic_accessor_conversion() { + using ExecutionSpace = TEST_EXECSPACE; + using T = float; + using acc_t = Kokkos::Impl::AtomicAccessorRelaxed; + using const_acc_t = Kokkos::Impl::AtomicAccessorRelaxed; + using int_acc_t = Kokkos::Impl::AtomicAccessorRelaxed; + using defacc_t = Kokkos::default_accessor; + using const_defacc_t = Kokkos::default_accessor; + using int_defacc_t = Kokkos::default_accessor; + + Kokkos::parallel_for( + Kokkos::RangePolicy(0, 1), KOKKOS_LAMBDA(int) { + static_assert(std::is_constructible_v); + static_assert(std::is_convertible_v); + static_assert(!std::is_constructible_v); + static_assert(!std::is_constructible_v); + static_assert(std::is_constructible_v); + static_assert(std::is_constructible_v); + static_assert(!std::is_constructible_v); + static_assert(!std::is_constructible_v); + static_assert(std::is_constructible_v); + static_assert(std::is_constructible_v); + static_assert(std::is_constructible_v); + static_assert(std::is_constructible_v); + static_assert(!std::is_constructible_v); + static_assert(!std::is_constructible_v); + static_assert(std::is_convertible_v); + static_assert(std::is_convertible_v); + static_assert(std::is_convertible_v); + static_assert(std::is_convertible_v); + static_assert(!std::is_convertible_v); + static_assert(!std::is_convertible_v); + static_assert(!std::is_convertible_v); + }); +} + +TEST(TEST_CATEGORY, mdspan_atomic_accessor) { + using ExecutionSpace = TEST_EXECSPACE; + test_atomic_accessor(); + test_atomic_accessor(); +} diff --git a/lib/kokkos/core/unit_test/TestMDSpanConversion.hpp b/lib/kokkos/core/unit_test/TestMDSpanConversion.hpp new file mode 100644 index 0000000000..10123901c4 --- /dev/null +++ b/lib/kokkos/core/unit_test/TestMDSpanConversion.hpp @@ -0,0 +1,507 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#include +#include + +#include +#include "experimental/__p0009_bits/layout_stride.hpp" + +namespace { + +template +struct TestViewMDSpanConversion { + using value_type = T; + + template + using layout_left_padded = Kokkos::Experimental::layout_left_padded; + + template + using layout_right_padded = + Kokkos::Experimental::layout_right_padded; + + struct TestAccessor { + using offset_policy = TestAccessor; + using element_type = value_type; + using reference = element_type &; + using data_handle_type = element_type *; + + constexpr TestAccessor() noexcept = default; + constexpr reference access(data_handle_type p, std::size_t i) noexcept { + return p[i]; + } + constexpr data_handle_type offset(data_handle_type p, + std::size_t i) noexcept { + return p + i; + } + }; + + template + static void test_conversion_from_mdspan( + Kokkos::View ref, + const MDSpanLayoutMapping &mapping) { + using unmanaged_view_type = + Kokkos::View>; + using natural_mdspan_type = typename Kokkos::Impl::MDSpanViewTraits< + typename unmanaged_view_type::traits>::mdspan_type; + using mapping_type = MDSpanLayoutMapping; + using mdspan_layout_type = typename MDSpanLayoutMapping::layout_type; + using extents_type = typename mapping_type::extents_type; + using mdspan_type = + Kokkos::mdspan; + + static_assert(std::is_constructible_v); + static_assert(std::is_convertible_v == + std::is_convertible_v); + // Manually create an mdspan from ref so we have a valid pointer to play + // with + const auto &exts = mapping.extents(); + auto mds = mdspan_type{ref.data(), mapping}; + + auto test_view = unmanaged_view_type(mds); + + ASSERT_EQ(test_view.data(), ref.data()); + ASSERT_EQ(test_view.data(), mds.data_handle()); + ASSERT_EQ(test_view.layout(), ref.layout()); + for (std::size_t r = 0; r < mdspan_type::rank(); ++r) { + ASSERT_EQ(test_view.extent(r), ref.extent(r)); + ASSERT_EQ(test_view.extent(r), exts.extent(r)); + } + } + + template + static void test_conversion_to_mdspan( + const MDSpanLayoutMapping &ref_layout_mapping, ViewType v) { + using view_type = ViewType; + using natural_mdspan_type = typename Kokkos::Impl::MDSpanViewTraits< + typename view_type::traits>::mdspan_type; + + static_assert(natural_mdspan_type::rank() == view_type::rank); + static_assert(std::is_same_v); + constexpr bool is_strided_layout = + std::is_same_v; + if constexpr (!is_strided_layout) { + static_assert(natural_mdspan_type::mapping_type::padding_value == + Kokkos::dynamic_extent); + } + // test conversion operator to natural mdspan + { + natural_mdspan_type cvt = v; + ASSERT_EQ(cvt.data_handle(), v.data()); + ASSERT_EQ(cvt.mapping(), ref_layout_mapping); + + if constexpr (!is_strided_layout && natural_mdspan_type::rank() > 1) { + ASSERT_EQ(cvt.mapping().stride(1), ref_layout_mapping.stride(1)); + } + } + // test to_mdspan() returning natural mdspan + { + auto cvt = v.to_mdspan(); + static_assert(std::is_same_v); + ASSERT_EQ(cvt.data_handle(), v.data()); + ASSERT_EQ(cvt.mapping(), ref_layout_mapping); + } + // test conversion operator to different mdspan type + { + using element_type = const typename natural_mdspan_type::element_type; + using const_acc_type = Kokkos::Impl::SpaceAwareAccessor< + typename ViewType::memory_space, + Kokkos::default_accessor>; + using mdspan_type = Kokkos::mdspan< + element_type, + Kokkos::dextents, + typename natural_mdspan_type::layout_type, const_acc_type>; + mdspan_type cvt = v; + ASSERT_EQ(cvt.data_handle(), v.data()); + ASSERT_EQ(cvt.mapping(), ref_layout_mapping); + } + } + + template + static void test_conversion_to_mdspan_with_accessor( + const MDSpanLayoutMapping &ref_layout_mapping, ViewType v, + const AccessorType &a) { + auto cvt = v.to_mdspan(a); + static_assert(decltype(cvt)::rank() == ViewType::rank); + static_assert(std::is_same_v); + ASSERT_EQ(cvt.data_handle(), v.data()); + ASSERT_EQ(cvt.mapping(), ref_layout_mapping); + } + + template + using natural_mdspan_type_for_view = typename Kokkos::Impl::MDSpanViewTraits< + typename ViewType::traits>::mdspan_type; + + static void run_test() { + // Verify we can only convert to compatible mdspans + static_assert(std::is_convertible_v< + Kokkos::View, + natural_mdspan_type_for_view>>); + static_assert( + std::is_convertible_v< + Kokkos::View, + natural_mdspan_type_for_view>>); + + // Do not cast const away + static_assert(!std::is_convertible_v< + Kokkos::View, + natural_mdspan_type_for_view>>); + + // Mismatched dim + static_assert(!std::is_convertible_v< + Kokkos::View, + natural_mdspan_type_for_view>>); + + // Mismatched layouts + static_assert( + !std::is_convertible_v, + natural_mdspan_type_for_view>>); + static_assert( + !std::is_convertible_v, + natural_mdspan_type_for_view>>); + // nvcc doesn't do CTAD properly here, making this way more verbose.. + // LayoutLeft + test_conversion_from_mdspan( + Kokkos::View("ref", + 7), + typename layout_left_padded<7>::template mapping< + Kokkos::dextents>{ + Kokkos::dextents(7)}); + + test_conversion_from_mdspan( + Kokkos::View("ref"), + typename layout_left_padded<7>::template mapping< + Kokkos::extents>{ + Kokkos::extents()}); + test_conversion_from_mdspan( + Kokkos::View("ref"), + typename layout_left_padded<7>::template mapping< + Kokkos::dextents>{ + Kokkos::dextents(7)}); + test_conversion_from_mdspan( + Kokkos::View("ref", + 7), + typename layout_left_padded<7>::template mapping< + Kokkos::extents>{ + Kokkos::extents()}); + + test_conversion_from_mdspan( + Kokkos::View("ref", + 7, 3), + typename layout_left_padded<7>::template mapping< + Kokkos::dextents>{ + Kokkos::dextents(7, 3)}); + test_conversion_from_mdspan( + Kokkos::View( + "ref"), + typename layout_left_padded<7>::template mapping< + Kokkos::extents>{ + Kokkos::extents()}); + test_conversion_from_mdspan( + Kokkos::View( + "ref"), + typename layout_left_padded<7>::template mapping< + Kokkos::dextents>{ + Kokkos::dextents(7, 3)}); + test_conversion_from_mdspan( + Kokkos::View("ref", + 7, 3), + typename layout_left_padded<7>::template mapping< + Kokkos::extents>{ + Kokkos::extents()}); + + // LayoutRight + test_conversion_from_mdspan( + Kokkos::View("ref", + 7), + typename layout_right_padded<7>::template mapping< + Kokkos::dextents>{ + Kokkos::dextents(7)}); + test_conversion_from_mdspan( + Kokkos::View("ref"), + typename layout_right_padded<7>::template mapping< + Kokkos::extents>{ + Kokkos::extents()}); + test_conversion_from_mdspan( + Kokkos::View("ref"), + typename layout_right_padded<7>::template mapping< + Kokkos::dextents>{ + Kokkos::dextents(7)}); + test_conversion_from_mdspan( + Kokkos::View("ref", + 7), + typename layout_right_padded<7>::template mapping< + Kokkos::extents>{ + Kokkos::extents()}); + + test_conversion_from_mdspan( + Kokkos::View("ref", + 3, 7), + typename layout_right_padded<7>::template mapping< + Kokkos::dextents>{ + Kokkos::dextents(3, 7)}); + test_conversion_from_mdspan( + Kokkos::View( + "ref"), + typename layout_right_padded<7>::template mapping< + Kokkos::extents>{ + Kokkos::extents()}); + test_conversion_from_mdspan( + Kokkos::View( + "ref"), + typename layout_right_padded<7>::template mapping< + Kokkos::dextents>{ + Kokkos::dextents(3, 7)}); + test_conversion_from_mdspan( + Kokkos::View("ref", + 3, 7), + typename layout_right_padded<7>::template mapping< + Kokkos::extents>{ + Kokkos::extents()}); + + // LayoutStride + { + const size_t strides[] = {2}; + test_conversion_from_mdspan( + Kokkos::View( + "ref", Kokkos::LayoutStride{7, 2}), + Kokkos::layout_stride::mapping>{ + Kokkos::mdspan_non_standard, Kokkos::dextents{7}, + strides}); + } + { + const size_t strides[] = {2}; + test_conversion_from_mdspan( + Kokkos::View( + "ref", Kokkos::LayoutStride{7, 2}), + Kokkos::layout_stride::mapping>{ + Kokkos::mdspan_non_standard, {}, strides}); + } + { + const size_t strides[] = {2}; + test_conversion_from_mdspan( + Kokkos::View( + "ref", Kokkos::LayoutStride{7, 2}), + Kokkos::layout_stride::mapping>{ + Kokkos::mdspan_non_standard, Kokkos::dextents{7}, + strides}); + } + { + const size_t strides[] = {2}; + test_conversion_from_mdspan( + Kokkos::View( + "ref", Kokkos::LayoutStride{7, 2}), + Kokkos::layout_stride::mapping>{ + Kokkos::mdspan_non_standard, Kokkos::extents(), + strides}); + } + + { + const size_t strides[] = {2, 4}; + test_conversion_from_mdspan( + Kokkos::View( + "ref", Kokkos::LayoutStride{7, 2, 3, 4}), + Kokkos::layout_stride::mapping>{ + Kokkos::mdspan_non_standard, + Kokkos::dextents(7, 3), strides}); + } + { + const size_t strides[] = {2, 4}; + test_conversion_from_mdspan( + Kokkos::View( + "ref", Kokkos::LayoutStride{7, 2, 3, 4}), + Kokkos::layout_stride::mapping>{ + Kokkos::mdspan_non_standard, Kokkos::extents(), + strides}); + } + { + const size_t strides[] = {2, 4}; + test_conversion_from_mdspan( + Kokkos::View( + "ref", Kokkos::LayoutStride{7, 2, 3, 4}), + Kokkos::layout_stride::mapping>{ + Kokkos::mdspan_non_standard, + Kokkos::dextents(7, 3), strides}); + } + { + const size_t strides[] = {2, 4}; + test_conversion_from_mdspan( + Kokkos::View( + "ref", Kokkos::LayoutStride{7, 2, 3, 4}), + Kokkos::layout_stride::mapping>{ + Kokkos::mdspan_non_standard, Kokkos::extents(), + strides}); + } + + // Conversion to mdspan + test_conversion_to_mdspan( + layout_left_padded::mapping< + Kokkos::extents>({}, 4), + Kokkos::View("v", 4)); + test_conversion_to_mdspan( + layout_left_padded::mapping< + Kokkos::extents>({}, 4), + Kokkos::View("v", 4, + 7)); + + test_conversion_to_mdspan( + layout_right_padded::mapping< + Kokkos::extents>({}, 4), + Kokkos::View("v", + 4)); + test_conversion_to_mdspan( + layout_right_padded::mapping< + Kokkos::extents>({}, 7), + Kokkos::View("v", 4, + 7)); + + { + const size_t strides[] = {5}; + test_conversion_to_mdspan( + Kokkos::layout_stride::mapping>( + Kokkos::mdspan_non_standard, {}, strides), + Kokkos::View( + "v", Kokkos::LayoutStride{4, 5})); + } + { + const size_t strides[] = {5, 9}; + test_conversion_to_mdspan( + Kokkos::layout_stride::mapping>( + Kokkos::mdspan_non_standard, {}, strides), + Kokkos::View( + "v", Kokkos::LayoutStride{4, 5, 7, 9})); + } + + // Aligned types (for padded layouts) + test_conversion_to_mdspan( + layout_left_padded::mapping< + Kokkos::extents>({}, 128), + Kokkos::View( + Kokkos::view_alloc("v", Kokkos::AllowPadding), 127, 7)); + + test_conversion_to_mdspan( + layout_right_padded::mapping< + Kokkos::extents>({}, 128), + Kokkos::View( + Kokkos::view_alloc("v", Kokkos::AllowPadding), 7, 127)); + + // Conversion with standard default_accessor + + test_conversion_to_mdspan_with_accessor( + layout_left_padded::mapping< + Kokkos::extents>({}, 4), + Kokkos::View("v", 4), + Kokkos::default_accessor{}); + test_conversion_to_mdspan_with_accessor( + layout_left_padded::mapping< + Kokkos::extents>({}, 4), + Kokkos::View("v", 4, + 7), + Kokkos::default_accessor{}); + + test_conversion_to_mdspan_with_accessor( + layout_right_padded::mapping< + Kokkos::extents>({}, 4), + Kokkos::View("v", 4), + Kokkos::default_accessor{}); + test_conversion_to_mdspan_with_accessor( + layout_right_padded::mapping< + Kokkos::extents>({}, 7), + Kokkos::View("v", 4, + 7), + Kokkos::default_accessor{}); + + { + const size_t strides[] = {5}; + test_conversion_to_mdspan_with_accessor( + Kokkos::layout_stride::mapping>( + Kokkos::mdspan_non_standard, {}, strides), + Kokkos::View( + "v", Kokkos::LayoutStride{4, 5}), + Kokkos::default_accessor{}); + } + { + const size_t strides[] = {5, 9}; + test_conversion_to_mdspan_with_accessor( + Kokkos::layout_stride::mapping>( + Kokkos::mdspan_non_standard, {}, strides), + Kokkos::View( + "v", Kokkos::LayoutStride{4, 5, 7, 9}), + Kokkos::default_accessor{}); + } + + // Conversion with a test accessor + + test_conversion_to_mdspan_with_accessor( + layout_left_padded::mapping< + Kokkos::extents>({}, 4), + Kokkos::View("v", 4), + TestAccessor{}); + test_conversion_to_mdspan_with_accessor( + layout_left_padded::mapping< + Kokkos::extents>({}, 4), + Kokkos::View("v", 4, + 7), + TestAccessor{}); + + test_conversion_to_mdspan_with_accessor( + layout_right_padded::mapping< + Kokkos::extents>({}, 4), + Kokkos::View("v", 4), + TestAccessor{}); + test_conversion_to_mdspan_with_accessor( + layout_right_padded::mapping< + Kokkos::extents>({}, 7), + Kokkos::View("v", 4, + 7), + TestAccessor{}); + + { + const size_t strides[] = {5}; + test_conversion_to_mdspan_with_accessor( + Kokkos::layout_stride::mapping>( + Kokkos::mdspan_non_standard, {}, strides), + Kokkos::View( + "v", Kokkos::LayoutStride{4, 5}), + TestAccessor{}); + } + { + const size_t strides[] = {5, 9}; + test_conversion_to_mdspan_with_accessor( + Kokkos::layout_stride::mapping>( + Kokkos::mdspan_non_standard, {}, strides), + Kokkos::View( + "v", Kokkos::LayoutStride{4, 5, 7, 9}), + TestAccessor{}); + } + } +}; + +TEST(TEST_CATEGORY, view_mdspan_conversion) { + TestViewMDSpanConversion::run_test(); + TestViewMDSpanConversion::run_test(); + TestViewMDSpanConversion::run_test(); +} + +} // namespace diff --git a/lib/kokkos/core/unit_test/TestMathematicalConstants.hpp b/lib/kokkos/core/unit_test/TestMathematicalConstants.hpp index e446d81321..f52bfeaff7 100644 --- a/lib/kokkos/core/unit_test/TestMathematicalConstants.hpp +++ b/lib/kokkos/core/unit_test/TestMathematicalConstants.hpp @@ -63,8 +63,7 @@ struct TestMathematicalConstants { KOKKOS_FUNCTION void use_on_device() const { #if defined(KOKKOS_COMPILER_NVCC) || defined(KOKKOS_ENABLE_OPENMPTARGET) || \ - defined(KOKKOS_ENABLE_OPENACC) || \ - defined(KOKKOS_COMPILER_NVHPC) // FIXME_NVHPC 23.7 + defined(KOKKOS_ENABLE_OPENACC) take_by_value(Trait::value); #else (void)take_address_of(Trait::value); diff --git a/lib/kokkos/core/unit_test/TestMathematicalFunctions.hpp b/lib/kokkos/core/unit_test/TestMathematicalFunctions.hpp index ad035d4e4b..f996c61a52 100644 --- a/lib/kokkos/core/unit_test/TestMathematicalFunctions.hpp +++ b/lib/kokkos/core/unit_test/TestMathematicalFunctions.hpp @@ -1585,34 +1585,24 @@ struct TestIsFinite { Kokkos::printf("failed isfinite(float)\n"); } #if !(defined(KOKKOS_ENABLE_CUDA) && defined(KOKKOS_COMPILER_MSVC)) - if (!isfinite(static_cast(2.f)) -#ifndef KOKKOS_COMPILER_NVHPC // FIXME_NVHPC 23.7 - || isfinite(quiet_NaN::value) || + if (!isfinite(static_cast(2.f)) || + isfinite(quiet_NaN::value) || isfinite(signaling_NaN::value) || - isfinite(infinity::value) -#endif - ) { + isfinite(infinity::value)) { ++e; Kokkos::printf("failed isfinite(KE::half_t)\n"); } - if (!isfinite(static_cast(2.f)) -#ifndef KOKKOS_COMPILER_NVHPC // FIXME_NVHPC 23.7 - || isfinite(quiet_NaN::value) || + if (!isfinite(static_cast(2.f)) || + isfinite(quiet_NaN::value) || isfinite(signaling_NaN::value) || - isfinite(infinity::value) -#endif - ) { + isfinite(infinity::value)) { ++e; Kokkos::printf("failed isfinite(KE::bhalf_t)\n"); } #endif - if (!isfinite(3.) -#ifndef KOKKOS_COMPILER_NVHPC // FIXME_NVHPC 23.7 - || isfinite(quiet_NaN::value) || + if (!isfinite(3.) || isfinite(quiet_NaN::value) || isfinite(signaling_NaN::value) || - isfinite(infinity::value) -#endif - ) { + isfinite(infinity::value)) { ++e; Kokkos::printf("failed isfinite(double)\n"); } @@ -1666,33 +1656,24 @@ struct TestIsInf { Kokkos::printf("failed isinf(float)\n"); } #if !(defined(KOKKOS_ENABLE_CUDA) && defined(KOKKOS_COMPILER_MSVC)) - if (isinf(static_cast(2.f)) -#ifndef KOKKOS_COMPILER_NVHPC // FIXME_NVHPC 23.7 - || isinf(quiet_NaN::value) || + if (isinf(static_cast(2.f)) || + isinf(quiet_NaN::value) || isinf(signaling_NaN::value) || - !isinf(infinity::value) -#endif - ) { + !isinf(infinity::value)) { ++e; Kokkos::printf("failed isinf(KE::half_t)\n"); } - if (isinf(static_cast(2.f)) -#ifndef KOKKOS_COMPILER_NVHPC // FIXME_NVHPC 23.7 - || isinf(quiet_NaN::value) || + if (isinf(static_cast(2.f)) || + isinf(quiet_NaN::value) || isinf(signaling_NaN::value) || - !isinf(infinity::value) -#endif - ) { + !isinf(infinity::value)) { ++e; Kokkos::printf("failed isinf(KE::bhalf_t)\n"); } #endif - if (isinf(3.) -#ifndef KOKKOS_COMPILER_NVHPC // FIXME_NVHPC 23.7 - || isinf(quiet_NaN::value) || - isinf(signaling_NaN::value) || !isinf(infinity::value) -#endif - ) { + if (isinf(3.) || isinf(quiet_NaN::value) || + isinf(signaling_NaN::value) || + !isinf(infinity::value)) { ++e; Kokkos::printf("failed isinf(double)\n"); } @@ -1746,32 +1727,23 @@ struct TestIsNaN { Kokkos::printf("failed isnan(float)\n"); } #if !(defined(KOKKOS_ENABLE_CUDA) && defined(KOKKOS_COMPILER_MSVC)) - if (isnan(static_cast(2.f)) -#ifndef KOKKOS_COMPILER_NVHPC // FIXME_NVHPC 23.7 - || !isnan(quiet_NaN::value) || + if (isnan(static_cast(2.f)) || + !isnan(quiet_NaN::value) || !isnan(signaling_NaN::value) || - isnan(infinity::value) -#endif - ) { + isnan(infinity::value)) { ++e; Kokkos::printf("failed isnan(KE::half_t)\n"); } - if (isnan(static_cast(2.f)) -#ifndef KOKKOS_COMPILER_NVHPC // FIXME_NVHPC 23.7 - || !isnan(quiet_NaN::value) || + if (isnan(static_cast(2.f)) || + !isnan(quiet_NaN::value) || !isnan(signaling_NaN::value) || - isnan(infinity::value) -#endif - ) { + isnan(infinity::value)) { ++e; Kokkos::printf("failed isnan(KE::bhalf_t)\n"); } - if (isnan(3.) -#ifndef KOKKOS_COMPILER_NVHPC // FIXME_NVHPC 23.7 - || !isnan(quiet_NaN::value) || - !isnan(signaling_NaN::value) || isnan(infinity::value) -#endif - ) { + if (isnan(3.) || !isnan(quiet_NaN::value) || + !isnan(signaling_NaN::value) || + isnan(infinity::value)) { ++e; Kokkos::printf("failed isnan(double)\n"); } diff --git a/lib/kokkos/core/unit_test/TestMultiGPU.hpp b/lib/kokkos/core/unit_test/TestMultiGPU.hpp new file mode 100644 index 0000000000..aad2fa45f4 --- /dev/null +++ b/lib/kokkos/core/unit_test/TestMultiGPU.hpp @@ -0,0 +1,184 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#include + +namespace { + +void test_policies(TEST_EXECSPACE exec0, Kokkos::View v0, + TEST_EXECSPACE exec, Kokkos::View v) { + using MemorySpace = typename TEST_EXECSPACE::memory_space; + + exec.fence(); + exec0.fence(); + + Kokkos::deep_copy(exec, v, 5); + Kokkos::deep_copy(exec0, v0, 5); + + Kokkos::deep_copy(v, v0); + + int sum; + int sum0; + + Kokkos::parallel_for("Test::Range_0", + Kokkos::RangePolicy(exec0, 0, 100), + Test::FunctorRange(v0)); + Kokkos::parallel_for("Test::Range", + Kokkos::RangePolicy(exec, 0, 100), + Test::FunctorRange(v)); + exec.fence(); + exec0.fence(); + Kokkos::parallel_reduce( + "Test::RangeReduce_0", + Kokkos::RangePolicy>(exec0, + 0, 100), + Test::FunctorRangeReduce(v0), sum0); + Kokkos::parallel_reduce( + "Test::RangeReduce", + Kokkos::RangePolicy>(exec, 0, + 100), + Test::FunctorRangeReduce(v), sum); + ASSERT_EQ(600, sum0); + ASSERT_EQ(600, sum); + + Kokkos::parallel_for("Test::MDRange_0", + Kokkos::MDRangePolicy>( + exec0, {0, 0}, {10, 10}), + Test::FunctorMDRange(v0)); + Kokkos::parallel_for("Test::MDRange", + Kokkos::MDRangePolicy>( + exec, {0, 0}, {10, 10}), + Test::FunctorMDRange(v)); + Kokkos::parallel_reduce("Test::MDRangeReduce_0", + Kokkos::MDRangePolicy, + Kokkos::LaunchBounds<128, 2>>( + exec0, {0, 0}, {10, 10}), + Test::FunctorMDRangeReduce(v0), sum0); + Kokkos::parallel_reduce("Test::MDRangeReduce", + Kokkos::MDRangePolicy, + Kokkos::LaunchBounds<128, 2>>( + exec, {0, 0}, {10, 10}), + Test::FunctorMDRangeReduce(v), sum); + ASSERT_EQ(700, sum0); + ASSERT_EQ(700, sum); + + Kokkos::parallel_for("Test::Team_0", + Kokkos::TeamPolicy(exec0, 10, 10), + Test::FunctorTeam(v0)); + Kokkos::parallel_for("Test::Team", + Kokkos::TeamPolicy(exec, 10, 10), + Test::FunctorTeam(v)); + Kokkos::parallel_reduce( + "Test::Team_0", + Kokkos::TeamPolicy>(exec0, + 10, 10), + Test::FunctorTeamReduce(v0), sum0); + Kokkos::parallel_reduce( + "Test::Team", + Kokkos::TeamPolicy>(exec, 10, + 10), + Test::FunctorTeamReduce(v), sum); + ASSERT_EQ(800, sum0); + ASSERT_EQ(800, sum); +} + +struct ScratchFunctor { + int scratch_size; + int R; + + ScratchFunctor(int scratch_size_, int R_) + : scratch_size(scratch_size_), R(R_) {} + + KOKKOS_FUNCTION + void operator()(const Kokkos::TeamPolicy::member_type &team, + int &error_accum) const { + Kokkos::View scratch_mem( + team.team_scratch(1), scratch_size); + + // Initialize scratch memory + Kokkos::parallel_for(Kokkos::TeamVectorRange(team, 0, scratch_size), + [&](int i) { scratch_mem(i) = 0; }); + team.team_barrier(); + + // Increment each entry in scratch memory R times + for (int r = 0; r < R; ++r) { + Kokkos::parallel_for(Kokkos::TeamVectorRange(team, 0, scratch_size), + [&](int i) { scratch_mem(i) += 1; }); + } + team.team_barrier(); + + // Check that each scratch entry has been incremented exactly R times + int team_error_accum; + auto R_loc = R; // avoid implicit capture of this + Kokkos::parallel_reduce( + Kokkos::TeamVectorRange(team, 0, scratch_size), + [&](int i, int &tsum) { + if (scratch_mem(i) != R_loc) { + tsum += 1; + } + }, + team_error_accum); + Kokkos::single(Kokkos::PerTeam(team), + [&]() { error_accum += team_error_accum; }); + } +}; + +void test_scratch(TEST_EXECSPACE exec0, TEST_EXECSPACE exec1) { + constexpr int N = 10; + constexpr int R = 1000; + constexpr int scratch_size = 100; + using ScratchType = Kokkos::View; + + // Test allocating and using scratch space + ScratchFunctor f(scratch_size, R); + + auto policy0 = + Kokkos::TeamPolicy(exec0, N, 10) + .set_scratch_size( + 1, Kokkos::PerTeam(ScratchType::shmem_size(scratch_size))); + auto policy1 = + Kokkos::TeamPolicy(exec1, N, 10) + .set_scratch_size( + 1, Kokkos::PerTeam(ScratchType::shmem_size(scratch_size))); + + int error0, error1; + + Kokkos::parallel_reduce("test_scratch_device_0", policy0, f, error0); + Kokkos::parallel_reduce("test_scratch_device_1", policy1, f, error1); + ASSERT_EQ(error0, 0); + ASSERT_EQ(error1, 0); + + // Request larger scratch size to trigger a realloc and test + const auto new_scratch_size = scratch_size + 10; + ScratchFunctor f_more_scratch(new_scratch_size, R); + + auto policy0_more_scratch = + Kokkos::TeamPolicy(exec0, N, 10) + .set_scratch_size( + 1, Kokkos::PerTeam(ScratchType::shmem_size(new_scratch_size))); + auto policy1_more_scratch = + Kokkos::TeamPolicy(exec1, N, 10) + .set_scratch_size( + 1, Kokkos::PerTeam(ScratchType::shmem_size(new_scratch_size))); + + Kokkos::parallel_reduce("test_realloc_scratch_device_0", policy0_more_scratch, + f_more_scratch, error0); + Kokkos::parallel_reduce("test_realloc_scratch_device_1", policy1_more_scratch, + f_more_scratch, error1); + ASSERT_EQ(error0, 0); + ASSERT_EQ(error1, 0); +} +} // namespace diff --git a/lib/kokkos/core/unit_test/TestNestedReducerCTAD.cpp b/lib/kokkos/core/unit_test/TestNestedReducerCTAD.cpp new file mode 100644 index 0000000000..95493a5874 --- /dev/null +++ b/lib/kokkos/core/unit_test/TestNestedReducerCTAD.cpp @@ -0,0 +1,246 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#include + +namespace { + +struct TestNestedReducerCTAD { + using MemorySpace = Kokkos::DefaultExecutionSpace::memory_space; + using ScalarType = int; + using IndexType = int; + using TeamPolicy = Kokkos::TeamPolicy; + using TeamHandle = TeamPolicy::member_type; + + struct FakeComparator { + template + KOKKOS_FUNCTION bool operator()(T const&, T const&) const { + return true; + } + }; + + template + struct FakeFunctor { + KOKKOS_FUNCTION void operator()(int, ValueType&) const {} + }; + + template + KOKKOS_FUNCTION static void check_types([ + [maybe_unused]] ReducerTypeToCheck const& reducer) { + static_assert(std::is_same_v); + } + + KOKKOS_FUNCTION void operator()([ + [maybe_unused]] TeamHandle const& team_handle) const { + { + using ReducerTypeExpected = Kokkos::Sum; + using ValueType = ReducerTypeExpected::value_type; + Kokkos::View view; + Kokkos::Sum reducer(view); + check_types(reducer); + } + + { + using ReducerTypeExpected = Kokkos::Prod; + using ValueType = ReducerTypeExpected::value_type; + Kokkos::View view; + Kokkos::Prod reducer(view); + check_types(reducer); + } + + { + using ReducerTypeExpected = Kokkos::Min; + using ValueType = ReducerTypeExpected::value_type; + Kokkos::View view; + Kokkos::Min reducer(view); + check_types(reducer); + } + + { + using ReducerTypeExpected = Kokkos::Max; + using ValueType = ReducerTypeExpected::value_type; + Kokkos::View view; + Kokkos::Max reducer(view); + check_types(reducer); + } + + { + using ReducerTypeExpected = Kokkos::LAnd; + using ValueType = ReducerTypeExpected::value_type; + Kokkos::View view; + Kokkos::LAnd reducer(view); + check_types(reducer); + } + + { + using ReducerTypeExpected = Kokkos::LOr; + using ValueType = ReducerTypeExpected::value_type; + Kokkos::View view; + Kokkos::LOr reducer(view); + check_types(reducer); + } + + { + using ReducerTypeExpected = Kokkos::BAnd; + using ValueType = ReducerTypeExpected::value_type; + Kokkos::View view; + Kokkos::BAnd reducer(view); + check_types(reducer); + } + + { + using ReducerTypeExpected = Kokkos::BOr; + using ValueType = ReducerTypeExpected::value_type; + Kokkos::View view; + Kokkos::BOr reducer(view); + check_types(reducer); + } + + { + using ReducerTypeExpected = + Kokkos::MinLoc; + using ValueType = ReducerTypeExpected::value_type; + Kokkos::View view; + Kokkos::MinLoc reducer(view); + check_types(reducer); + } + + { + using ReducerTypeExpected = + Kokkos::MaxLoc; + using ValueType = ReducerTypeExpected::value_type; + Kokkos::View view; + Kokkos::MaxLoc reducer(view); + check_types(reducer); + } + + { + using ReducerTypeExpected = Kokkos::MinMax; + using ValueType = ReducerTypeExpected::value_type; + Kokkos::View view; + Kokkos::MinMax reducer(view); + check_types(reducer); + } + + { + using ReducerTypeExpected = + Kokkos::MinMaxLoc; + using ValueType = ReducerTypeExpected::value_type; + Kokkos::View view; + Kokkos::MinMaxLoc reducer(view); + check_types(reducer); + } + + { + using ReducerTypeExpected = + Kokkos::MaxFirstLoc; + using ValueType = ReducerTypeExpected::value_type; + Kokkos::View view; + Kokkos::MaxFirstLoc reducer(view); + check_types(reducer); + } + + { + using ReducerTypeExpected = + Kokkos::MaxFirstLocCustomComparator; + using ValueType = ReducerTypeExpected::value_type; + Kokkos::View view; + FakeComparator comparator; + Kokkos::MaxFirstLocCustomComparator reducer(view, comparator); + check_types(reducer); + } + + { + using ReducerTypeExpected = + Kokkos::MinFirstLoc; + using ValueType = ReducerTypeExpected::value_type; + Kokkos::View view; + Kokkos::MinFirstLoc reducer(view); + check_types(reducer); + } + + { + using ReducerTypeExpected = + Kokkos::MinFirstLocCustomComparator; + using ValueType = ReducerTypeExpected::value_type; + Kokkos::View view; + FakeComparator comparator; + Kokkos::MinFirstLocCustomComparator reducer(view, comparator); + check_types(reducer); + } + + { + using ReducerTypeExpected = + Kokkos::MinMaxFirstLastLoc; + using ValueType = ReducerTypeExpected::value_type; + Kokkos::View view; + Kokkos::MinMaxFirstLastLoc reducer(view); + check_types(reducer); + } + + { + using ReducerTypeExpected = Kokkos::MinMaxFirstLastLocCustomComparator< + ScalarType, IndexType, FakeComparator, MemorySpace>; + using ValueType = ReducerTypeExpected::value_type; + Kokkos::View view; + FakeComparator comparator; + Kokkos::MinMaxFirstLastLocCustomComparator reducer(view, comparator); + check_types(reducer); + } + + { + using ReducerTypeExpected = Kokkos::FirstLoc; + using ValueType = ReducerTypeExpected::value_type; + Kokkos::View view; + Kokkos::FirstLoc reducer(view); + check_types(reducer); + } + + { + using ReducerTypeExpected = Kokkos::LastLoc; + using ValueType = ReducerTypeExpected::value_type; + Kokkos::View view; + Kokkos::LastLoc reducer(view); + check_types(reducer); + } + + { + using ReducerTypeExpected = + Kokkos::StdIsPartitioned; + using ValueType = ReducerTypeExpected::value_type; + Kokkos::View view; + Kokkos::StdIsPartitioned reducer(view); + check_types(reducer); + } + + { + using ReducerTypeExpected = + Kokkos::StdPartitionPoint; + using ValueType = ReducerTypeExpected::value_type; + Kokkos::View view; + Kokkos::StdPartitionPoint reducer(view); + check_types(reducer); + } + } + + TestNestedReducerCTAD() { + Kokkos::parallel_for(TeamPolicy(0, Kokkos::AUTO), *this); + } +}; + +} // namespace diff --git a/lib/kokkos/core/unit_test/TestNumericTraits.hpp b/lib/kokkos/core/unit_test/TestNumericTraits.hpp index 81a9d0a5e0..0c80335488 100644 --- a/lib/kokkos/core/unit_test/TestNumericTraits.hpp +++ b/lib/kokkos/core/unit_test/TestNumericTraits.hpp @@ -21,6 +21,19 @@ #include #include "Kokkos_NumericTraits.hpp" +// Suppress "'long double' is treated as 'double' in device code" +#ifdef KOKKOS_COMPILER_NVCC +#ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ +#pragma nv_diagnostic push +#pragma nv_diag_suppress 20208 +#else +#ifdef __CUDA_ARCH__ +#pragma diagnostic push +#pragma diag_suppress 20208 +#endif +#endif +#endif + struct extrema { #define DEFINE_EXTREMA(T, m, M) \ KOKKOS_FUNCTION static T min(T) { return m; } \ @@ -145,33 +158,25 @@ struct TestNumericTraits { KOKKOS_FUNCTION void operator()(MaxExponent10, int, int&) const { use_on_device(); } // clang-format on KOKKOS_FUNCTION void operator()(QuietNaN, int, int& e) const { -#ifndef KOKKOS_COMPILER_NVHPC // FIXME_NVHPC 23.7 nan using Kokkos::Experimental::quiet_NaN; constexpr auto nan = quiet_NaN::value; auto const zero = T(0); e += (int)!(nan != nan); e += (int)!(nan != zero); -#else - (void)e; -#endif use_on_device(); } KOKKOS_FUNCTION void operator()(SignalingNaN, int, int& e) const { -#ifndef KOKKOS_COMPILER_NVHPC // FIXME_NVHPC 23.7 nan using Kokkos::Experimental::signaling_NaN; constexpr auto nan = signaling_NaN::value; auto const zero = T(0); e += (int)!(nan != nan); e += (int)!(nan != zero); -#else - (void)e; -#endif use_on_device(); } KOKKOS_FUNCTION void use_on_device() const { -#if defined(KOKKOS_COMPILER_NVCC) || defined(KOKKOS_COMPILER_NVHPC) || \ - defined(KOKKOS_ENABLE_OPENMPTARGET) || defined(KOKKOS_ENABLE_OPENACC) +#if defined(KOKKOS_COMPILER_NVCC) || defined(KOKKOS_ENABLE_OPENMPTARGET) || \ + defined(KOKKOS_ENABLE_OPENACC) take_by_value(trait::value); #else (void)take_address_of(trait::value); @@ -204,59 +209,46 @@ struct TestNumericTraits< #endif TEST(TEST_CATEGORY, numeric_traits_infinity) { -#ifndef KOKKOS_COMPILER_NVHPC // FIXME_NVHPC 23.7 TestNumericTraits(); TestNumericTraits(); -#endif TestNumericTraits(); TestNumericTraits(); - // FIXME_NVHPC 23.7 long double // FIXME_OPENMPTARGET long double on Intel GPUs -#if (!defined(KOKKOS_ENABLE_CUDA) || !defined(KOKKOS_COMPILER_NVHPC)) && \ - (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) +#if (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) TestNumericTraits(); #endif } TEST(TEST_CATEGORY, numeric_traits_epsilon) { -#ifndef KOKKOS_COMPILER_NVHPC // FIXME_NVHPC 23.7 bit_comparison_type TestNumericTraits(); TestNumericTraits(); -#endif TestNumericTraits(); TestNumericTraits(); - // FIXME_NVHPC 23.7 long double -#if (!defined(KOKKOS_ENABLE_CUDA) || !defined(KOKKOS_COMPILER_NVHPC)) && \ - (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) + // FIXME_OPENMPTARGET long double on Intel GPUs +#if (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) TestNumericTraits(); #endif } TEST(TEST_CATEGORY, numeric_traits_round_error) { -#ifndef KOKKOS_COMPILER_NVHPC // FIXME_NVHPC 23.7 bit_comparison_type TestNumericTraits(); TestNumericTraits(); -#endif TestNumericTraits(); TestNumericTraits(); - // FIXME_NVHPC 23.7 long double -#if (!defined(KOKKOS_ENABLE_CUDA) || !defined(KOKKOS_COMPILER_NVHPC)) && \ - (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) + // FIXME_OPENMPTARGET long double on Intel GPUs +#if (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) TestNumericTraits(); #endif } TEST(TEST_CATEGORY, numeric_traits_norm_min) { -#ifndef KOKKOS_COMPILER_NVHPC // FIXME_NVHPC 23.7 bit_comparison_type TestNumericTraits(); TestNumericTraits(); -#endif TestNumericTraits(); TestNumericTraits(); - // FIXME_NVHPC 23.7 long double -#if (!defined(KOKKOS_ENABLE_CUDA) || !defined(KOKKOS_COMPILER_NVHPC)) && \ - (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) + // FIXME_OPENMPTARGET long double on Intel GPUs +#if (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) TestNumericTraits(); #endif } @@ -264,9 +256,8 @@ TEST(TEST_CATEGORY, numeric_traits_norm_min) { TEST(TEST_CATEGORY, numeric_traits_denorm_min) { TestNumericTraits(); TestNumericTraits(); - // FIXME_NVHPC 23.7 long double -#if (!defined(KOKKOS_ENABLE_CUDA) || !defined(KOKKOS_COMPILER_NVHPC)) && \ - (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) + // FIXME_OPENMPTARGET long double on Intel GPUs +#if (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) TestNumericTraits(); #endif } @@ -303,10 +294,8 @@ TEST(TEST_CATEGORY, numeric_traits_finite_min_max) { TestNumericTraits(); TestNumericTraits(); TestNumericTraits(); - // FIXME_NVHPC 23.7 long double // FIXME_OPENMPTARGET long double on Intel GPUs -#if (!defined(KOKKOS_ENABLE_CUDA) || !defined(KOKKOS_COMPILER_NVHPC)) && \ - (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) +#if (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) TestNumericTraits(); TestNumericTraits(); #endif @@ -329,10 +318,8 @@ TEST(TEST_CATEGORY, numeric_traits_digits) { TestNumericTraits(); TestNumericTraits(); TestNumericTraits(); - // FIXME_NVHPC 23.7 long double // FIXME_OPENMPTARGET long double on Intel GPUs -#if (!defined(KOKKOS_ENABLE_CUDA) || !defined(KOKKOS_COMPILER_NVHPC)) && \ - (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) +#if (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) TestNumericTraits(); #endif } @@ -354,10 +341,8 @@ TEST(TEST_CATEGORY, numeric_traits_digits10) { TestNumericTraits(); TestNumericTraits(); TestNumericTraits(); - // FIXME_NVHPC 23.7 long double // FIXME_OPENMPTARGET long double on Intel GPUs -#if (!defined(KOKKOS_ENABLE_CUDA) || !defined(KOKKOS_COMPILER_NVHPC)) && \ - (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) +#if (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) TestNumericTraits(); #endif } @@ -365,10 +350,8 @@ TEST(TEST_CATEGORY, numeric_traits_digits10) { TEST(TEST_CATEGORY, numeric_traits_max_digits10) { TestNumericTraits(); TestNumericTraits(); - // FIXME_NVHPC 23.7 long double // FIXME_OPENMPTARGET long double on Intel GPUs -#if (!defined(KOKKOS_ENABLE_CUDA) || !defined(KOKKOS_COMPILER_NVHPC)) && \ - (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) +#if (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) TestNumericTraits(); #endif } @@ -389,10 +372,8 @@ TEST(TEST_CATEGORY, numeric_traits_radix) { TestNumericTraits(); TestNumericTraits(); TestNumericTraits(); - // FIXME_NVHPC 23.7 long double // FIXME_OPENMPTARGET long double on Intel GPUs -#if (!defined(KOKKOS_ENABLE_CUDA) || !defined(KOKKOS_COMPILER_NVHPC)) && \ - (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) +#if (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) TestNumericTraits(); #endif } @@ -406,10 +387,8 @@ TEST(TEST_CATEGORY, numeric_traits_min_max_exponent) { TestNumericTraits(); TestNumericTraits(); TestNumericTraits(); - // FIXME_NVHPC 23.7 long double // FIXME_OPENMPTARGET long double on Intel GPUs -#if (!defined(KOKKOS_ENABLE_CUDA) || !defined(KOKKOS_COMPILER_NVHPC)) && \ - (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) +#if (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) TestNumericTraits(); TestNumericTraits(); #endif @@ -420,31 +399,29 @@ TEST(TEST_CATEGORY, numeric_traits_min_max_exponent10) { TestNumericTraits(); TestNumericTraits(); TestNumericTraits(); - // FIXME_NVHPC 23.7 long double // FIXME_OPENMPTARGET long double on Intel GPUs -#if (!defined(KOKKOS_ENABLE_CUDA) || !defined(KOKKOS_COMPILER_NVHPC)) && \ - (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) +#if (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) TestNumericTraits(); TestNumericTraits(); #endif } TEST(TEST_CATEGORY, numeric_traits_quiet_and_signaling_nan) { -#ifndef KOKKOS_COMPILER_NVHPC // FIXME_NVHPC 23.7 +// FIXME_NVHPC +#ifdef KOKKOS_COMPILER_NVHPC + GTEST_SKIP() << "This test is known to fail with the NVHPC compiler"; +#endif TestNumericTraits(); TestNumericTraits(); TestNumericTraits(); TestNumericTraits(); -#endif TestNumericTraits(); TestNumericTraits(); TestNumericTraits(); TestNumericTraits(); - // FIXME_NVHPC 23.7 long double // FIXME_OPENMPTARGET long double on Intel GPUs -#if (!defined(KOKKOS_ENABLE_CUDA) || !defined(KOKKOS_COMPILER_NVHPC)) && \ - (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) +#if (!defined(KOKKOS_ENABLE_OPENMPTARGET) || !defined(KOKKOS_ARCH_INTEL_GPU)) TestNumericTraits(); TestNumericTraits(); #endif @@ -736,3 +713,13 @@ CHECK_NAN_INSTANTIATED_ON_CV_QUALIFIED_TYPES_FLOATING_POINT(signaling_NaN); #undef CHECK_NAN_INSTANTIATED_ON_CV_QUALIFIED_TYPES_FLOATING_POINT #undef CHECK_NAN_INSTANTIATED_ON_CV_QUALIFIED_TYPES + +#ifdef KOKKOS_COMPILER_NVCC +#ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ +#pragma nv_diagnostic pop +#else +#ifdef __CUDA_ARCH__ +#pragma diagnostic pop +#endif +#endif +#endif diff --git a/lib/kokkos/core/unit_test/TestOther.hpp b/lib/kokkos/core/unit_test/TestOther.hpp index fcf0353a88..9daef3ca3f 100644 --- a/lib/kokkos/core/unit_test/TestOther.hpp +++ b/lib/kokkos/core/unit_test/TestOther.hpp @@ -16,13 +16,8 @@ #ifndef KOKKOS_TEST_OTHER_HPP #define KOKKOS_TEST_OTHER_HPP -#include #include #include #include -// with VS 16.11.3 and CUDA 11.4.2 getting cudafe stackoverflow crash -#if !(defined(_WIN32) && defined(KOKKOS_ENABLE_CUDA)) -#include -#endif #endif diff --git a/lib/kokkos/core/unit_test/TestRangePolicyConstructors.hpp b/lib/kokkos/core/unit_test/TestRangePolicyConstructors.hpp index c8c1542af1..d6920beed0 100644 --- a/lib/kokkos/core/unit_test/TestRangePolicyConstructors.hpp +++ b/lib/kokkos/core/unit_test/TestRangePolicyConstructors.hpp @@ -20,6 +20,7 @@ #include #include +#include namespace { @@ -196,4 +197,43 @@ TEST(TEST_CATEGORY_DEATH, range_policy_implicitly_converted_bounds) { #endif } +constexpr bool test_chunk_size_explicit() { + using ExecutionSpace = TEST_EXECSPACE; + using Kokkos::ChunkSize; + +#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4 + static_assert(std::is_convertible_v); + static_assert(std::is_constructible_v); + // Some execution spaces were implicitly constructible from int + // which made the constructor call ambiguous. + static_assert( + std::is_constructible_v || + std::is_constructible_v< + Kokkos::RangePolicy, int, int, int>); + static_assert(std::is_constructible_v< + Kokkos::RangePolicy, int, int, + ChunkSize>); + static_assert(std::is_constructible_v, + ExecutionSpace, int, int, int>); + static_assert(std::is_constructible_v, + ExecutionSpace, int, int, ChunkSize>); +#else + static_assert(!std::is_convertible_v); + static_assert(std::is_constructible_v); + static_assert( + !std::is_constructible_v< + Kokkos::RangePolicy, int, int, int>); + static_assert(std::is_constructible_v< + Kokkos::RangePolicy, int, int, + ChunkSize>); + static_assert(!std::is_constructible_v, + ExecutionSpace, int, int, int>); + static_assert(std::is_constructible_v, + ExecutionSpace, int, int, ChunkSize>); +#endif + return true; +} + +static_assert(test_chunk_size_explicit()); + } // namespace diff --git a/lib/kokkos/core/unit_test/TestRealloc.hpp b/lib/kokkos/core/unit_test/TestRealloc.hpp index 2c9dc5ee47..f30c9e15e1 100644 --- a/lib/kokkos/core/unit_test/TestRealloc.hpp +++ b/lib/kokkos/core/unit_test/TestRealloc.hpp @@ -144,6 +144,11 @@ void impl_testRealloc() { EXPECT_EQ(oldPointer, newPointer); } } +struct NoDefaultConstructor { + int value; + KOKKOS_FUNCTION + NoDefaultConstructor(int x) : value(x) {} +}; template void testRealloc() { @@ -154,6 +159,14 @@ void testRealloc() { impl_testRealloc(); // without data initialization } + // Check #6992 fix (no default initialization in realloc without initializing) + { + using view_type = Kokkos::View; + view_type view_1d_no_default( + Kokkos::view_alloc(Kokkos::WithoutInitializing, "view_1d_no_default"), + 5); + realloc_dispatch(WithoutInitializing{}, view_1d_no_default, 3); + } } } // namespace TestViewRealloc diff --git a/lib/kokkos/core/unit_test/TestResize.hpp b/lib/kokkos/core/unit_test/TestResize.hpp index 13d7e16d58..3102d2b9a1 100644 --- a/lib/kokkos/core/unit_test/TestResize.hpp +++ b/lib/kokkos/core/unit_test/TestResize.hpp @@ -358,6 +358,12 @@ void impl_testResize() { } } +struct NoDefaultConstructor { + int value; + KOKKOS_FUNCTION + NoDefaultConstructor(int x) : value(x) {} +}; + template void testResize() { { @@ -367,6 +373,13 @@ void testResize() { impl_testResize(); // without data initialization } + { + using view_type = Kokkos::View; + view_type view_1d_no_default( + Kokkos::view_alloc(Kokkos::WithoutInitializing, "view_1d_no_default"), + 5); + resize_dispatch(WithoutInitializing{}, view_1d_no_default, 3); + } } } // namespace TestViewResize diff --git a/lib/kokkos/core/unit_test/TestSpaceAwareAccessor.hpp b/lib/kokkos/core/unit_test/TestSpaceAwareAccessor.hpp new file mode 100644 index 0000000000..2fad17cb85 --- /dev/null +++ b/lib/kokkos/core/unit_test/TestSpaceAwareAccessor.hpp @@ -0,0 +1,156 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#include +#include + +#include +#ifndef KOKKOS_ENABLE_CXX17 +#include +#endif + +template +struct funky_data_handle { + T* val; + + KOKKOS_FUNCTION + operator T*() { return val; } + KOKKOS_FUNCTION + operator const T*() const { return val; } +}; + +template +struct FunkyAcc { + using element_type = ElementType; + using reference = std::conditional_t, + element_type, element_type&>; + using data_handle_type = funky_data_handle; + using offset_policy = Kokkos::default_accessor; + KOKKOS_FUNCTION + reference access(data_handle_type p, size_t i) const { return p.val[i]; } + KOKKOS_FUNCTION + element_type* offset(data_handle_type p, size_t i) const { return p.val + i; } +}; + +template +void test_space_aware_accessor() { + using memory_space_t = MemorySpace; + using value_type = std::remove_const_t; + Kokkos::View v("V", 100); + + Kokkos::parallel_for( + Kokkos::RangePolicy(0, v.extent(0)), + KOKKOS_LAMBDA(int i) { v(i) = i; }); + + int errors; + using acc_t = Kokkos::Impl::SpaceAwareAccessor>; + acc_t acc{}; + typename acc_t::data_handle_type ptr{v.data()}; + + Kokkos::parallel_reduce( + Kokkos::RangePolicy(0, v.extent(0)), + KOKKOS_LAMBDA(int i, int& error) { + if (acc.access(ptr, i) != ptr[i]) error++; + if (acc.offset(ptr, i) != ptr + i) error++; + static_assert(std::is_same_v); + if constexpr (std::is_const_v) { + static_assert(std::is_same_v); + } else { + static_assert(std::is_same_v); + } + static_assert(std::is_same_v>); + static_assert( + std::is_same_v>>); + if constexpr (std::is_const_v) { + static_assert(std::is_same_v>); + } else { + static_assert(std::is_same_v); + } + static_assert(std::is_same_v); + static_assert(std::is_same_v&>); + static_assert(std::is_nothrow_move_constructible_v); + static_assert(std::is_nothrow_move_assignable_v); + static_assert(std::is_nothrow_swappable_v); + static_assert( + std::is_same_v); + static_assert( + std::is_same_v>); +#ifndef KOKKOS_ENABLE_CXX17 + static_assert(std::copyable); + static_assert(std::is_empty_v); +#endif + }, + errors); + ASSERT_EQ(errors, 0); +} + +void test_space_aware_accessor_conversion() { + using ExecutionSpace = TEST_EXECSPACE; + using memory_space_t = typename ExecutionSpace::memory_space; + using T = float; + using acc_t = Kokkos::Impl::SpaceAwareAccessor>; + using const_acc_t = + Kokkos::Impl::SpaceAwareAccessor>; + using int_acc_t = + Kokkos::Impl::SpaceAwareAccessor>; + using host_acc_t = + Kokkos::Impl::SpaceAwareAccessor>; + using anon_acc_t = + Kokkos::Impl::SpaceAwareAccessor>; + + Kokkos::parallel_for( + Kokkos::RangePolicy(0, 1), KOKKOS_LAMBDA(int) { + static_assert(std::is_constructible_v); + static_assert(std::is_convertible_v); + static_assert(!std::is_constructible_v); + static_assert(!std::is_constructible_v); + static_assert( + std::is_constructible_v == + Kokkos::Impl::MemorySpaceAccess::assignable); + static_assert( + std::is_constructible_v == + Kokkos::Impl::MemorySpaceAccess::assignable); + static_assert(std::is_constructible_v); + static_assert(std::is_constructible_v); + static_assert(std::is_convertible_v); + static_assert(std::is_convertible_v); + }); +} + +TEST(TEST_CATEGORY, mdspan_space_aware_accessor) { + using ExecutionSpace = TEST_EXECSPACE; + test_space_aware_accessor(); + test_space_aware_accessor(); + test_space_aware_accessor(); + test_space_aware_accessor(); + test_space_aware_accessor(); + test_space_aware_accessor(); + test_space_aware_accessor_conversion(); +} diff --git a/lib/kokkos/core/unit_test/TestSpaceAwareAccessorAccessViolation.hpp b/lib/kokkos/core/unit_test/TestSpaceAwareAccessorAccessViolation.hpp new file mode 100644 index 0000000000..b9982d5fc4 --- /dev/null +++ b/lib/kokkos/core/unit_test/TestSpaceAwareAccessorAccessViolation.hpp @@ -0,0 +1,128 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#include + +#include + +template +struct TestMemoryAccessViolation { + Kokkos::Impl::SpaceAwareAccessor> + acc; + + KOKKOS_FUNCTION decltype(auto) bad_access() const { + return acc.access(nullptr, 0); + } + + KOKKOS_FUNCTION void operator()(int) const { ++bad_access(); } + + TestMemoryAccessViolation(ExecutionSpace const& s, + std::string const& matcher) { + constexpr bool accessible_from_execution_space = Kokkos::SpaceAccessibility< + /*AccessSpace=*/ExecutionSpace, + /*MemorySpace=*/MemorySpace>::accessible; + EXPECT_FALSE(accessible_from_execution_space); + EXPECT_DEATH( + { + Kokkos::parallel_for(Kokkos::RangePolicy(s, 0, 1), + *this); + Kokkos::fence(); + }, + matcher); + } +}; + +template +void test_memory_access_violation(ExecutionSpace const& s, + std::string const& m) { + TestMemoryAccessViolation(s, m); +} + +template +void test_memory_access_violations_from_host() { + using memory_space_t = typename ExecutionSpace::memory_space; + using exec_space_t = Kokkos::DefaultHostExecutionSpace; + const exec_space_t exec_space{}; + std::string const message = + "Kokkos::SpaceAwareAccessor ERROR: attempt to access inaccessible memory " + "space"; + test_memory_access_violation(exec_space, + message); +} + +template +void test_memory_access_violations_from_device() { + using memory_space_t = Kokkos::HostSpace; + using exec_space_t = ExecutionSpace; + const exec_space_t exec_space{}; + std::string const message = + "Kokkos::SpaceAwareAccessor ERROR: attempt to access inaccessible memory " + "space"; + test_memory_access_violation(exec_space, + message); +} + +// FIXME_SYCL +#if !(defined(KOKKOS_COMPILER_INTEL_LLVM) && defined(KOKKOS_ENABLE_SYCL)) +TEST(TEST_CATEGORY_DEATH, + mdspan_space_aware_accessor_invalid_access_from_host) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + + using ExecutionSpace = TEST_EXECSPACE; + + if (Kokkos::SpaceAccessibility< + /*AccessSpace=*/Kokkos::HostSpace, + /*MemorySpace=*/typename ExecutionSpace::memory_space>::accessible) { + GTEST_SKIP() << "skipping since no memory access violation would occur"; + } + + test_memory_access_violations_from_host(); +} +#endif + +TEST(TEST_CATEGORY_DEATH, + mdspan_space_aware_accessor_invalid_access_from_device) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + + using ExecutionSpace = TEST_EXECSPACE; + + if (Kokkos::SpaceAccessibility< + /*AccessSpace=*/ExecutionSpace, + /*MemorySpace=*/Kokkos::HostSpace>::accessible) { + GTEST_SKIP() << "skipping since no memory access violation would occur"; + } + +#if defined(KOKKOS_ENABLE_SYCL) && defined(NDEBUG) // FIXME_SYCL + if (std::is_same::value) { + GTEST_SKIP() << "skipping SYCL device-side abort does not work when NDEBUG " + "is defined"; + } +#endif +#if defined(KOKKOS_ENABLE_OPENMPTARGET) // FIXME_OPENMPTARGET + if (std::is_same::value) { + GTEST_SKIP() << "skipping because OpenMPTarget backend is currently not " + "able to abort from the device"; + } +#endif +#if defined(KOKKOS_ENABLE_OPENACC) // FIXME_OPENACC + if (std::is_same::value) { + GTEST_SKIP() << "skipping because OpenACC backend is currently not " + "able to abort from the device"; + } +#endif + + test_memory_access_violations_from_device(); +} diff --git a/lib/kokkos/core/unit_test/TestTeamMDRangePolicyCTAD.cpp b/lib/kokkos/core/unit_test/TestTeamMDRangePolicyCTAD.cpp new file mode 100644 index 0000000000..0de639e02e --- /dev/null +++ b/lib/kokkos/core/unit_test/TestTeamMDRangePolicyCTAD.cpp @@ -0,0 +1,199 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#include + +namespace { + +struct TestTeamThreadMDRangeCTAD { + using TeamPolicy = Kokkos::TeamPolicy; + using TeamHandle = TeamPolicy::member_type; + + KOKKOS_FUNCTION void operator()(TeamHandle const& team_handle) const { + { + Kokkos::TeamThreadMDRange md_range(team_handle, 0, 0); + static_assert( + std::is_same_v, TeamHandle>, + decltype(md_range)>); + } + + { + Kokkos::TeamThreadMDRange md_range(team_handle, 0, 0, 0); + static_assert( + std::is_same_v, TeamHandle>, + decltype(md_range)>); + } + + { + Kokkos::TeamThreadMDRange md_range(team_handle, 0, 0, 0, 0); + static_assert( + std::is_same_v, TeamHandle>, + decltype(md_range)>); + } + + { + Kokkos::TeamThreadMDRange md_range(team_handle, 0, 0, 0, 0, 0); + static_assert( + std::is_same_v, TeamHandle>, + decltype(md_range)>); + } + + { + Kokkos::TeamThreadMDRange md_range(team_handle, 0, 0, 0, 0, 0, 0); + static_assert( + std::is_same_v, TeamHandle>, + decltype(md_range)>); + } + + { + Kokkos::TeamThreadMDRange md_range(team_handle, 0, 0, 0, 0, 0, 0, 0); + static_assert( + std::is_same_v, TeamHandle>, + decltype(md_range)>); + } + + { + Kokkos::TeamThreadMDRange md_range(team_handle, 0, 0, 0, 0, 0, 0, 0, 0); + static_assert( + std::is_same_v, TeamHandle>, + decltype(md_range)>); + } + } + + TestTeamThreadMDRangeCTAD() { + Kokkos::parallel_for(TeamPolicy(0, Kokkos::AUTO), *this); + } +}; + +struct TestTeamVectorMDRangeCTAD { + using TeamPolicy = Kokkos::TeamPolicy; + using TeamHandle = TeamPolicy::member_type; + + KOKKOS_FUNCTION void operator()(TeamHandle const& team_handle) const { + { + Kokkos::TeamVectorMDRange md_range(team_handle, 0, 0); + static_assert( + std::is_same_v, TeamHandle>, + decltype(md_range)>); + } + { + Kokkos::TeamVectorMDRange md_range(team_handle, 0, 0, 0); + static_assert( + std::is_same_v, TeamHandle>, + decltype(md_range)>); + } + + { + Kokkos::TeamVectorMDRange md_range(team_handle, 0, 0, 0, 0); + static_assert( + std::is_same_v, TeamHandle>, + decltype(md_range)>); + } + + { + Kokkos::TeamVectorMDRange md_range(team_handle, 0, 0, 0, 0, 0); + static_assert( + std::is_same_v, TeamHandle>, + decltype(md_range)>); + } + + { + Kokkos::TeamVectorMDRange md_range(team_handle, 0, 0, 0, 0, 0, 0); + static_assert( + std::is_same_v, TeamHandle>, + decltype(md_range)>); + } + + { + Kokkos::TeamVectorMDRange md_range(team_handle, 0, 0, 0, 0, 0, 0, 0); + static_assert( + std::is_same_v, TeamHandle>, + decltype(md_range)>); + } + + { + Kokkos::TeamVectorMDRange md_range(team_handle, 0, 0, 0, 0, 0, 0, 0, 0); + static_assert( + std::is_same_v, TeamHandle>, + decltype(md_range)>); + } + } + + TestTeamVectorMDRangeCTAD() { + Kokkos::parallel_for(TeamPolicy(0, Kokkos::AUTO), *this); + } +}; + +struct TestThreadVectorMDRangeCTAD { + using TeamPolicy = Kokkos::TeamPolicy; + using TeamHandle = TeamPolicy::member_type; + + template + KOKKOS_FUNCTION static void check_types([ + [maybe_unused]] PolicyTypeToCheck const& team_handle) { + static_assert(std::is_same_v); + } + + KOKKOS_FUNCTION void operator()(TeamHandle const& team_handle) const { + { + Kokkos::ThreadVectorMDRange md_range(team_handle, 0, 0); + check_types, TeamHandle>>( + md_range); + } + + { + Kokkos::ThreadVectorMDRange md_range(team_handle, 0, 0, 0); + check_types, TeamHandle>>( + md_range); + } + + { + Kokkos::ThreadVectorMDRange md_range(team_handle, 0, 0, 0, 0); + check_types, TeamHandle>>( + md_range); + } + + { + Kokkos::ThreadVectorMDRange md_range(team_handle, 0, 0, 0, 0, 0); + check_types, TeamHandle>>( + md_range); + } + + { + Kokkos::ThreadVectorMDRange md_range(team_handle, 0, 0, 0, 0, 0, 0); + check_types, TeamHandle>>( + md_range); + } + + { + Kokkos::ThreadVectorMDRange md_range(team_handle, 0, 0, 0, 0, 0, 0, 0); + check_types, TeamHandle>>( + md_range); + } + + { + Kokkos::ThreadVectorMDRange md_range(team_handle, 0, 0, 0, 0, 0, 0, 0, 0); + check_types, TeamHandle>>( + md_range); + } + } + + TestThreadVectorMDRangeCTAD() { + Kokkos::parallel_for(TeamPolicy(0, Kokkos::AUTO), *this); + } +}; + +} // namespace diff --git a/lib/kokkos/core/unit_test/TestTeamPolicyCTAD.cpp b/lib/kokkos/core/unit_test/TestTeamPolicyCTAD.cpp new file mode 100644 index 0000000000..07aaeae819 --- /dev/null +++ b/lib/kokkos/core/unit_test/TestTeamPolicyCTAD.cpp @@ -0,0 +1,135 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#include + +namespace { + +struct TestTeamPolicyCTAD { + template + static void maybe_unused(Ts&&...) {} + + struct SomeExecutionSpace { + using execution_space = SomeExecutionSpace; + using size_type = size_t; + }; + static_assert(Kokkos::is_execution_space_v); + + struct ImplicitlyConvertibleToDefaultExecutionSpace { + [[maybe_unused]] operator Kokkos::DefaultExecutionSpace() const { + return Kokkos::DefaultExecutionSpace(); + } + }; + static_assert(!Kokkos::is_execution_space_v< + ImplicitlyConvertibleToDefaultExecutionSpace>); + + [[maybe_unused]] static inline Kokkos::DefaultExecutionSpace des; + [[maybe_unused]] static inline ImplicitlyConvertibleToDefaultExecutionSpace + notEs; + [[maybe_unused]] static inline SomeExecutionSpace ses; + + [[maybe_unused]] static inline int i; + + // Workaround for nvc++ (CUDA-11.7-NVHPC) ignoring [[maybe_unused]] on + // ImplicitlyConvertibleToDefaultExecutionSpace::operator + // Kokkos::DefaultExecutionSpace() const + [[maybe_unused]] static inline Kokkos::DefaultExecutionSpace notEsToDes = + notEs; + + // Workaround for HIP-ROCm-5.2 warning about was declared but never referenced + TestTeamPolicyCTAD() { maybe_unused(des, notEs, ses, i, notEsToDes); } + + // Default construction deduces to TeamPolicy<> + static_assert( + std::is_same_v, decltype(Kokkos::TeamPolicy{})>); + + // Execution space not provided deduces to TeamPolicy<> + + static_assert( + std::is_same_v, decltype(Kokkos::TeamPolicy(i, i))>); + static_assert(std::is_same_v, + decltype(Kokkos::TeamPolicy(i, i, i))>); + static_assert(std::is_same_v, + decltype(Kokkos::TeamPolicy(i, Kokkos::AUTO))>); + static_assert( + std::is_same_v, + decltype(Kokkos::TeamPolicy(i, Kokkos::AUTO, i))>); + static_assert(std::is_same_v, + decltype(Kokkos::TeamPolicy(i, Kokkos::AUTO, + Kokkos::AUTO))>); + static_assert( + std::is_same_v, + decltype(Kokkos::TeamPolicy(i, i, Kokkos::AUTO))>); + + // DefaultExecutionSpace deduces to TeamPolicy<> + + static_assert(std::is_same_v, + decltype(Kokkos::TeamPolicy(des, i, i))>); + static_assert(std::is_same_v, + decltype(Kokkos::TeamPolicy(des, i, i, i))>); + static_assert( + std::is_same_v, + decltype(Kokkos::TeamPolicy(des, i, Kokkos::AUTO))>); + static_assert( + std::is_same_v, + decltype(Kokkos::TeamPolicy(des, i, Kokkos::AUTO, i))>); + static_assert(std::is_same_v, + decltype(Kokkos::TeamPolicy(des, i, Kokkos::AUTO, + Kokkos::AUTO))>); + static_assert( + std::is_same_v, + decltype(Kokkos::TeamPolicy(des, i, i, Kokkos::AUTO))>); + + // Convertible to DefaultExecutionSpace deduces to TeamPolicy<> + + static_assert(std::is_same_v, + decltype(Kokkos::TeamPolicy(notEs, i, i))>); + static_assert(std::is_same_v, + decltype(Kokkos::TeamPolicy(notEs, i, i, i))>); + static_assert( + std::is_same_v, + decltype(Kokkos::TeamPolicy(notEs, i, Kokkos::AUTO))>); + static_assert( + std::is_same_v, + decltype(Kokkos::TeamPolicy(notEs, i, Kokkos::AUTO, i))>); + static_assert(std::is_same_v, + decltype(Kokkos::TeamPolicy( + notEs, i, Kokkos::AUTO, Kokkos::AUTO))>); + static_assert( + std::is_same_v, + decltype(Kokkos::TeamPolicy(notEs, i, i, Kokkos::AUTO))>); + + // SES != DefaultExecutionSpace deduces to TeamPolicy + + static_assert(std::is_same_v, + decltype(Kokkos::TeamPolicy(ses, i, i))>); + static_assert(std::is_same_v, + decltype(Kokkos::TeamPolicy(ses, i, i, i))>); + static_assert( + std::is_same_v, + decltype(Kokkos::TeamPolicy(ses, i, Kokkos::AUTO))>); + static_assert( + std::is_same_v, + decltype(Kokkos::TeamPolicy(ses, i, Kokkos::AUTO, i))>); + static_assert(std::is_same_v, + decltype(Kokkos::TeamPolicy(ses, i, Kokkos::AUTO, + Kokkos::AUTO))>); + static_assert( + std::is_same_v, + decltype(Kokkos::TeamPolicy(ses, i, i, Kokkos::AUTO))>); +}; + +} // namespace diff --git a/lib/kokkos/core/unit_test/TestTeamVector.hpp b/lib/kokkos/core/unit_test/TestTeamVector.hpp index 5e16539d65..4d8f42720d 100644 --- a/lib/kokkos/core/unit_test/TestTeamVector.hpp +++ b/lib/kokkos/core/unit_test/TestTeamVector.hpp @@ -1060,11 +1060,8 @@ TEST(TEST_CATEGORY, parallel_scan_with_reducers) { constexpr int n = 1000000; constexpr int n_vector_range = 100; -#if defined(KOKKOS_ENABLE_CUDA) && \ - defined(KOKKOS_COMPILER_NVHPC) // FIXME_NVHPC 23.7 - if constexpr (std::is_same_v) { - GTEST_SKIP() << "All but max inclusive scan differ at index 101"; - } +#ifdef KOKKOS_IMPL_32BIT + GTEST_SKIP() << "Failing KOKKOS_IMPL_32BIT"; // FIXME_32BIT #endif checkScan(0))); -#if defined(KOKKOS_ENABLE_CUDA) && \ - defined(KOKKOS_COMPILER_NVHPC) // FIXME_NVHPC 23.7 - if constexpr (std::is_same_v) { - GTEST_SKIP() << "Disabling 2/3rd of the test for now"; - } -#endif ASSERT_TRUE((TestTeamVectorRange::Test(1))); // FIXME_OPENMPTARGET - Use of kokkos reducers currently results in runtime // memory errors. diff --git a/lib/kokkos/core/unit_test/TestViewAPI.hpp b/lib/kokkos/core/unit_test/TestViewAPI.hpp index ca098dbc24..53c1f01678 100644 --- a/lib/kokkos/core/unit_test/TestViewAPI.hpp +++ b/lib/kokkos/core/unit_test/TestViewAPI.hpp @@ -837,18 +837,15 @@ struct TestViewMirror { view_const_cast(v)); } - template + template struct CopyUnInit { - using mirror_view_type = typename Kokkos::Impl::MirrorViewType< - Space, double *, Layout, Kokkos::HostSpace, MemoryTraits>::view_type; - - mirror_view_type a_d; + View a_d; KOKKOS_INLINE_FUNCTION - CopyUnInit(mirror_view_type &a_d_) : a_d(a_d_) {} + explicit CopyUnInit(View const &a_d_) : a_d(a_d_) {} KOKKOS_INLINE_FUNCTION - void operator()(const typename Space::size_type i) const { + void operator()(const typename View::size_type i) const { a_d(i) = (double)(10 - i); } }; @@ -875,7 +872,8 @@ struct TestViewMirror { Kokkos::parallel_for( Kokkos::RangePolicy(0, int(10)), - CopyUnInit(a_d)); + // decltype required for Intel classics, that doesn't recognize the CTAD + CopyUnInit(a_d)); Kokkos::deep_copy(a_h, a_d); @@ -1339,6 +1337,40 @@ class TestViewAPI { ASSERT_EQ(dz.data(), nullptr); } + struct test_refcount_poison_copy_functor { + using view_type = Kokkos::View; + explicit test_refcount_poison_copy_functor(view_type v) : view(v) {} + + test_refcount_poison_copy_functor( + const test_refcount_poison_copy_functor &other) + : view(other.view) { + throw std::bad_alloc(); + } + + KOKKOS_INLINE_FUNCTION void operator()(int) const {} + + view_type view; + }; + + static void run_test_refcount_exception() { + using view_type = typename test_refcount_poison_copy_functor::view_type; + view_type original("original", N0); + ASSERT_EQ(original.use_count(), 1); + + // test_refcount_poison_copy_functor throws during copy construction + try { + Kokkos::parallel_for( + Kokkos::RangePolicy(0, N0), + test_refcount_poison_copy_functor(original)); + } catch (const std::bad_alloc &) { + } + + // Ensure refcounting is enabled, we should increment here + auto copy = original; + ASSERT_EQ(original.use_count(), 2); + ASSERT_EQ(copy.use_count(), 2); + } + static void run_test_deep_copy_empty() { // Check Deep Copy of LayoutLeft to LayoutRight { @@ -1539,56 +1571,6 @@ class TestViewAPI { typename multivector_type::const_type cmvX(cmv); typename const_multivector_type::const_type ccmvX(cmv); } - - static void run_test_error() { -#ifdef KOKKOS_ENABLE_OPENMPTARGET - if (std::is_same::value) - return; -#endif -// FIXME_MSVC_WITH_CUDA -// This test doesn't behave as expected on Windows with CUDA -#if defined(_WIN32) && defined(KOKKOS_ENABLE_CUDA) - if (std::is_same::value) - return; -#endif - bool did_throw = false; - auto alloc_size = std::numeric_limits::max() - 42; - try { - auto should_always_fail = dView1("hello_world_failure", alloc_size); - } catch (std::runtime_error const &error) { - // TODO once we remove the conversion to std::runtime_error, catch the - // appropriate Kokkos error here - std::string msg = error.what(); - ASSERT_PRED_FORMAT2(::testing::IsSubstring, "hello_world_failure", msg); - ASSERT_PRED_FORMAT2(::testing::IsSubstring, - typename device::memory_space{}.name(), msg); - // Can't figure out how to make assertions either/or, so we'll just use - // an if statement here for now. Test failure message will be a bit - // misleading, but developers should figure out what's going on pretty - // quickly. - if (msg.find("is not a valid size") != std::string::npos) { - ASSERT_PRED_FORMAT2(::testing::IsSubstring, "is not a valid size", msg); - } else -#ifdef KOKKOS_ENABLE_SYCL - if (msg.find("insufficient memory") != std::string::npos) -#endif - { - ASSERT_PRED_FORMAT2(::testing::IsSubstring, "insufficient memory", msg); - } - // SYCL cannot tell the reason why a memory allocation failed -#ifdef KOKKOS_ENABLE_SYCL - else { - // Otherwise, there has to be some sort of "unknown error" error - ASSERT_PRED_FORMAT2(::testing::IsSubstring, - "because of an unknown error.", msg); - } -#endif - did_throw = true; - } - ASSERT_TRUE(did_throw); - } }; } // namespace Test diff --git a/lib/kokkos/core/unit_test/TestViewAPI_c.hpp b/lib/kokkos/core/unit_test/TestViewAPI_c.hpp index 5efbd95bc9..042da1e984 100644 --- a/lib/kokkos/core/unit_test/TestViewAPI_c.hpp +++ b/lib/kokkos/core/unit_test/TestViewAPI_c.hpp @@ -19,6 +19,7 @@ namespace Test { TEST(TEST_CATEGORY, view_api_c) { + TestViewAPI::run_test_refcount_exception(); TestViewAPI::run_test_deep_copy_empty(); TestViewAPI::run_test_view_operator_b(); } diff --git a/lib/kokkos/core/unit_test/TestViewAPI_d.hpp b/lib/kokkos/core/unit_test/TestViewAPI_d.hpp index b0d759ffcc..075ac3329c 100644 --- a/lib/kokkos/core/unit_test/TestViewAPI_d.hpp +++ b/lib/kokkos/core/unit_test/TestViewAPI_d.hpp @@ -26,22 +26,4 @@ TEST(TEST_CATEGORY, view_api_d) { TestViewAPI::run_test_view_operator_c(); } -TEST(TEST_CATEGORY, view_allocation_error) { -#if defined(__has_feature) -#if __has_feature(address_sanitizer) - GTEST_SKIP() << "AddressSanitzer detects allocating too much memory " - "preventing our checks to run"; -#endif -#endif -#if ((HIP_VERSION_MAJOR == 5) && (HIP_VERSION_MINOR == 3)) - GTEST_SKIP() << "ROCm 5.3 segfaults when trying to allocate too much memory"; -#endif -#if defined(KOKKOS_ENABLE_OPENACC) // FIXME_OPENACC - if (std::is_same_v) { - GTEST_SKIP() << "acc_malloc() not properly returning nullptr"; - } -#endif - TestViewAPI::run_test_error(); -} - } // namespace Test diff --git a/lib/kokkos/core/unit_test/TestViewBadAlloc.hpp b/lib/kokkos/core/unit_test/TestViewBadAlloc.hpp new file mode 100644 index 0000000000..7cb2f91655 --- /dev/null +++ b/lib/kokkos/core/unit_test/TestViewBadAlloc.hpp @@ -0,0 +1,86 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#include + +#include + +namespace { + +template +void test_view_bad_alloc() { + bool did_throw = false; + auto too_large = std::numeric_limits::max() - 42; + std::string label = "my_label"; + try { + auto should_always_fail = + Kokkos::View(label, too_large); + } catch (std::runtime_error const &error) { + std::string msg = error.what(); + ASSERT_PRED_FORMAT2( + ::testing::IsSubstring, + std::string(MemorySpace::name()) + " memory space failed to allocate", + msg) + << "memory space name is missing"; + ASSERT_PRED_FORMAT2(::testing::IsSubstring, + std::string("(label=\"") + label + "\")", msg) + << "label is missing"; + did_throw = true; + } + ASSERT_TRUE(did_throw); +} + +TEST(TEST_CATEGORY, view_bad_alloc) { + using ExecutionSpace = TEST_EXECSPACE; + using MemorySpace = ExecutionSpace::memory_space; +#if defined(__has_feature) +#if __has_feature(address_sanitizer) + if (std::is_same_v) { + GTEST_SKIP() << "AddressSanitizer detects allocating too much memory " + "preventing our checks to run"; + } +#endif +#endif +#if ((HIP_VERSION_MAJOR == 5) && (HIP_VERSION_MINOR == 3)) + if (std::is_same_v) { + GTEST_SKIP() + << "ROCm 5.3 segfaults when trying to allocate too much memory"; + } +#endif +#if defined(KOKKOS_ENABLE_OPENACC) // FIXME_OPENACC + if (std::is_same_v) { + GTEST_SKIP() << "acc_malloc() not properly returning nullptr"; + } +#endif + + test_view_bad_alloc(); + + constexpr bool execution_space_is_device = + std::is_same_v && + !std::is_same_v; + + if constexpr (execution_space_is_device) { + if constexpr (Kokkos::has_shared_space) { + test_view_bad_alloc(); + } + if constexpr (Kokkos::has_shared_host_pinned_space) { + test_view_bad_alloc(); + } + } +} + +} // namespace diff --git a/lib/kokkos/core/unit_test/TestViewCopy_c.hpp b/lib/kokkos/core/unit_test/TestViewCopy_c.hpp new file mode 100644 index 0000000000..758af13c7d --- /dev/null +++ b/lib/kokkos/core/unit_test/TestViewCopy_c.hpp @@ -0,0 +1,434 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#include + +#include + +namespace { +// Do not rely on deep_copy(0) as we want to test it! +template +void reset_view(const ExecSpace& space, ViewType& a, int magic) { + auto policy = Kokkos::RangePolicy(space, 0, a.span()); + + assert(a.span_is_contiguous()); + + Kokkos::parallel_for( + "TestViewCopy::ResetView", policy, + KOKKOS_LAMBDA(int i) { a.data()[i] = magic; }); +} + +template +size_t compute_overall_sum(const ExecSpace& space, ViewType& a) { + auto policy = Kokkos::RangePolicy(space, 0, a.span()); + + assert(a.span_is_contiguous()); + + typename ViewType::value_type sum = 0; + Kokkos::parallel_reduce( + "TestViewCopy::ComputeSum", policy, + KOKKOS_LAMBDA(int i, int& lcl_sum) { lcl_sum += a.data()[i]; }, sum); + + return static_cast(sum); +} + +template +bool check_magic_value( + const ExecSpace& space, const Kokkos::View& a, int magic, + std::enable_if_t::rank == 0>* = nullptr) { + auto policy = Kokkos::RangePolicy(space, 0, 1); + + bool all_elements_are_set; // Uninitialized, set by parallel_reduce + + Kokkos::parallel_reduce( + "TestViewCopy::CheckMagicValueRank0", policy, + KOKKOS_LAMBDA(int, bool& local_check) { local_check &= (a() == magic); }, + Kokkos::LAnd(all_elements_are_set)); + + return all_elements_are_set; +} + +template +bool check_magic_value( + const ExecSpace& space, const Kokkos::View& a, int magic, + std::enable_if_t::rank == 1>* = nullptr) { + auto policy = Kokkos::RangePolicy(space, 0, a.extent(0)); + + bool all_elements_are_set; // Uninitialized, set by parallel_reduce + + Kokkos::parallel_reduce( + "TestViewCopy::CheckMagicValueRank1", policy, + KOKKOS_LAMBDA(int i, bool& local_check) { + local_check &= (a(i) == magic); + }, + Kokkos::LAnd(all_elements_are_set)); + + return all_elements_are_set; +} + +template +bool check_magic_value( + const ExecSpace& space, const Kokkos::View& a, int magic, + std::enable_if_t::rank == 2>* = nullptr) { + auto policy = Kokkos::MDRangePolicy, ExecSpace>( + space, {0, 0}, {a.extent(0), a.extent(1)}); + + bool all_elements_are_set; // Uninitialized, set by parallel_reduce + + Kokkos::parallel_reduce( + "TestViewCopy::CheckMagicValueRank2", policy, + KOKKOS_LAMBDA(int i0, int i1, bool& local_check) { + local_check &= (a(i0, i1) == magic); + }, + Kokkos::LAnd(all_elements_are_set)); + + return all_elements_are_set; +} + +template +bool check_magic_value( + const ExecSpace& space, const Kokkos::View& a, int magic, + std::enable_if_t::rank == 3>* = nullptr) { + auto policy = Kokkos::MDRangePolicy, ExecSpace>( + space, {0, 0, 0}, {a.extent(0), a.extent(1), a.extent(2)}); + + bool all_elements_are_set; // Uninitialized, set by parallel_reduce + + Kokkos::parallel_reduce( + "TestViewCopy::CheckMagicValueRank3", policy, + KOKKOS_LAMBDA(int i0, int i1, int i2, bool& local_check) { + local_check &= (a(i0, i1, i2) == magic); + }, + Kokkos::LAnd(all_elements_are_set)); + + return all_elements_are_set; +} + +template +bool check_magic_value( + const ExecSpace& space, const Kokkos::View& a, int magic, + std::enable_if_t::rank == 4>* = nullptr) { + auto policy = Kokkos::MDRangePolicy, ExecSpace>( + space, {0, 0, 0, 0}, + {a.extent(0), a.extent(1), a.extent(2), a.extent(3)}); + + bool all_elements_are_set; // Uninitialized, set by parallel_reduce + + Kokkos::parallel_reduce( + "TestViewCopy::CheckMagicValueRank4", policy, + KOKKOS_LAMBDA(int i0, int i1, int i2, int i3, bool& local_check) { + local_check &= (a(i0, i1, i2, i3) == magic); + }, + Kokkos::LAnd(all_elements_are_set)); + + return all_elements_are_set; +} + +template +bool check_magic_value( + const ExecSpace& space, const Kokkos::View& a, int magic, + std::enable_if_t::rank == 5>* = nullptr) { + auto policy = Kokkos::MDRangePolicy, ExecSpace>( + space, {0, 0, 0, 0, 0}, + {a.extent(0), a.extent(1), a.extent(2), a.extent(3), a.extent(4)}); + + bool all_elements_are_set; // Uninitialized, set by parallel_reduce + + Kokkos::parallel_reduce( + "TestViewCopy::CheckMagicValueRank5", policy, + KOKKOS_LAMBDA(int i0, int i1, int i2, int i3, int i4, bool& local_check) { + local_check &= (a(i0, i1, i2, i3, i4) == magic); + }, + Kokkos::LAnd(all_elements_are_set)); + + return all_elements_are_set; +} + +template +bool check_magic_value( + const ExecSpace& space, const Kokkos::View& a, int magic, + std::enable_if_t::rank == 6>* = nullptr) { + auto policy = Kokkos::MDRangePolicy, ExecSpace>( + space, {0, 0, 0, 0, 0, 0}, + {a.extent(0), a.extent(1), a.extent(2), a.extent(3), a.extent(4), + a.extent(5)}); + + bool all_elements_are_set; // Uninitialized, set by parallel_reduce + + Kokkos::parallel_reduce( + "TestViewCopy::CheckMagicValueRank6", policy, + KOKKOS_LAMBDA(int i0, int i1, int i2, int i3, int i4, int i5, + bool& local_check) { + local_check &= (a(i0, i1, i2, i3, i4, i5) == magic); + }, + Kokkos::LAnd(all_elements_are_set)); + + return all_elements_are_set; +} + +template +bool check_magic_value( + const ExecSpace& space, const Kokkos::View& a, int magic, + std::enable_if_t::rank == 7>* = nullptr) { + auto policy = Kokkos::MDRangePolicy, ExecSpace>( + space, {0, 0, 0, 0, 0, 0}, + {a.extent(0), a.extent(1), a.extent(2), a.extent(3), a.extent(4), + a.extent(5)}); + + bool all_elements_are_set = true; + + for (size_t outer = 0; outer < a.extent(6); ++outer) { + bool all_local_elements_are_set; // Uninitialized, set by parallel_reduce + Kokkos::parallel_reduce( + "TestViewCopy::CheckMagicValueRank7", policy, + KOKKOS_LAMBDA(int i0, int i1, int i2, int i3, int i4, int i5, + bool& local_check) { + local_check &= (a(i0, i1, i2, i3, i4, i5, outer) == magic); + }, + Kokkos::LAnd(all_local_elements_are_set)); + + all_elements_are_set = all_elements_are_set && all_local_elements_are_set; + } + return all_elements_are_set; +} + +template +bool check_magic_value( + const ExecSpace& space, const Kokkos::View& a, int magic, + std::enable_if_t::rank == 8>* = nullptr) { + auto policy = Kokkos::MDRangePolicy, ExecSpace>( + space, {0, 0, 0, 0, 0, 0}, + {a.extent(0), a.extent(1), a.extent(2), a.extent(3), a.extent(4), + a.extent(5)}); + + bool all_elements_are_set = true; + + for (size_t outer = 0; outer < a.extent(7); ++outer) { + for (size_t inner = 0; inner < a.extent(6); ++inner) { + bool all_local_elements_are_set; // Uninitialized, set by parallel_reduce + Kokkos::parallel_reduce( + "TestViewCopy::CheckMagicValueRank8", policy, + KOKKOS_LAMBDA(int i0, int i1, int i2, int i3, int i4, int i5, + bool& local_check) { + local_check &= (a(i0, i1, i2, i3, i4, i5, inner, outer) == magic); + }, + Kokkos::LAnd(all_local_elements_are_set)); + + all_elements_are_set = all_elements_are_set && all_local_elements_are_set; + } + } + return all_elements_are_set; +} + +template +bool view_fill_test(const ExecSpace& space, ViewType& a, int magic) { + Kokkos::deep_copy(space, a, magic); +#if defined(KOKKOS_ENABLE_OPENMPTARGET) + // FIXME_OPENMPTARGET Does not work with Land reducer + return true; +#else // KOKKOS_ENABLE_OPENMPTARGET + return check_magic_value(space, a, magic); +#endif // KOKKOS_ENABLE_OPENMPTARGET +} + +template +void run_test() { + int magic = 19; + + using ViewType = Kokkos::View; + // Create views with different lengths for each dimension + // We want to test if all loops are over the correct dimensions + // We use prime numbers to make sure that the strides are different + ViewType a_decreasing("a", 23, 19, 17, 13, 11, 7, 5, 3); + // We also test with increasing strides to catch more "out-of-bounds" errors + // within subviews. + ViewType a_increasing("a", 3, 5, 7, 11, 13, 17, 19, 23); + + using exec_space = typename Space::execution_space; + auto space = exec_space(); + + // Use subviews in the tests to have cases with different ranks and + // non-contiguous memory + // Tests have two parts: + // 1. Fill the subview with a magic value and check that all elements are set + // 2. Check if only the subview is set by summing all elements in the view and + // comparing to the subview size times the magic value + + // Rank 0 + { + auto sub_dec = Kokkos::subview(a_decreasing, 0, 0, 0, 0, 0, 0, 0, 0); + EXPECT_TRUE(view_fill_test(space, sub_dec, magic)); + EXPECT_EQ(compute_overall_sum(space, a_decreasing), + static_cast(magic)); + + auto sub_inc = Kokkos::subview(a_increasing, 0, 0, 0, 0, 0, 0, 0, 0); + EXPECT_TRUE(view_fill_test(space, sub_inc, magic)); + EXPECT_EQ(compute_overall_sum(space, a_increasing), + static_cast(magic)); + } + reset_view(space, a_decreasing, 0); + reset_view(space, a_increasing, 0); + + // Rank 1 + { + auto sub_dec = + Kokkos::subview(a_decreasing, Kokkos::ALL, 0, 0, 0, 0, 0, 0, 0); + EXPECT_TRUE(view_fill_test(space, sub_dec, magic)); + EXPECT_EQ(compute_overall_sum(space, a_decreasing), sub_dec.size() * magic); + + auto sub_inc = + Kokkos::subview(a_increasing, Kokkos::ALL, 0, 0, 0, 0, 0, 0, 0); + EXPECT_TRUE(view_fill_test(space, sub_inc, magic)); + EXPECT_EQ(compute_overall_sum(space, a_increasing), sub_inc.size() * magic); + } + reset_view(space, a_decreasing, 0); + reset_view(space, a_increasing, 0); + + // Rank 2 + { + auto sub_dec = Kokkos::subview(a_decreasing, Kokkos::ALL, Kokkos::ALL, 0, 0, + 0, 0, 0, 0); + EXPECT_TRUE(view_fill_test(space, sub_dec, magic)); + EXPECT_EQ(compute_overall_sum(space, a_decreasing), sub_dec.size() * magic); + + auto sub_inc = Kokkos::subview(a_increasing, Kokkos::ALL, Kokkos::ALL, 0, 0, + 0, 0, 0, 0); + EXPECT_TRUE(view_fill_test(space, sub_inc, magic)); + EXPECT_EQ(compute_overall_sum(space, a_increasing), sub_inc.size() * magic); + } + reset_view(space, a_decreasing, 0); + reset_view(space, a_increasing, 0); + space.fence(); + + // Rank 3 + { + auto sub_dec = Kokkos::subview(a_decreasing, Kokkos::ALL, Kokkos::ALL, + Kokkos::ALL, 0, 0, 0, 0, 0); + EXPECT_TRUE(view_fill_test(space, sub_dec, magic)); + EXPECT_EQ( + compute_overall_sum(space, a_decreasing), + sub_dec.extent(0) * sub_dec.extent(1) * sub_dec.extent(2) * magic); + + auto sub_inc = Kokkos::subview(a_increasing, Kokkos::ALL, Kokkos::ALL, + Kokkos::ALL, 0, 0, 0, 0, 0); + EXPECT_TRUE(view_fill_test(space, sub_inc, magic)); + EXPECT_EQ(compute_overall_sum(space, a_increasing), sub_inc.size() * magic); + } + reset_view(space, a_decreasing, 0); + reset_view(space, a_increasing, 0); + space.fence(); + + // Rank 4 + { + auto sub_dec = Kokkos::subview(a_decreasing, Kokkos::ALL, Kokkos::ALL, + Kokkos::ALL, Kokkos::ALL, 0, 0, 0, 0); + EXPECT_TRUE(view_fill_test(space, sub_dec, magic)); + EXPECT_EQ(compute_overall_sum(space, a_decreasing), + sub_dec.extent(0) * sub_dec.extent(1) * sub_dec.extent(2) * + sub_dec.extent(3) * magic); + + auto sub_inc = Kokkos::subview(a_increasing, Kokkos::ALL, Kokkos::ALL, + Kokkos::ALL, Kokkos::ALL, 0, 0, 0, 0); + EXPECT_TRUE(view_fill_test(space, sub_inc, magic)); + EXPECT_EQ(compute_overall_sum(space, a_increasing), sub_inc.size() * magic); + } + reset_view(space, a_decreasing, 0); + reset_view(space, a_increasing, 0); + space.fence(); + + // Rank 5 + { + auto sub_dec = + Kokkos::subview(a_decreasing, Kokkos::ALL, Kokkos::ALL, Kokkos::ALL, + Kokkos::ALL, Kokkos::ALL, 0, 0, 0); + EXPECT_TRUE(view_fill_test(space, sub_dec, magic)); + EXPECT_EQ(compute_overall_sum(space, a_decreasing), sub_dec.size() * magic); + + auto sub_inc = + Kokkos::subview(a_increasing, Kokkos::ALL, Kokkos::ALL, Kokkos::ALL, + Kokkos::ALL, Kokkos::ALL, 0, 0, 0); + EXPECT_TRUE(view_fill_test(space, sub_inc, magic)); + EXPECT_EQ(compute_overall_sum(space, a_increasing), sub_inc.size() * magic); + } + reset_view(space, a_decreasing, 0); + reset_view(space, a_increasing, 0); + space.fence(); + + // Rank 6 + { + auto sub_dec = + Kokkos::subview(a_decreasing, Kokkos::ALL, Kokkos::ALL, Kokkos::ALL, + Kokkos::ALL, Kokkos::ALL, Kokkos::ALL, 0, 0); + EXPECT_TRUE(view_fill_test(space, sub_dec, magic)); + EXPECT_EQ(compute_overall_sum(space, a_decreasing), sub_dec.size() * magic); + + auto sub_inc = + Kokkos::subview(a_increasing, Kokkos::ALL, Kokkos::ALL, Kokkos::ALL, + Kokkos::ALL, Kokkos::ALL, Kokkos::ALL, 0, 0); + EXPECT_TRUE(view_fill_test(space, sub_inc, magic)); + EXPECT_EQ(compute_overall_sum(space, a_increasing), sub_inc.size() * magic); + } + reset_view(space, a_decreasing, 0); + reset_view(space, a_increasing, 0); + space.fence(); + + // Rank 7 + { + auto sub_dec = + Kokkos::subview(a_decreasing, Kokkos::ALL, Kokkos::ALL, Kokkos::ALL, + Kokkos::ALL, Kokkos::ALL, Kokkos::ALL, Kokkos::ALL, 0); + EXPECT_TRUE(view_fill_test(space, sub_dec, magic)); + EXPECT_EQ(compute_overall_sum(space, a_decreasing), sub_dec.size() * magic); + + auto sub_inc = + Kokkos::subview(a_increasing, Kokkos::ALL, Kokkos::ALL, Kokkos::ALL, + Kokkos::ALL, Kokkos::ALL, Kokkos::ALL, Kokkos::ALL, 0); + EXPECT_TRUE(view_fill_test(space, sub_inc, magic)); + EXPECT_EQ(compute_overall_sum(space, a_increasing), sub_inc.size() * magic); + } + reset_view(space, a_decreasing, 0); + reset_view(space, a_increasing, 0); + space.fence(); + + // Rank 8 + { + auto sub_dec = Kokkos::subview( + a_decreasing, Kokkos::ALL, Kokkos::ALL, Kokkos::ALL, Kokkos::ALL, + Kokkos::ALL, Kokkos::ALL, Kokkos::ALL, std::make_pair(0, 2)); + EXPECT_TRUE(view_fill_test(space, sub_dec, magic)); + EXPECT_EQ(compute_overall_sum(space, a_decreasing), sub_dec.size() * magic); + + auto sub_inc = Kokkos::subview( + a_increasing, Kokkos::ALL, Kokkos::ALL, Kokkos::ALL, Kokkos::ALL, + Kokkos::ALL, Kokkos::ALL, Kokkos::ALL, std::make_pair(0, 2)); + EXPECT_TRUE(view_fill_test(space, sub_inc, magic)); + EXPECT_EQ(compute_overall_sum(space, a_increasing), sub_inc.size() * magic); + } +} + +TEST(TEST_CATEGORY, view_fill_tests_layout_right) { + using Space = TEST_EXECSPACE; + using Layout = Kokkos::LayoutRight; + run_test(); +} + +TEST(TEST_CATEGORY, view_fill_tests_layout_left) { + using Space = TEST_EXECSPACE; + using Layout = Kokkos::LayoutLeft; + run_test(); +} + +} // namespace diff --git a/lib/kokkos/core/unit_test/TestViewLayoutTiled.hpp b/lib/kokkos/core/unit_test/TestViewLayoutTiled.hpp deleted file mode 100644 index 67308212ee..0000000000 --- a/lib/kokkos/core/unit_test/TestViewLayoutTiled.hpp +++ /dev/null @@ -1,1756 +0,0 @@ -//@HEADER -// ************************************************************************ -// -// Kokkos v. 4.0 -// Copyright (2022) National Technology & Engineering -// Solutions of Sandia, LLC (NTESS). -// -// Under the terms of Contract DE-NA0003525 with NTESS, -// the U.S. Government retains certain rights in this software. -// -// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. -// See https://kokkos.org/LICENSE for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//@HEADER - -#ifndef KOKKOS_IMPL_PUBLIC_INCLUDE -#define KOKKOS_IMPL_PUBLIC_INCLUDE -#endif - -#include - -#include - -#include -#include - -#include -#include - -namespace Test { - -namespace { - -template -struct TestViewLayoutTiled { - using Scalar = double; - - static constexpr int T0 = 2; - static constexpr int T1 = 4; - static constexpr int T2 = 4; - static constexpr int T3 = 2; - static constexpr int T4 = 2; - static constexpr int T5 = 2; - static constexpr int T6 = 2; - static constexpr int T7 = 2; - - // Rank 2 - using LayoutLL_2D_2x4 = - Kokkos::Experimental::LayoutTiled; - using LayoutRL_2D_2x4 = - Kokkos::Experimental::LayoutTiled; - using LayoutLR_2D_2x4 = - Kokkos::Experimental::LayoutTiled; - using LayoutRR_2D_2x4 = - Kokkos::Experimental::LayoutTiled; - - // Rank 3 - using LayoutLL_3D_2x4x4 = - Kokkos::Experimental::LayoutTiled; - using LayoutRL_3D_2x4x4 = - Kokkos::Experimental::LayoutTiled; - using LayoutLR_3D_2x4x4 = - Kokkos::Experimental::LayoutTiled; - using LayoutRR_3D_2x4x4 = - Kokkos::Experimental::LayoutTiled; - - // Rank 4 - using LayoutLL_4D_2x4x4x2 = - Kokkos::Experimental::LayoutTiled; - using LayoutRL_4D_2x4x4x2 = - Kokkos::Experimental::LayoutTiled; - using LayoutLR_4D_2x4x4x2 = - Kokkos::Experimental::LayoutTiled; - using LayoutRR_4D_2x4x4x2 = - Kokkos::Experimental::LayoutTiled; - -#if !defined(KOKKOS_ENABLE_CXX11_DISPATCH_LAMBDA) - static void test_view_layout_tiled_2d(const int, const int) { -#else - static void test_view_layout_tiled_2d(const int N0, const int N1) { - const int FT = T0 * T1; - - const int NT0 = int(std::ceil(N0 / T0)); - const int NT1 = int(std::ceil(N1 / T1)); - // Test create_mirror_view, deep_copy - // Create LL View - { - using ViewType = - typename Kokkos::View; - ViewType v("v", N0, N1); - - typename ViewType::HostMirror hv = Kokkos::create_mirror_view(v); - - // Initialize host-view - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - hv(ti * T0 + i, tj * T1 + j) = - (ti + tj * NT0) * FT + (i + j * T0); - } - } - } - } - - // copy to device - Kokkos::deep_copy(v, hv); - - Kokkos::MDRangePolicy< - Kokkos::Rank<2, Kokkos::Iterate::Left, Kokkos::Iterate::Left>, - ExecSpace> - mdrangepolicy({0, 0}, {NT0, NT1}, {T0, T1}); - - // iterate by tile - Kokkos::parallel_for( - "ViewTile rank 2 LL", mdrangepolicy, - KOKKOS_LAMBDA(const int ti, const int tj) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - if ((ti * T0 + i < N0) && (tj * T1 + j < N1)) { - v(ti * T0 + i, tj * T1 + j) += 1; - } - } - } - }); - - Kokkos::deep_copy(hv, v); - - long counter_subview = 0; - long counter_inc = 0; - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - auto tile_subview = Kokkos::tile_subview(hv, ti, tj); - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - if (tile_subview(i, j) != hv(ti * T0 + i, tj * T1 + j)) { - ++counter_subview; - } - if (tile_subview(i, j) != - ((ti + tj * NT0) * FT + (i + j * T0) + 1)) { - ++counter_inc; - } - } - } - } - } - ASSERT_EQ(counter_subview, long(0)); - ASSERT_EQ(counter_inc, long(0)); - } - - // Create RL View - { - using ViewType = - typename Kokkos::View; - Kokkos::View v("v", N0, N1); - - typename ViewType::HostMirror hv = Kokkos::create_mirror_view(v); - - // Initialize host-view - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - hv(ti * T0 + i, tj * T1 + j) = - (ti * NT1 + tj) * FT + (i + j * T0); - } - } - } - } - - // copy to device - Kokkos::deep_copy(v, hv); - - Kokkos::MDRangePolicy< - Kokkos::Rank<2, Kokkos::Iterate::Right, Kokkos::Iterate::Left>, - ExecSpace> - mdrangepolicy({0, 0}, {NT0, NT1}, {T0, T1}); - - // iterate by tile - Kokkos::parallel_for( - "ViewTile rank 2 RL", mdrangepolicy, - KOKKOS_LAMBDA(const int ti, const int tj) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - if ((ti * T0 + i < N0) && (tj * T1 + j < N1)) { - v(ti * T0 + i, tj * T1 + j) += 1; - } - } - } - }); - - Kokkos::deep_copy(hv, v); - - long counter_subview = 0; - long counter_inc = 0; - - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - auto tile_subview = Kokkos::tile_subview(hv, ti, tj); - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - if (tile_subview(i, j) != hv(ti * T0 + i, tj * T1 + j)) { - ++counter_subview; - } - if (tile_subview(i, j) != - ((ti * NT1 + tj) * FT + (i + j * T0) + 1)) { - ++counter_inc; - } - } - } - } - } - ASSERT_EQ(counter_subview, long(0)); - ASSERT_EQ(counter_inc, long(0)); - } // end scope - - // Create LR View - { - using ViewType = - typename Kokkos::View; - Kokkos::View v("v", N0, N1); - - typename ViewType::HostMirror hv = Kokkos::create_mirror_view(v); - - // Initialize host-view - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - hv(ti * T0 + i, tj * T1 + j) = - (ti + tj * NT0) * FT + (i * T1 + j); - } - } - } - } - - // copy to device - Kokkos::deep_copy(v, hv); - - Kokkos::MDRangePolicy< - Kokkos::Rank<2, Kokkos::Iterate::Left, Kokkos::Iterate::Right>, - ExecSpace> - mdrangepolicy({0, 0}, {NT0, NT1}, {T0, T1}); - - // iterate by tile - Kokkos::parallel_for( - "ViewTile rank 2 LR", mdrangepolicy, - KOKKOS_LAMBDA(const int ti, const int tj) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - if ((ti * T0 + i < N0) && (tj * T1 + j < N1)) { - v(ti * T0 + i, tj * T1 + j) += 1; - } - } - } - }); - - Kokkos::deep_copy(hv, v); - - long counter_subview = 0; - long counter_inc = 0; - - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - auto tile_subview = Kokkos::tile_subview(hv, ti, tj); - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - if (tile_subview(i, j) != hv(ti * T0 + i, tj * T1 + j)) { - ++counter_subview; - } - if (tile_subview(i, j) != - ((ti + tj * NT0) * FT + (i * T1 + j) + 1)) { - ++counter_inc; - } - } - } - } - } - ASSERT_EQ(counter_subview, long(0)); - ASSERT_EQ(counter_inc, long(0)); - } // end scope - - // Create RR View - { - using ViewType = - typename Kokkos::View; - Kokkos::View v("v", N0, N1); - - typename ViewType::HostMirror hv = Kokkos::create_mirror_view(v); - - // Initialize host-view - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - hv(ti * T0 + i, tj * T1 + j) = - (ti * NT1 + tj) * FT + (i * T1 + j); - } - } - } - } - - // copy to device - Kokkos::deep_copy(v, hv); - - Kokkos::MDRangePolicy< - Kokkos::Rank<2, Kokkos::Iterate::Left, Kokkos::Iterate::Right>, - ExecSpace> - mdrangepolicy({0, 0}, {NT0, NT1}, {T0, T1}); - - // iterate by tile - Kokkos::parallel_for( - "ViewTile rank 2 LR", mdrangepolicy, - KOKKOS_LAMBDA(const int ti, const int tj) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - if ((ti * T0 + i < N0) && (tj * T1 + j < N1)) { - v(ti * T0 + i, tj * T1 + j) += 1; - } - } - } - }); - - Kokkos::deep_copy(hv, v); - - long counter_subview = 0; - long counter_inc = 0; - - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - auto tile_subview = Kokkos::tile_subview(hv, ti, tj); - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - if (tile_subview(i, j) != hv(ti * T0 + i, tj * T1 + j)) { - ++counter_subview; - } - if (tile_subview(i, j) != - ((ti * NT1 + tj) * FT + (i * T1 + j) + 1)) { - ++counter_inc; - } - } - } - } - } - ASSERT_EQ(counter_subview, long(0)); - ASSERT_EQ(counter_inc, long(0)); - } // end scope -#endif - } // end test_view_layout_tiled_2d - -#if !defined(KOKKOS_ENABLE_CXX11_DISPATCH_LAMBDA) - static void test_view_layout_tiled_3d(const int, const int, const int) { -#else - static void test_view_layout_tiled_3d(const int N0, const int N1, - const int N2) { - const int FT = T0 * T1 * T2; - - const int NT0 = int(std::ceil(N0 / T0)); - const int NT1 = int(std::ceil(N1 / T1)); - const int NT2 = int(std::ceil(N2 / T2)); - - // Create LL View - { - using ViewType = Kokkos::View; - Kokkos::View dv("dv", N0, N1, - N2); - - typename ViewType::HostMirror v = Kokkos::create_mirror_view(dv); - - // Initialize on host - for (int tk = 0; tk < NT2; ++tk) { - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - for (int k = 0; k < T2; ++k) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - v(ti * T0 + i, tj * T1 + j, tk * T2 + k) = - (ti + tj * NT0 + tk * N0 * N1) * FT + - (i + j * T0 + k * T0 * T1); - } - } - } - } - } - } - - // copy to device - Kokkos::deep_copy(dv, v); - - Kokkos::MDRangePolicy< - Kokkos::Rank<3, Kokkos::Iterate::Left, Kokkos::Iterate::Left>, - ExecSpace> - mdrangepolicy({0, 0, 0}, {N0, N1, N2}, {T0, T1, T2}); - - // iterate by tile - Kokkos::parallel_for( - "ViewTile rank 3 LL", mdrangepolicy, - KOKKOS_LAMBDA(const int i, const int j, const int k) { - dv(i, j, k) += 1; - }); - - Kokkos::deep_copy(v, dv); - - long counter_subview = 0; - long counter_inc = 0; - - for (int tk = 0; tk < NT2; ++tk) { - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - auto tile_subview = Kokkos::tile_subview(v, ti, tj, tk); - for (int k = 0; k < T2; ++k) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - if (tile_subview(i, j, k) != - v(ti * T0 + i, tj * T1 + j, tk * T2 + k)) { - ++counter_subview; - } - if (tile_subview(i, j, k) != - ((ti + tj * NT0 + tk * N0 * N1) * FT + - (i + j * T0 + k * T0 * T1) + 1)) { - ++counter_inc; - } - } - } - } - } - } - } - ASSERT_EQ(counter_subview, long(0)); - ASSERT_EQ(counter_inc, long(0)); - } // end scope - - // Create RL View - { - using ViewType = Kokkos::View; - Kokkos::View dv("dv", N0, N1, - N2); - - typename ViewType::HostMirror v = Kokkos::create_mirror_view(dv); - - // Initialize on host - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - for (int tk = 0; tk < NT2; ++tk) { - for (int k = 0; k < T2; ++k) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - v(ti * T0 + i, tj * T1 + j, tk * T2 + k) = - (ti * NT1 * NT2 + tj * NT2 + tk) * FT + - (i + j * T0 + k * T0 * T1); - } - } - } - } - } - } - - // copy to device - Kokkos::deep_copy(dv, v); - - Kokkos::MDRangePolicy< - Kokkos::Rank<3, Kokkos::Iterate::Right, Kokkos::Iterate::Left>, - ExecSpace> - mdrangepolicy({0, 0, 0}, {N0, N1, N2}, {T0, T1, T2}); - - // iterate by tile - Kokkos::parallel_for( - "ViewTile rank 3 RL", mdrangepolicy, - KOKKOS_LAMBDA(const int i, const int j, const int k) { - dv(i, j, k) += 1; - }); - - Kokkos::deep_copy(v, dv); - - long counter_subview = 0; - long counter_inc = 0; - - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - for (int tk = 0; tk < NT2; ++tk) { - auto tile_subview = Kokkos::tile_subview(v, ti, tj, tk); - for (int k = 0; k < T2; ++k) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - if (tile_subview(i, j, k) != - v(ti * T0 + i, tj * T1 + j, tk * T2 + k)) { - ++counter_subview; - } - if (tile_subview(i, j, k) != - ((ti * NT1 * NT2 + tj * NT2 + tk) * FT + - (i + j * T0 + k * T0 * T1) + 1)) { - ++counter_inc; - } - } - } - } - } - } - } - ASSERT_EQ(counter_subview, long(0)); - ASSERT_EQ(counter_inc, long(0)); - } // end scope - - // Create LR View - { - using ViewType = Kokkos::View; - Kokkos::View dv("dv", N0, N1, - N2); - - typename ViewType::HostMirror v = Kokkos::create_mirror_view(dv); - - // Initialize on host - for (int tk = 0; tk < NT2; ++tk) { - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - for (int k = 0; k < T2; ++k) { - v(ti * T0 + i, tj * T1 + j, tk * T2 + k) = - (ti + tj * NT0 + tk * NT0 * NT1) * FT + - (i * T1 * T2 + j * T2 + k); - } - } - } - } - } - } - - // copy to device - Kokkos::deep_copy(dv, v); - - Kokkos::MDRangePolicy< - Kokkos::Rank<3, Kokkos::Iterate::Left, Kokkos::Iterate::Right>, - ExecSpace> - mdrangepolicy({0, 0, 0}, {N0, N1, N2}, {T0, T1, T2}); - - // iterate by tile - Kokkos::parallel_for( - "ViewTile rank 3 LR", mdrangepolicy, - KOKKOS_LAMBDA(const int i, const int j, const int k) { - dv(i, j, k) += 1; - }); - - Kokkos::deep_copy(v, dv); - - long counter_subview = 0; - long counter_inc = 0; - - for (int tk = 0; tk < NT2; ++tk) { - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - auto tile_subview = Kokkos::tile_subview(v, ti, tj, tk); - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - for (int k = 0; k < T2; ++k) { - if (tile_subview(i, j, k) != - v(ti * T0 + i, tj * T1 + j, tk * T2 + k)) { - ++counter_subview; - } - if (tile_subview(i, j, k) != - ((ti + tj * NT0 + tk * NT0 * NT1) * FT + - (i * T1 * T2 + j * T2 + k) + 1)) { - ++counter_inc; - } - } - } - } - } - } - } - ASSERT_EQ(counter_subview, long(0)); - ASSERT_EQ(counter_inc, long(0)); - } // end scope - - // Create RR View - { - using ViewType = Kokkos::View; - Kokkos::View dv("dv", N0, N1, - N2); - - typename ViewType::HostMirror v = Kokkos::create_mirror_view(dv); - - // Initialize on host - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - for (int tk = 0; tk < NT2; ++tk) { - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - for (int k = 0; k < T2; ++k) { - v(ti * T0 + i, tj * T1 + j, tk * T2 + k) = - (ti * NT1 * NT2 + tj * NT2 + tk) * FT + - (i * T1 * T2 + j * T2 + k); - } - } - } - } - } - } - - // copy to device - Kokkos::deep_copy(dv, v); - - Kokkos::MDRangePolicy< - Kokkos::Rank<3, Kokkos::Iterate::Right, Kokkos::Iterate::Right>, - ExecSpace> - mdrangepolicy({0, 0, 0}, {N0, N1, N2}, {T0, T1, T2}); - - // iterate by tile - Kokkos::parallel_for( - "ViewTile rank 3 RR", mdrangepolicy, - KOKKOS_LAMBDA(const int i, const int j, const int k) { - dv(i, j, k) += 1; - }); - - Kokkos::deep_copy(v, dv); - - long counter_subview = 0; - long counter_inc = 0; - - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - for (int tk = 0; tk < NT2; ++tk) { - auto tile_subview = Kokkos::tile_subview(v, ti, tj, tk); - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - for (int k = 0; k < T2; ++k) { - if (tile_subview(i, j, k) != - v(ti * T0 + i, tj * T1 + j, tk * T2 + k)) { - ++counter_subview; - } - if (tile_subview(i, j, k) != - ((ti * NT1 * NT2 + tj * NT2 + tk) * FT + - (i * T1 * T2 + j * T2 + k) + 1)) { - ++counter_inc; - } - } - } - } - } - } - } - ASSERT_EQ(counter_subview, long(0)); - ASSERT_EQ(counter_inc, long(0)); - } // end scope -#endif - } // end test_view_layout_tiled_3d - -#if !defined(KOKKOS_ENABLE_CXX11_DISPATCH_LAMBDA) - static void test_view_layout_tiled_4d(const int, const int, const int, - const int){ -#else - static void test_view_layout_tiled_4d(const int N0, const int N1, - const int N2, const int N3) { - const int FT = T0 * T1 * T2 * T3; - - const int NT0 = int(std::ceil(N0 / T0)); - const int NT1 = int(std::ceil(N1 / T1)); - const int NT2 = int(std::ceil(N2 / T2)); - const int NT3 = int(std::ceil(N3 / T3)); - - // Create LL View - { - using ViewType = Kokkos::View; - Kokkos::View dv("dv", N0, N1, - N2, N3); - - typename ViewType::HostMirror v = Kokkos::create_mirror_view(dv); - - // Initialize on host - for (int tl = 0; tl < NT3; ++tl) { - for (int tk = 0; tk < NT2; ++tk) { - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - for (int l = 0; l < T3; ++l) { - for (int k = 0; k < T2; ++k) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - v(ti * T0 + i, tj * T1 + j, tk * T2 + k, tl * T3 + l) = - (ti + tj * NT0 + tk * N0 * N1 + tl * N0 * N1 * N2) * - FT + - (i + j * T0 + k * T0 * T1 + l * T0 * T1 * T2); - } - } - } - } - } - } - } - } - - // copy to device - Kokkos::deep_copy(dv, v); - - Kokkos::MDRangePolicy< - Kokkos::Rank<4, Kokkos::Iterate::Left, Kokkos::Iterate::Left>, - ExecSpace> - mdrangepolicy({0, 0, 0, 0}, {N0, N1, N2, N3}, {T0, T1, T2, T3}); - - // iterate by tile - Kokkos::parallel_for( - "ViewTile rank 4 LL", mdrangepolicy, - KOKKOS_LAMBDA(const int i, const int j, const int k, const int l) { - dv(i, j, k, l) += 1; - }); - - Kokkos::deep_copy(v, dv); - - long counter_subview = 0; - long counter_inc = 0; - - for (int tl = 0; tl < NT3; ++tl) { - for (int tk = 0; tk < NT2; ++tk) { - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - auto tile_subview = Kokkos::tile_subview(v, ti, tj, tk, tl); - for (int l = 0; l < T3; ++l) { - for (int k = 0; k < T2; ++k) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - if (tile_subview(i, j, k, l) != - v(ti * T0 + i, tj * T1 + j, tk * T2 + k, - tl * T3 + l)) { - ++counter_subview; - } - if (tile_subview(i, j, k, l) != - ((ti + tj * NT0 + tk * N0 * N1 + tl * N0 * N1 * N2) * - FT + - (i + j * T0 + k * T0 * T1 + l * T0 * T1 * T2) + 1)) { - ++counter_inc; - } - } - } - } - } - } - } - } - } - ASSERT_EQ(counter_subview, long(0)); - ASSERT_EQ(counter_inc, long(0)); - } // end scope - - // Create RL View - { - using ViewType = Kokkos::View; - Kokkos::View dv("dv", N0, N1, - N2, N3); - - typename ViewType::HostMirror v = Kokkos::create_mirror_view(dv); - - // Initialize on host - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - for (int tk = 0; tk < NT2; ++tk) { - for (int tl = 0; tl < NT3; ++tl) { - for (int l = 0; l < T3; ++l) { - for (int k = 0; k < T2; ++k) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - v(ti * T0 + i, tj * T1 + j, tk * T2 + k, tl * T3 + l) = - (ti * NT1 * NT2 * N3 + tj * NT2 * N3 + tk * N3 + tl) * - FT + - (i + j * T0 + k * T0 * T1 + l * T0 * T1 * T2); - } - } - } - } - } - } - } - } - - // copy to device - Kokkos::deep_copy(dv, v); - - Kokkos::MDRangePolicy< - Kokkos::Rank<4, Kokkos::Iterate::Right, Kokkos::Iterate::Left>, - ExecSpace> - mdrangepolicy({0, 0, 0, 0}, {N0, N1, N2, N3}, {T0, T1, T2, T3}); - - // iterate by tile - Kokkos::parallel_for( - "ViewTile rank 4 RL", mdrangepolicy, - KOKKOS_LAMBDA(const int i, const int j, const int k, const int l) { - dv(i, j, k, l) += 1; - }); - - Kokkos::deep_copy(v, dv); - - long counter_subview = 0; - long counter_inc = 0; - - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - for (int tk = 0; tk < NT2; ++tk) { - for (int tl = 0; tl < NT3; ++tl) { - auto tile_subview = Kokkos::tile_subview(v, ti, tj, tk, tl); - for (int l = 0; l < T3; ++l) { - for (int k = 0; k < T2; ++k) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - if (tile_subview(i, j, k, l) != - v(ti * T0 + i, tj * T1 + j, tk * T2 + k, - tl * T3 + l)) { - ++counter_subview; - } - if (tile_subview(i, j, k, l) != - ((ti * NT1 * NT2 * N3 + tj * NT2 * N3 + tk * N3 + - tl) * - FT + - (i + j * T0 + k * T0 * T1 + l * T0 * T1 * T2) + 1)) { - ++counter_inc; - } - } - } - } - } - } - } - } - } - ASSERT_EQ(counter_subview, long(0)); - ASSERT_EQ(counter_inc, long(0)); - } // end scope - - // Create LR View - { - using ViewType = Kokkos::View; - Kokkos::View dv("dv", N0, N1, - N2, N3); - - typename ViewType::HostMirror v = Kokkos::create_mirror_view(dv); - - // Initialize on host - for (int tl = 0; tl < NT3; ++tl) { - for (int tk = 0; tk < NT2; ++tk) { - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - for (int k = 0; k < T2; ++k) { - for (int l = 0; l < T3; ++l) { - v(ti * T0 + i, tj * T1 + j, tk * T2 + k, tl * T3 + l) = - (ti + tj * NT0 + tk * NT0 * NT1 + - tl * NT0 * NT1 * NT2) * - FT + - (i * T1 * T2 * T3 + j * T2 * T3 + k * T3 + l); - } - } - } - } - } - } - } - } - - // copy to device - Kokkos::deep_copy(dv, v); - - Kokkos::MDRangePolicy< - Kokkos::Rank<4, Kokkos::Iterate::Left, Kokkos::Iterate::Right>, - ExecSpace> - mdrangepolicy({0, 0, 0, 0}, {N0, N1, N2, N3}, {T0, T1, T2, T3}); - - // iterate by tile - Kokkos::parallel_for( - "ViewTile rank 4 LR", mdrangepolicy, - KOKKOS_LAMBDA(const int i, const int j, const int k, const int l) { - dv(i, j, k, l) += 1; - }); - - Kokkos::deep_copy(v, dv); - - long counter_subview = 0; - long counter_inc = 0; - - for (int tl = 0; tl < NT3; ++tl) { - for (int tk = 0; tk < NT2; ++tk) { - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - auto tile_subview = Kokkos::tile_subview(v, ti, tj, tk, tl); - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - for (int k = 0; k < T2; ++k) { - for (int l = 0; l < T3; ++l) { - if (tile_subview(i, j, k, l) != - v(ti * T0 + i, tj * T1 + j, tk * T2 + k, - tl * T3 + l)) { - ++counter_subview; - } - if (tile_subview(i, j, k, l) != - ((ti + tj * NT0 + tk * NT0 * NT1 + - tl * NT0 * NT1 * NT2) * - FT + - (i * T1 * T2 * T3 + j * T2 * T3 + k * T3 + l) + 1)) { - ++counter_inc; - } - } - } - } - } - } - } - } - } - ASSERT_EQ(counter_subview, long(0)); - ASSERT_EQ(counter_inc, long(0)); - } // end scope - - // Create RR View - { - using ViewType = Kokkos::View; - Kokkos::View dv("dv", N0, N1, - N2, N3); - - typename ViewType::HostMirror v = Kokkos::create_mirror_view(dv); - - // Initialize on host - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - for (int tk = 0; tk < NT2; ++tk) { - for (int tl = 0; tl < NT3; ++tl) { - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - for (int k = 0; k < T2; ++k) { - for (int l = 0; l < T3; ++l) { - v(ti * T0 + i, tj * T1 + j, tk * T2 + k, tl * T3 + l) = - (ti * NT1 * NT2 * NT3 + tj * NT2 * NT3 + tk * NT3 + - tl) * - FT + - (i * T1 * T2 * T3 + j * T2 * T3 + k * T3 + l); - } - } - } - } - } - } - } - } - - // copy to device - Kokkos::deep_copy(dv, v); - - Kokkos::MDRangePolicy< - Kokkos::Rank<4, Kokkos::Iterate::Right, Kokkos::Iterate::Right>, - ExecSpace> - mdrangepolicy({0, 0, 0, 0}, {N0, N1, N2, N3}, {T0, T1, T2, T3}); - - // iterate by tile - Kokkos::parallel_for( - "ViewTile rank 4 RR", mdrangepolicy, - KOKKOS_LAMBDA(const int i, const int j, const int k, const int l) { - dv(i, j, k, l) += 1; - }); - - Kokkos::deep_copy(v, dv); - - long counter_subview = 0; - long counter_inc = 0; - - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - for (int tk = 0; tk < NT2; ++tk) { - for (int tl = 0; tl < NT3; ++tl) { - auto tile_subview = Kokkos::tile_subview(v, ti, tj, tk, tl); - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - for (int k = 0; k < T2; ++k) { - for (int l = 0; l < T3; ++l) { - if (tile_subview(i, j, k, l) != - v(ti * T0 + i, tj * T1 + j, tk * T2 + k, - tl * T3 + l)) { - ++counter_subview; - } - if (tile_subview(i, j, k, l) != - ((ti * NT1 * NT2 * NT3 + tj * NT2 * NT3 + tk * NT3 + - tl) * - FT + - (i * T1 * T2 * T3 + j * T2 * T3 + k * T3 + l) + 1)) { - ++counter_inc; - } - } - } - } - } - } - } - } - } - ASSERT_EQ(counter_subview, long(0)); - ASSERT_EQ(counter_inc, long(0)); - } // end scope -#endif - } // end test_view_layout_tiled_4d - - static void test_view_layout_tiled_subtile_2d(const int N0, const int N1) { - const int FT = T0 * T1; - - const int NT0 = int(std::ceil(N0 / T0)); - const int NT1 = int(std::ceil(N1 / T1)); - - // Counter to check for errors at the end - long counter[4] = {0}; - - // Create LL View - { - Kokkos::View v("v", N0, N1); - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - v(ti * T0 + i, tj * T1 + j) = (ti + tj * NT0) * FT + (i + j * T0); - } - } - } - } - - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - auto tile_subview = Kokkos::tile_subview(v, ti, tj); - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - if (tile_subview(i, j) != v(ti * T0 + i, tj * T1 + j)) { - ++counter[0]; - } -#ifdef KOKKOS_VERBOSE_LAYOUTTILED_OUTPUT - std::cout << "idx0,idx1 = " << ti * T0 + i << "," << tj * T1 + j - << std::endl; - std::cout << "ti,tj,i,j: " << ti << "," << tj << "," << i << "," - << j << " v = " << v(ti * T0 + i, tj * T1 + j) - << " flat idx = " - << (ti + tj * NT0) * FT + (i + j * T0) << std::endl; - std::cout << "subview_tile output = " << tile_subview(i, j) - << std::endl; -#endif - } - } - } - } - } // end scope - - // Create RL View - { - Kokkos::View v("v", N0, N1); - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - v(ti * T0 + i, tj * T1 + j) = (ti * NT1 + tj) * FT + (i + j * T0); - } - } - } - } - - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - auto tile_subview = Kokkos::tile_subview(v, ti, tj); - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - if (tile_subview(i, j) != v(ti * T0 + i, tj * T1 + j)) { - ++counter[1]; - } -#ifdef KOKKOS_VERBOSE_LAYOUTTILED_OUTPUT - std::cout << "idx0,idx1 = " << ti * T0 + i << "," << tj * T1 + j - << std::endl; - std::cout << "ti,tj,i,j: " << ti << "," << tj << "," << i << "," - << j << " v = " << v(ti * T0 + i, tj * T1 + j) - << " flat idx = " - << (ti * NT1 + tj) * FT + (i + j * T0) << std::endl; - std::cout << "subview_tile output = " << tile_subview(i, j) - << std::endl; -#endif - } - } - } - } - } // end scope - - // Create LR View - { - Kokkos::View v("v", N0, N1); - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - v(ti * T0 + i, tj * T1 + j) = (ti + tj * NT0) * FT + (i * T1 + j); - } - } - } - } - - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - auto tile_subview = Kokkos::tile_subview(v, ti, tj); - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - if (tile_subview(i, j) != v(ti * T0 + i, tj * T1 + j)) { - ++counter[2]; - } -#ifdef KOKKOS_VERBOSE_LAYOUTTILED_OUTPUT - std::cout << "idx0,idx1 = " << ti * T0 + i << "," << tj * T1 + j - << std::endl; - std::cout << "ti,tj,i,j: " << ti << "," << tj << "," << i << "," - << j << " v = " << v(ti * T0 + i, tj * T1 + j) - << " flat idx = " - << (ti + tj * NT0) * FT + (i * T1 + j) << std::endl; - std::cout << "subview_tile output = " << tile_subview(i, j) - << std::endl; -#endif - } - } - } - } - } // end scope - - // Create RR View - { - Kokkos::View v("v", N0, N1); - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - v(ti * T0 + i, tj * T1 + j) = (ti * NT1 + tj) * FT + (i * T1 + j); - } - } - } - } - - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - auto tile_subview = Kokkos::tile_subview(v, ti, tj); - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - if (tile_subview(i, j) != v(ti * T0 + i, tj * T1 + j)) { - ++counter[3]; - } -#ifdef KOKKOS_VERBOSE_LAYOUTTILED_OUTPUT - std::cout << "idx0,idx1 = " << ti * T0 + i << "," << tj * T1 + j - << std::endl; - std::cout << "ti,tj,i,j: " << ti << "," << tj << "," << i << "," - << j << " v = " << v(ti * T0 + i, tj * T1 + j) - << " flat idx = " - << (ti * NT1 + tj) * FT + (i * T1 + j) << std::endl; - std::cout << "subview_tile output = " << tile_subview(i, j) - << std::endl; - std::cout << "subview tile rank = " << Kokkos::rank(tile_subview) - << std::endl; -#endif - } - } - } - } - } // end scope - -#ifdef KOKKOS_VERBOSE_LAYOUTTILED_OUTPUT - std::cout << "subview_tile vs view errors:\n" - << " LL: " << counter[0] << " RL: " << counter[1] - << " LR: " << counter[2] << " RR: " << counter[3] << std::endl; -#endif - - ASSERT_EQ(counter[0], long(0)); - ASSERT_EQ(counter[1], long(0)); - ASSERT_EQ(counter[2], long(0)); - ASSERT_EQ(counter[3], long(0)); - } // end test_view_layout_tiled_subtile_2d - - static void test_view_layout_tiled_subtile_3d(const int N0, const int N1, - const int N2) { - const int FT = T0 * T1 * T2; - - const int NT0 = int(std::ceil(N0 / T0)); - const int NT1 = int(std::ceil(N1 / T1)); - const int NT2 = int(std::ceil(N2 / T2)); - - // Counter to check for errors at the end - long counter[4] = {0}; - // Create LL View - { - Kokkos::View v("v", N0, - N1, N2); - for (int tk = 0; tk < NT2; ++tk) { - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - for (int k = 0; k < T2; ++k) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - v(ti * T0 + i, tj * T1 + j, tk * T2 + k) = - (ti + tj * NT0 + tk * N0 * N1) * FT + - (i + j * T0 + k * T0 * T1); - } - } - } - } - } - } - - for (int tk = 0; tk < NT2; ++tk) { - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - auto tile_subview = Kokkos::tile_subview(v, ti, tj, tk); - for (int k = 0; k < T2; ++k) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - if (tile_subview(i, j, k) != - v(ti * T0 + i, tj * T1 + j, tk * T2 + k)) { - ++counter[0]; - } -#ifdef KOKKOS_VERBOSE_LAYOUTTILED_OUTPUT - std::cout << "idx0,idx1,idx2 = " << ti * T0 + i << "," - << tj * T1 + j << "," << tk * T2 + k << std::endl; - std::cout - << "ti,tj,tk,i,j,k: " << ti << "," << tj << "," << tk - << "," << i << "," << j << "," << k - << " v = " << v(ti * T0 + i, tj * T1 + j, tk * T2 + k) - << " flat idx = " - << (ti + tj * NT0 + tk * N0 * N1) * FT + - (i + j * T0 + k * T0 * T1) - << std::endl; - std::cout << "subview_tile output = " << tile_subview(i, j, k) - << std::endl; - std::cout - << "subview tile rank = " << Kokkos::rank(tile_subview) - << std::endl; -#endif - } - } - } - } - } - } - } // end scope - - // Create RL View - { - Kokkos::View v("v", N0, - N1, N2); - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - for (int tk = 0; tk < NT2; ++tk) { - for (int k = 0; k < T2; ++k) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - v(ti * T0 + i, tj * T1 + j, tk * T2 + k) = - (ti * NT1 * NT2 + tj * NT2 + tk) * FT + - (i + j * T0 + k * T0 * T1); - } - } - } - } - } - } - - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - for (int tk = 0; tk < NT2; ++tk) { - auto tile_subview = Kokkos::tile_subview(v, ti, tj, tk); - for (int k = 0; k < T2; ++k) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - if (tile_subview(i, j, k) != - v(ti * T0 + i, tj * T1 + j, tk * T2 + k)) { - ++counter[1]; - } -#ifdef KOKKOS_VERBOSE_LAYOUTTILED_OUTPUT - std::cout << "idx0,idx1,idx2 = " << ti * T0 + i << "," - << tj * T1 + j << "," << tk * T2 + k << std::endl; - std::cout - << "ti,tj,tk,i,j,k: " << ti << "," << tj << "," << tk - << "," << i << "," << j << "," << k - << " v = " << v(ti * T0 + i, tj * T1 + j, tk * T2 + k) - << " flat idx = " - << (ti * NT1 * NT2 + tj * NT2 + tk) * FT + - (i + j * T0 + k * T0 * T1) - << std::endl; - std::cout << "subview_tile output = " << tile_subview(i, j, k) - << std::endl; -#endif - } - } - } - } - } - } - } // end scope - - // Create LR View - { - Kokkos::View v("v", N0, - N1, N2); - for (int tk = 0; tk < NT2; ++tk) { - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - for (int k = 0; k < T2; ++k) { - v(ti * T0 + i, tj * T1 + j, tk * T2 + k) = - (ti + tj * NT0 + tk * NT0 * NT1) * FT + - (i * T1 * T2 + j * T2 + k); - } - } - } - } - } - } - - for (int tk = 0; tk < NT2; ++tk) { - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - auto tile_subview = Kokkos::tile_subview(v, ti, tj, tk); - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - for (int k = 0; k < T2; ++k) { - if (tile_subview(i, j, k) != - v(ti * T0 + i, tj * T1 + j, tk * T2 + k)) { - ++counter[2]; - } -#ifdef KOKKOS_VERBOSE_LAYOUTTILED_OUTPUT - std::cout << "idx0,idx1,idx2 = " << ti * T0 + i << "," - << tj * T1 + j << "," << tk * T2 + k << std::endl; - std::cout - << "ti,tj,tk,i,j,k: " << ti << "," << tj << "," << tk - << "," << i << "," << j << "," << k - << " v = " << v(ti * T0 + i, tj * T1 + j, tk * T2 + k) - << " flat idx = " - << (ti + tj * NT0 + tk * NT0 * NT1) * FT + - (i * T1 * T2 + j * T2 + k) - << std::endl; - std::cout << "subview_tile output = " << tile_subview(i, j, k) - << std::endl; - std::cout - << "subview tile rank = " << Kokkos::rank(tile_subview) - << std::endl; -#endif - } - } - } - } - } - } - } // end scope - - // Create RR View - { - Kokkos::View v("v", N0, - N1, N2); - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - for (int tk = 0; tk < NT2; ++tk) { - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - for (int k = 0; k < T2; ++k) { - v(ti * T0 + i, tj * T1 + j, tk * T2 + k) = - (ti * NT1 * NT2 + tj * NT2 + tk) * FT + - (i * T1 * T2 + j * T2 + k); - } - } - } - } - } - } - - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - for (int tk = 0; tk < NT2; ++tk) { - auto tile_subview = Kokkos::tile_subview(v, ti, tj, tk); - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - for (int k = 0; k < T2; ++k) { - if (tile_subview(i, j, k) != - v(ti * T0 + i, tj * T1 + j, tk * T2 + k)) { - ++counter[3]; - } -#ifdef KOKKOS_VERBOSE_LAYOUTTILED_OUTPUT - std::cout << "idx0,idx1,idx2 = " << ti * T0 + i << "," - << tj * T1 + j << "," << tk * T2 + k << std::endl; - std::cout - << "ti,tj,tk,i,j,k: " << ti << "," << tj << "," << tk - << "," << i << "," << j << "," << k - << " v = " << v(ti * T0 + i, tj * T1 + j, tk * T2 + k) - << " flat idx = " - << (ti * NT1 * NT2 + tj * NT2 + tk) * FT + - (i * T1 * T2 + j * T2 + k) - << std::endl; - std::cout << "subview_tile output = " << tile_subview(i, j, k) - << std::endl; - std::cout - << "subview tile rank = " << Kokkos::rank(tile_subview) - << std::endl; -#endif - } - } - } - } - } - } - } // end scope - -#ifdef KOKKOS_VERBOSE_LAYOUTTILED_OUTPUT - std::cout << "subview_tile vs view errors:\n" - << " LL: " << counter[0] << " RL: " << counter[1] - << " LR: " << counter[2] << " RR: " << counter[3] << std::endl; -#endif - - ASSERT_EQ(counter[0], long(0)); - ASSERT_EQ(counter[1], long(0)); - ASSERT_EQ(counter[2], long(0)); - ASSERT_EQ(counter[3], long(0)); - - } // end test_view_layout_tiled_subtile_3d - - static void test_view_layout_tiled_subtile_4d(const int N0, const int N1, - const int N2, const int N3) { - const int FT = T0 * T1 * T2 * T3; - - const int NT0 = int(std::ceil(N0 / T0)); - const int NT1 = int(std::ceil(N1 / T1)); - const int NT2 = int(std::ceil(N2 / T2)); - const int NT3 = int(std::ceil(N3 / T3)); - - // Counter to check for errors at the end - long counter[4] = {0}; - // Create LL View - { - Kokkos::View v( - "v", N0, N1, N2, N3); - for (int tl = 0; tl < NT3; ++tl) { - for (int tk = 0; tk < NT2; ++tk) { - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - for (int l = 0; l < T3; ++l) { - for (int k = 0; k < T2; ++k) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - v(ti * T0 + i, tj * T1 + j, tk * T2 + k, tl * T3 + l) = - (ti + tj * NT0 + tk * N0 * N1 + tl * N0 * N1 * N2) * - FT + - (i + j * T0 + k * T0 * T1 + l * T0 * T1 * T2); - } - } - } - } - } - } - } - } - - for (int tl = 0; tl < NT3; ++tl) { - for (int tk = 0; tk < NT2; ++tk) { - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - auto tile_subview = Kokkos::tile_subview(v, ti, tj, tk, tl); - for (int l = 0; l < T3; ++l) { - for (int k = 0; k < T2; ++k) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - if (tile_subview(i, j, k, l) != - v(ti * T0 + i, tj * T1 + j, tk * T2 + k, - tl * T3 + l)) { - ++counter[0]; - } -#ifdef KOKKOS_VERBOSE_LAYOUTTILED_OUTPUT - std::cout << "idx0,idx1,idx2,idx3 = " << ti * T0 + i - << "," << tj * T1 + j << "," << tk * T2 + k - << "," << tl * T3 + l << std::endl; - std::cout - << "ti,tj,tk,tl: " << ti << "," << tj << "," << tk - << "," << tl << "," - << " i,j,k,l: " << i << "," << j << "," << k << "," - << l << " v = " - << v(ti * T0 + i, tj * T1 + j, tk * T2 + k, - tl * T3 + l) - << " flat idx = " - << (ti + tj * NT0 + tk * N0 * N1 + - tl * N0 * N1 * N2) * - FT + - (i + j * T0 + k * T0 * T1 + l * T0 * T1 * T2) - << std::endl; - std::cout << "subview_tile output = " - << tile_subview(i, j, k, l) << std::endl; - std::cout << "subview tile rank = " - << Kokkos::rank(tile_subview) << std::endl; -#endif - } - } - } - } - } - } - } - } - } // end scope - - // Create RL View - { - Kokkos::View v( - "v", N0, N1, N2, N3); - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - for (int tk = 0; tk < NT2; ++tk) { - for (int tl = 0; tl < NT3; ++tl) { - for (int l = 0; l < T3; ++l) { - for (int k = 0; k < T2; ++k) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - v(ti * T0 + i, tj * T1 + j, tk * T2 + k, tl * T3 + l) = - (ti * NT1 * NT2 * N3 + tj * NT2 * N3 + tk * N3 + tl) * - FT + - (i + j * T0 + k * T0 * T1 + l * T0 * T1 * T2); - } - } - } - } - } - } - } - } - - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - for (int tk = 0; tk < NT2; ++tk) { - for (int tl = 0; tl < NT3; ++tl) { - auto tile_subview = Kokkos::tile_subview(v, ti, tj, tk, tl); - for (int l = 0; l < T3; ++l) { - for (int k = 0; k < T2; ++k) { - for (int j = 0; j < T1; ++j) { - for (int i = 0; i < T0; ++i) { - if (tile_subview(i, j, k, l) != - v(ti * T0 + i, tj * T1 + j, tk * T2 + k, - tl * T3 + l)) { - ++counter[1]; - } -#ifdef KOKKOS_VERBOSE_LAYOUTTILED_OUTPUT - std::cout << "idx0,idx1,idx2,idx3 = " << ti * T0 + i - << "," << tj * T1 + j << "," << tk * T2 + k - << "," << tl * T3 + l << std::endl; - std::cout - << "ti,tj,tk,tl: " << ti << "," << tj << "," << tk - << "," << tl << "," - << " i,j,k,l: " << i << "," << j << "," << k << "," - << l << " v = " - << v(ti * T0 + i, tj * T1 + j, tk * T2 + k, - tl * T3 + l) - << " flat idx = " - << (ti * NT1 * NT2 * N3 + tj * NT2 * N3 + tk * N3 + - tl) * FT + - (i + j * T0 + k * T0 * T1 + l * T0 * T1 * T2) - << std::endl; - std::cout << "subview_tile output = " - << tile_subview(i, j, k, l) << std::endl; - std::cout << "subview tile rank = " - << Kokkos::rank(tile_subview) << std::endl; -#endif - } - } - } - } - } - } - } - } - } // end scope - - // Create LR View - { - Kokkos::View v( - "v", N0, N1, N2, N3); - for (int tl = 0; tl < NT3; ++tl) { - for (int tk = 0; tk < NT2; ++tk) { - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - for (int k = 0; k < T2; ++k) { - for (int l = 0; l < T3; ++l) { - v(ti * T0 + i, tj * T1 + j, tk * T2 + k, tl * T3 + l) = - (ti + tj * NT0 + tk * NT0 * NT1 + - tl * NT0 * NT1 * NT2) * - FT + - (i * T1 * T2 * T3 + j * T2 * T3 + k * T3 + l); - } - } - } - } - } - } - } - } - - for (int tl = 0; tl < NT3; ++tl) { - for (int tk = 0; tk < NT2; ++tk) { - for (int tj = 0; tj < NT1; ++tj) { - for (int ti = 0; ti < NT0; ++ti) { - auto tile_subview = Kokkos::tile_subview(v, ti, tj, tk, tl); - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - for (int k = 0; k < T2; ++k) { - for (int l = 0; l < T3; ++l) { - if (tile_subview(i, j, k, l) != - v(ti * T0 + i, tj * T1 + j, tk * T2 + k, - tl * T3 + l)) { - ++counter[2]; - } -#ifdef KOKKOS_VERBOSE_LAYOUTTILED_OUTPUT - std::cout << "idx0,idx1,idx2,idx3 = " << ti * T0 + i - << "," << tj * T1 + j << "," << tk * T2 + k - << "," << tl * T3 + l << std::endl; - std::cout - << "ti,tj,tk,tl: " << ti << "," << tj << "," << tk - << "," << tl << "," - << " i,j,k,l: " << i << "," << j << "," << k << "," - << l << " v = " - << v(ti * T0 + i, tj * T1 + j, tk * T2 + k, - tl * T3 + l) - << " flat idx = " - << (ti + tj * NT0 + tk * NT0 * NT1 + - tl * NT0 * NT1 * NT2) * - FT + - (i * T1 * T2 * T3 + j * T2 * T3 + k * T3 + l) - << std::endl; - std::cout << "subview_tile output = " - << tile_subview(i, j, k, l) << std::endl; - std::cout << "subview tile rank = " - << Kokkos::rank(tile_subview) << std::endl; -#endif - } - } - } - } - } - } - } - } - } // end scope - - // Create RR View - { - Kokkos::View v( - "v", N0, N1, N2, N3); - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - for (int tk = 0; tk < NT2; ++tk) { - for (int tl = 0; tl < NT3; ++tl) { - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - for (int k = 0; k < T2; ++k) { - for (int l = 0; l < T3; ++l) { - v(ti * T0 + i, tj * T1 + j, tk * T2 + k, tl * T3 + l) = - (ti * NT1 * NT2 * NT3 + tj * NT2 * NT3 + tk * NT3 + - tl) * - FT + - (i * T1 * T2 * T3 + j * T2 * T3 + k * T3 + l); - } - } - } - } - } - } - } - } - - for (int ti = 0; ti < NT0; ++ti) { - for (int tj = 0; tj < NT1; ++tj) { - for (int tk = 0; tk < NT2; ++tk) { - for (int tl = 0; tl < NT3; ++tl) { - auto tile_subview = Kokkos::tile_subview(v, ti, tj, tk, tl); - for (int i = 0; i < T0; ++i) { - for (int j = 0; j < T1; ++j) { - for (int k = 0; k < T2; ++k) { - for (int l = 0; l < T3; ++l) { - if (tile_subview(i, j, k, l) != - v(ti * T0 + i, tj * T1 + j, tk * T2 + k, - tl * T3 + l)) { - ++counter[3]; - } -#ifdef KOKKOS_VERBOSE_LAYOUTTILED_OUTPUT - std::cout << "idx0,idx1,idx2,idx3 = " << ti * T0 + i - << "," << tj * T1 + j << "," << tk * T2 + k - << "," << tl * T3 + l << std::endl; - std::cout - << "ti,tj,tk,tl: " << ti << "," << tj << "," << tk - << "," << tl << "," - << " i,j,k,l: " << i << "," << j << "," << k << "," - << l << " v = " - << v(ti * T0 + i, tj * T1 + j, tk * T2 + k, - tl * T3 + l) - << " flat idx = " - << (ti * NT1 * NT2 * NT3 + tj * NT2 * NT3 + tk * NT3 + - tl) * FT + - (i * T1 * T2 * T3 + j * T2 * T3 + k * T3 + l) - << std::endl; - std::cout << "subview_tile output = " - << tile_subview(i, j, k, l) << std::endl; - std::cout << "subview tile rank = " - << Kokkos::rank(tile_subview) << std::endl; -#endif - } - } - } - } - } - } - } - } - } // end scope - -#ifdef KOKKOS_VERBOSE_LAYOUTTILED_OUTPUT - std::cout << "subview_tile vs view errors:\n" - << " LL: " << counter[0] << " RL: " << counter[1] - << " LR: " << counter[2] << " RR: " << counter[3] << std::endl; -#endif - - ASSERT_EQ(counter[0], long(0)); - ASSERT_EQ(counter[1], long(0)); - ASSERT_EQ(counter[2], long(0)); - ASSERT_EQ(counter[3], long(0)); - - } // end test_view_layout_tiled_subtile_4d - -}; // end TestViewLayoutTiled struct - -} // namespace - -TEST(TEST_CATEGORY, view_layouttiled) { - // These two examples are iterating by tile, then within a tile - not by - // extents If N# is not a power of two, but want to iterate by tile then - // within a tile, need to check that mapped index is within extent - TestViewLayoutTiled::test_view_layout_tiled_2d(4, 12); - TestViewLayoutTiled::test_view_layout_tiled_3d(4, 12, 16); - TestViewLayoutTiled::test_view_layout_tiled_4d(4, 12, 16, 12); -} -TEST(TEST_CATEGORY, view_layouttiled_subtile) { - // These two examples are iterating by tile, then within a tile - not by - // extents If N# is not a power of two, but want to iterate by tile then - // within a tile, need to check that mapped index is within extent - TestViewLayoutTiled::test_view_layout_tiled_subtile_2d(4, 12); - TestViewLayoutTiled::test_view_layout_tiled_subtile_3d(4, 12, - 16); - TestViewLayoutTiled::test_view_layout_tiled_subtile_4d( - 4, 12, 16, 12); -} -} // namespace Test - -#undef KOKKOS_IMPL_PUBLIC_INCLUDE diff --git a/lib/kokkos/core/unit_test/TestViewOfViews.hpp b/lib/kokkos/core/unit_test/TestViewOfViews.hpp new file mode 100644 index 0000000000..a87c829bb7 --- /dev/null +++ b/lib/kokkos/core/unit_test/TestViewOfViews.hpp @@ -0,0 +1,75 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#include + +#include + +namespace { + +// User-defined type with a View data member +template +class S { + V v_; + + public: + template + S(std::string label, Extents... extents) : v_(std::move(label), extents...) {} + S() = default; +}; + +template +void test_view_of_views() { + using VoV = Kokkos::View; + { // assigning a default-constructed view to destruct the inner objects + VoV vov("vov", 2, 3); + V a("a"); + V b("b"); + vov(0, 0) = a; + vov(1, 0) = a; + vov(0, 1) = b; +#ifndef KOKKOS_ENABLE_IMPL_VIEW_OF_VIEWS_DESTRUCTOR_PRECONDITION_VIOLATION_WORKAROUND + vov(0, 0) = V(); + vov(1, 0) = V(); + vov(0, 1) = V(); +#endif + } + { // using placement new to construct the inner objects and explicitly + // calling the destructor + VoV vov(Kokkos::view_alloc("vov", Kokkos::WithoutInitializing), 2, 3); + V a("a"); + V b("b"); + new (&vov(0, 0)) V(a); + new (&vov(1, 0)) V(a); + new (&vov(0, 1)) V(b); +#ifndef KOKKOS_ENABLE_IMPL_VIEW_OF_VIEWS_DESTRUCTOR_PRECONDITION_VIOLATION_WORKAROUND + vov(0, 0).~V(); + vov(1, 0).~V(); + vov(0, 1).~V(); +#else + // leaks memory +#endif + } +} + +TEST(TEST_CATEGORY, view_of_views) { + test_view_of_views>(); + test_view_of_views>(); + // User-defined type with View data member + test_view_of_views>>(); +} + +} // namespace diff --git a/lib/kokkos/core/unit_test/TestViewSubview.hpp b/lib/kokkos/core/unit_test/TestViewSubview.hpp index 386887d923..c60aa2fe26 100644 --- a/lib/kokkos/core/unit_test/TestViewSubview.hpp +++ b/lib/kokkos/core/unit_test/TestViewSubview.hpp @@ -2294,9 +2294,8 @@ template struct TestExtentsStaticTests { using test1 = typename static_expect_same< /* expected */ - Kokkos::Experimental::Extents, + Kokkos::Experimental::Extents, /* actual */ typename Kokkos::Impl::ParseViewExtents::type>::type; diff --git a/lib/kokkos/core/unit_test/UnitTest_ScopeGuard.cpp b/lib/kokkos/core/unit_test/UnitTest_ScopeGuard.cpp new file mode 100644 index 0000000000..b2176f3ef0 --- /dev/null +++ b/lib/kokkos/core/unit_test/UnitTest_ScopeGuard.cpp @@ -0,0 +1,155 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#include +#include +#include + +namespace { + +/** + * Fixture that checks Kokkos is neither initialized nor finalized before and + * after the test. + */ +class AssertEnvironmentTest : public ::testing::Test { + protected: + void SetUp() override { + ASSERT_FALSE(Kokkos::is_initialized()); + ASSERT_FALSE(Kokkos::is_finalized()); + } + + void TearDown() override { + ASSERT_FALSE(Kokkos::is_initialized()); + ASSERT_FALSE(Kokkos::is_finalized()); + } +}; + +using scope_guard_DeathTest = AssertEnvironmentTest; + +/** + * Test to create a scope guard normally. + */ +TEST_F(scope_guard_DeathTest, create) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + // run it in a different process so side effects are not kept + EXPECT_EXIT( + { + { + Kokkos::ScopeGuard guard{}; + + if (!Kokkos::is_initialized()) std::exit(EXIT_FAILURE); + if (Kokkos::is_finalized()) std::exit(EXIT_FAILURE); + } + + if (Kokkos::is_initialized()) std::exit(EXIT_FAILURE); + if (!Kokkos::is_finalized()) std::exit(EXIT_FAILURE); + + std::exit(EXIT_SUCCESS); + }, + testing::ExitedWithCode(EXIT_SUCCESS), ""); +} + +/** + * Test to create a scope guard with an argument. + */ +TEST_F(scope_guard_DeathTest, create_argument) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + // run it in a different process so side effects are not kept + EXPECT_EXIT( + { + { + Kokkos::InitializationSettings settings{}; + Kokkos::ScopeGuard guard{settings}; + } + + std::exit(EXIT_SUCCESS); + }, + testing::ExitedWithCode(EXIT_SUCCESS), ""); +} + +/** + * Test to create another scope guard when one has been created. + */ +TEST_F(scope_guard_DeathTest, create_while_initialize) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + EXPECT_DEATH( + { + Kokkos::ScopeGuard guard1{}; + + // create a second scope guard while there is one already existing + Kokkos::ScopeGuard guard2{}; + }, + "Creating a ScopeGuard while Kokkos is initialized"); +} + +/** + * Test to create a scope guard when initialization has been done manually. + */ +TEST_F(scope_guard_DeathTest, create_after_initialize) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + EXPECT_DEATH( + { + Kokkos::initialize(); + + // create a scope guard after manual initialization + Kokkos::ScopeGuard guard{}; + }, + "Creating a ScopeGuard while Kokkos is initialized"); +} + +/** + * Test to create another scope guard when one has been destroyed. + */ +TEST_F(scope_guard_DeathTest, create_after_finalize) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + EXPECT_DEATH( + { + { Kokkos::ScopeGuard guard1{}; } + + // create a second scope guard while the first one has been destroyed + // already + Kokkos::ScopeGuard guard2{}; + }, + "Creating a ScopeGuard after Kokkos was finalized"); +} + +/** + * Test to destroy a scope guard when finalization has been done manually. + */ +TEST_F(scope_guard_DeathTest, destroy_after_finalize) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + EXPECT_DEATH( + { + // create a scope guard and finalize it manually + Kokkos::ScopeGuard guard{}; + Kokkos::finalize(); + }, + "Destroying a ScopeGuard after Kokkos was finalized"); +} + +/** + * Static tests + */ + +// Test scope guard is not copyable. +static_assert(!std::is_copy_assignable()); +static_assert(!std::is_copy_constructible()); + +// Test scope guard is not movable. +static_assert(!std::is_move_assignable()); +static_assert(!std::is_move_constructible()); + +} // namespace diff --git a/lib/kokkos/core/unit_test/category_files/TestHPX_Category.hpp b/lib/kokkos/core/unit_test/category_files/TestHPX_Category.hpp index d3a7cdbea5..c6a2aa9f20 100644 --- a/lib/kokkos/core/unit_test/category_files/TestHPX_Category.hpp +++ b/lib/kokkos/core/unit_test/category_files/TestHPX_Category.hpp @@ -23,5 +23,6 @@ #define TEST_CATEGORY_NUMBER 3 #define TEST_CATEGORY_DEATH hpx_DeathTest #define TEST_EXECSPACE Kokkos::Experimental::HPX +#define TEST_CATEGORY_FIXTURE(name) hpx_##name #endif diff --git a/lib/kokkos/core/unit_test/category_files/TestOpenACC_Category.hpp b/lib/kokkos/core/unit_test/category_files/TestOpenACC_Category.hpp index 0c4e4b7e11..6105eadf14 100644 --- a/lib/kokkos/core/unit_test/category_files/TestOpenACC_Category.hpp +++ b/lib/kokkos/core/unit_test/category_files/TestOpenACC_Category.hpp @@ -23,5 +23,6 @@ #define TEST_CATEGORY_NUMBER 8 #define TEST_CATEGORY_DEATH openacc_DeathTest #define TEST_EXECSPACE Kokkos::Experimental::OpenACC +#define TEST_CATEGORY_FIXTURE(name) openacc_##name #endif diff --git a/lib/kokkos/core/unit_test/category_files/TestOpenMPTarget_Category.hpp b/lib/kokkos/core/unit_test/category_files/TestOpenMPTarget_Category.hpp index 235b34ffab..921cff7890 100644 --- a/lib/kokkos/core/unit_test/category_files/TestOpenMPTarget_Category.hpp +++ b/lib/kokkos/core/unit_test/category_files/TestOpenMPTarget_Category.hpp @@ -23,5 +23,6 @@ #define TEST_CATEGORY_NUMBER 4 #define TEST_CATEGORY_DEATH openmptarget_DeathTest #define TEST_EXECSPACE Kokkos::Experimental::OpenMPTarget +#define TEST_CATEGORY_FIXTURE(name) openmptarget_##name #endif diff --git a/lib/kokkos/core/unit_test/category_files/TestSYCL_Category.hpp b/lib/kokkos/core/unit_test/category_files/TestSYCL_Category.hpp index 8e1b18c9ac..59e72c72c7 100644 --- a/lib/kokkos/core/unit_test/category_files/TestSYCL_Category.hpp +++ b/lib/kokkos/core/unit_test/category_files/TestSYCL_Category.hpp @@ -23,5 +23,6 @@ #define TEST_CATEGORY_NUMBER 7 #define TEST_CATEGORY_DEATH sycl_DeathTest #define TEST_EXECSPACE Kokkos::Experimental::SYCL +#define TEST_CATEGORY_FIXTURE(name) sycl_##name #endif diff --git a/lib/kokkos/core/unit_test/category_files/TestThreads_Category.hpp b/lib/kokkos/core/unit_test/category_files/TestThreads_Category.hpp index 13b0b653f2..ae8ac60833 100644 --- a/lib/kokkos/core/unit_test/category_files/TestThreads_Category.hpp +++ b/lib/kokkos/core/unit_test/category_files/TestThreads_Category.hpp @@ -23,5 +23,6 @@ #define TEST_CATEGORY_NUMBER 1 #define TEST_CATEGORY_DEATH threads_DeathTest #define TEST_EXECSPACE Kokkos::Threads +#define TEST_CATEGORY_FIXTURE(name) threads_##name #endif diff --git a/lib/kokkos/core/unit_test/cuda/TestCuda_Graph.cpp b/lib/kokkos/core/unit_test/cuda/TestCuda_Graph.cpp deleted file mode 100644 index 2720363969..0000000000 --- a/lib/kokkos/core/unit_test/cuda/TestCuda_Graph.cpp +++ /dev/null @@ -1,18 +0,0 @@ -//@HEADER -// ************************************************************************ -// -// Kokkos v. 4.0 -// Copyright (2022) National Technology & Engineering -// Solutions of Sandia, LLC (NTESS). -// -// Under the terms of Contract DE-NA0003525 with NTESS, -// the U.S. Government retains certain rights in this software. -// -// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. -// See https://kokkos.org/LICENSE for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//@HEADER - -#include -#include diff --git a/lib/kokkos/core/unit_test/cuda/TestCuda_InterOp_StreamsMultiGPU.cpp b/lib/kokkos/core/unit_test/cuda/TestCuda_InterOp_StreamsMultiGPU.cpp index d94735ceb2..40955e9c7c 100644 --- a/lib/kokkos/core/unit_test/cuda/TestCuda_InterOp_StreamsMultiGPU.cpp +++ b/lib/kokkos/core/unit_test/cuda/TestCuda_InterOp_StreamsMultiGPU.cpp @@ -15,7 +15,7 @@ //@HEADER #include -#include +#include namespace { @@ -57,79 +57,6 @@ std::array get_execution_spaces( return {exec0, exec1}; } -// Test Interoperability with Cuda Streams -void test_policies(TEST_EXECSPACE exec0, Kokkos::View v0, - TEST_EXECSPACE exec, Kokkos::View v) { - using MemorySpace = typename TEST_EXECSPACE::memory_space; - - Kokkos::deep_copy(exec, v, 5); - Kokkos::deep_copy(exec0, v0, 5); - - Kokkos::deep_copy(v, v0); - - int sum; - int sum0; - - Kokkos::parallel_for("Test::cuda::raw_cuda_stream::Range_0", - Kokkos::RangePolicy(exec0, 0, 100), - Test::FunctorRange(v0)); - Kokkos::parallel_for("Test::cuda::raw_cuda_stream::Range", - Kokkos::RangePolicy(exec, 0, 100), - Test::FunctorRange(v)); - Kokkos::parallel_reduce( - "Test::cuda::raw_cuda_stream::RangeReduce_0", - Kokkos::RangePolicy>(exec0, - 0, 100), - Test::FunctorRangeReduce(v0), sum0); - Kokkos::parallel_reduce( - "Test::cuda::raw_cuda_stream::RangeReduce", - Kokkos::RangePolicy>(exec, 0, - 100), - Test::FunctorRangeReduce(v), sum); - ASSERT_EQ(600, sum0); - ASSERT_EQ(600, sum); - - Kokkos::parallel_for("Test::cuda::raw_cuda_stream::MDRange_0", - Kokkos::MDRangePolicy>( - exec0, {0, 0}, {10, 10}), - Test::FunctorMDRange(v0)); - Kokkos::parallel_for("Test::cuda::raw_cuda_stream::MDRange", - Kokkos::MDRangePolicy>( - exec, {0, 0}, {10, 10}), - Test::FunctorMDRange(v)); - Kokkos::parallel_reduce("Test::cuda::raw_cuda_stream::MDRangeReduce_0", - Kokkos::MDRangePolicy, - Kokkos::LaunchBounds<128, 2>>( - exec0, {0, 0}, {10, 10}), - Test::FunctorMDRangeReduce(v0), sum0); - Kokkos::parallel_reduce("Test::cuda::raw_cuda_stream::MDRangeReduce", - Kokkos::MDRangePolicy, - Kokkos::LaunchBounds<128, 2>>( - exec, {0, 0}, {10, 10}), - Test::FunctorMDRangeReduce(v), sum); - ASSERT_EQ(700, sum0); - ASSERT_EQ(700, sum); - - Kokkos::parallel_for("Test::cuda::raw_cuda_stream::Team_0", - Kokkos::TeamPolicy(exec0, 10, 10), - Test::FunctorTeam(v0)); - Kokkos::parallel_for("Test::cuda::raw_cuda_stream::Team", - Kokkos::TeamPolicy(exec, 10, 10), - Test::FunctorTeam(v)); - Kokkos::parallel_reduce( - "Test::cuda::raw_cuda_stream::Team_0", - Kokkos::TeamPolicy>(exec0, - 10, 10), - Test::FunctorTeamReduce(v0), sum0); - Kokkos::parallel_reduce( - "Test::cuda::raw_cuda_stream::Team", - Kokkos::TeamPolicy>(exec, 10, - 10), - Test::FunctorTeamReduce(v), sum); - ASSERT_EQ(800, sum0); - ASSERT_EQ(800, sum); -} - TEST(cuda_multi_gpu, managed_views) { StreamsAndDevices streams_and_devices; { @@ -169,93 +96,6 @@ TEST(cuda_multi_gpu, unmanaged_views) { } } -struct ScratchFunctor { - int scratch_size; - int R; - - ScratchFunctor(int scratch_size_, int R_) - : scratch_size(scratch_size_), R(R_) {} - - KOKKOS_FUNCTION - void operator()(const Kokkos::TeamPolicy::member_type &team, - int &error_accum) const { - Kokkos::View scratch_mem( - team.team_scratch(1), scratch_size); - - // Initialize scratch memory - Kokkos::parallel_for(Kokkos::TeamVectorRange(team, 0, scratch_size), - [&](int i) { scratch_mem(i) = 0; }); - team.team_barrier(); - - // Increment each entry in scratch memory R times - for (int r = 0; r < R; ++r) { - Kokkos::parallel_for(Kokkos::TeamVectorRange(team, 0, scratch_size), - [&](int i) { scratch_mem(i) += 1; }); - } - team.team_barrier(); - - // Check that each scratch entry has been incremented exactly R times - int team_error_accum; - auto R_loc = R; // avoid implicit capture of this - Kokkos::parallel_reduce( - Kokkos::TeamVectorRange(team, 0, scratch_size), - [&](int i, int &tsum) { - if (scratch_mem(i) != R_loc) { - tsum += 1; - } - }, - team_error_accum); - Kokkos::single(Kokkos::PerTeam(team), - [&]() { error_accum += team_error_accum; }); - } -}; - -void test_scratch(TEST_EXECSPACE exec0, TEST_EXECSPACE exec1) { - constexpr int N = 10; - constexpr int R = 1000; - constexpr int scratch_size = 100; - using ScratchType = Kokkos::View; - - // Test allocating and using scratch space - ScratchFunctor f(scratch_size, R); - - auto policy0 = - Kokkos::TeamPolicy(exec0, N, 10) - .set_scratch_size( - 1, Kokkos::PerTeam(ScratchType::shmem_size(scratch_size))); - auto policy1 = - Kokkos::TeamPolicy(exec1, N, 10) - .set_scratch_size( - 1, Kokkos::PerTeam(ScratchType::shmem_size(scratch_size))); - - int error0, error1; - - Kokkos::parallel_reduce("test_scratch_device_0", policy0, f, error0); - Kokkos::parallel_reduce("test_scratch_device_1", policy1, f, error1); - ASSERT_EQ(error0, 0); - ASSERT_EQ(error1, 0); - - // Request larger scratch size to trigger a realloc and test - const auto new_scratch_size = scratch_size + 10; - ScratchFunctor f_more_scratch(new_scratch_size, R); - - auto policy0_more_scratch = - Kokkos::TeamPolicy(exec0, N, 10) - .set_scratch_size( - 1, Kokkos::PerTeam(ScratchType::shmem_size(new_scratch_size))); - auto policy1_more_scratch = - Kokkos::TeamPolicy(exec1, N, 10) - .set_scratch_size( - 1, Kokkos::PerTeam(ScratchType::shmem_size(new_scratch_size))); - - Kokkos::parallel_reduce("test_realloc_scratch_device_0", policy0_more_scratch, - f_more_scratch, error0); - Kokkos::parallel_reduce("test_realloc_scratch_device_1", policy1_more_scratch, - f_more_scratch, error1); - ASSERT_EQ(error0, 0); - ASSERT_EQ(error1, 0); -} - TEST(cuda_multi_gpu, scratch_space) { StreamsAndDevices streams_and_devices; { diff --git a/lib/kokkos/core/unit_test/headers_self_contained/CMakeLists.txt b/lib/kokkos/core/unit_test/headers_self_contained/CMakeLists.txt index f792b03ed8..4c364ceee7 100644 --- a/lib/kokkos/core/unit_test/headers_self_contained/CMakeLists.txt +++ b/lib/kokkos/core/unit_test/headers_self_contained/CMakeLists.txt @@ -10,7 +10,8 @@ file(GLOB KOKKOS_CONTAINERS_HEADERS RELATIVE ${BASE_DIR}/containers/src file(GLOB KOKKOS_ALGORITHMS_HEADERS RELATIVE ${BASE_DIR}/algorithms/src ${BASE_DIR}/algorithms/src/*.hpp) -if(NOT Kokkos_ENABLE_DEPRECATED_CODE_4) +# erroring out when deprecated code is disabled and raising warnings that are treated as errors in the CI otherwise +if(NOT Kokkos_ENABLE_DEPRECATED_CODE_4 OR Kokkos_ENABLE_DEPRECATION_WARNINGS) list(REMOVE_ITEM KOKKOS_CONTAINERS_HEADERS "Kokkos_Vector.hpp") endif() diff --git a/lib/kokkos/core/unit_test/hip/TestHIP_Memory_Requirements.cpp b/lib/kokkos/core/unit_test/hip/TestHIP_Memory_Requirements.cpp index a213453ea1..8c72e9f297 100644 --- a/lib/kokkos/core/unit_test/hip/TestHIP_Memory_Requirements.cpp +++ b/lib/kokkos/core/unit_test/hip/TestHIP_Memory_Requirements.cpp @@ -48,9 +48,6 @@ TEST(hip, memory_requirements) { // we want all user-facing memory in hip to be coarse grained. As of // today(07.01.22) the documentation is not reliable/correct, we test the // memory on the device and host - // FIXME_HIP - GTEST_SKIP() << "skipping the test because the CI on MI100 returns: error( " - "hipErrorInvalidValue)"; KOKKOS_TEST_MEMORY_COARSEGRAINEDNESS(Kokkos::HIPSpace, int, 10); KOKKOS_TEST_MEMORY_COARSEGRAINEDNESS(Kokkos::HIPHostPinnedSpace, int, 10); KOKKOS_TEST_MEMORY_COARSEGRAINEDNESS(Kokkos::HIPManagedSpace, int, 10); diff --git a/lib/kokkos/core/unit_test/incremental/Test01_execspace.hpp b/lib/kokkos/core/unit_test/incremental/Test01_execspace.hpp index d7b2a57b44..a7fa26c728 100644 --- a/lib/kokkos/core/unit_test/incremental/Test01_execspace.hpp +++ b/lib/kokkos/core/unit_test/incremental/Test01_execspace.hpp @@ -63,7 +63,9 @@ struct TestIncrExecSpace { ASSERT_GT(concurrency, 0); #ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4 + KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_PUSH() int in_parallel = ExecSpace::in_parallel(); + KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_POP() ASSERT_FALSE(in_parallel); #endif diff --git a/lib/kokkos/core/unit_test/openmp/TestOpenMP_Graph.cpp b/lib/kokkos/core/unit_test/openmp/TestOpenMP_Graph.cpp deleted file mode 100644 index 22c8ab1bf8..0000000000 --- a/lib/kokkos/core/unit_test/openmp/TestOpenMP_Graph.cpp +++ /dev/null @@ -1,18 +0,0 @@ -//@HEADER -// ************************************************************************ -// -// Kokkos v. 4.0 -// Copyright (2022) National Technology & Engineering -// Solutions of Sandia, LLC (NTESS). -// -// Under the terms of Contract DE-NA0003525 with NTESS, -// the U.S. Government retains certain rights in this software. -// -// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. -// See https://kokkos.org/LICENSE for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//@HEADER - -#include -#include diff --git a/lib/kokkos/core/unit_test/serial/TestSerial_Graph.cpp b/lib/kokkos/core/unit_test/serial/TestSerial_Graph.cpp deleted file mode 100644 index bff64d83e2..0000000000 --- a/lib/kokkos/core/unit_test/serial/TestSerial_Graph.cpp +++ /dev/null @@ -1,18 +0,0 @@ -//@HEADER -// ************************************************************************ -// -// Kokkos v. 4.0 -// Copyright (2022) National Technology & Engineering -// Solutions of Sandia, LLC (NTESS). -// -// Under the terms of Contract DE-NA0003525 with NTESS, -// the U.S. Government retains certain rights in this software. -// -// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. -// See https://kokkos.org/LICENSE for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//@HEADER - -#include -#include diff --git a/lib/kokkos/core/unit_test/sycl/TestSYCL_InterOp_StreamsMultiGPU.cpp b/lib/kokkos/core/unit_test/sycl/TestSYCL_InterOp_StreamsMultiGPU.cpp new file mode 100644 index 0000000000..d3906e409f --- /dev/null +++ b/lib/kokkos/core/unit_test/sycl/TestSYCL_InterOp_StreamsMultiGPU.cpp @@ -0,0 +1,64 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#include +#include + +namespace { + +std::array get_execution_spaces() { + std::vector gpu_devices = + sycl::device::get_devices(sycl::info::device_type::gpu); + + TEST_EXECSPACE exec0( + sycl::queue{gpu_devices.front(), sycl::property::queue::in_order()}); + TEST_EXECSPACE exec1( + sycl::queue{gpu_devices.back(), sycl::property::queue::in_order()}); + + return {exec0, exec1}; +} + +TEST(sycl_multi_gpu, managed_views) { + std::array execs = get_execution_spaces(); + + Kokkos::View view0(Kokkos::view_alloc("v0", execs[0]), + 100); + Kokkos::View view(Kokkos::view_alloc("v", execs[1]), + 100); + + test_policies(execs[0], view0, execs[1], view); +} + +TEST(sycl_multi_gpu, unmanaged_views) { + std::array execs = get_execution_spaces(); + + int *p0 = sycl::malloc_device(100, execs[0].sycl_queue()); + Kokkos::View view0(p0, 100); + + int *p1 = sycl::malloc_device(100, execs[1].sycl_queue()); + Kokkos::View view1(p1, 100); + + test_policies(execs[0], view0, execs[1], view1); + sycl::free(p0, execs[0].sycl_queue()); + sycl::free(p1, execs[1].sycl_queue()); +} + +TEST(sycl_multi_gpu, scratch_space) { + std::array execs = get_execution_spaces(); + + test_scratch(execs[0], execs[1]); +} +} // namespace diff --git a/lib/kokkos/core/unit_test/view/TestExtentsDatatypeConversion.cpp b/lib/kokkos/core/unit_test/view/TestExtentsDatatypeConversion.cpp index b95890614e..1b9b2a3681 100644 --- a/lib/kokkos/core/unit_test/view/TestExtentsDatatypeConversion.cpp +++ b/lib/kokkos/core/unit_test/view/TestExtentsDatatypeConversion.cpp @@ -23,15 +23,14 @@ namespace { // Helper to make static tests more succinct template -constexpr bool datatype_matches_extent = - std::is_same_v::type, - Extent>; +constexpr bool datatype_matches_extent = std::is_same_v< + typename Kokkos::Impl::ExtentsFromDataType::type, + Extent>; template constexpr bool extent_matches_datatype = - std::is_same_v::type>; + std::is_same_v::type>; // Conversion from DataType to extents // 0-rank view diff --git a/lib/kokkos/example/README b/lib/kokkos/example/README index 6686051244..2fe8727648 100644 --- a/lib/kokkos/example/README +++ b/lib/kokkos/example/README @@ -1,7 +1,7 @@ This directory contains example application proxies that use different parts of Kokkos. If you are looking for the FENL ("finite element -nonlinear" solve) example, it has moved into the LinAlg subpackage of -Tpetra. +nonlinear" solve) example, it has moved into the TrilinosCouplings +package in Trilinos. MANIFEST: diff --git a/lib/kokkos/example/build_cmake_installed/CMakeLists.txt b/lib/kokkos/example/build_cmake_installed/CMakeLists.txt index aaf745b418..c025f1d7d2 100644 --- a/lib/kokkos/example/build_cmake_installed/CMakeLists.txt +++ b/lib/kokkos/example/build_cmake_installed/CMakeLists.txt @@ -12,6 +12,7 @@ find_package(Kokkos REQUIRED) add_executable(example cmake_example.cpp foo.f) if(CMAKE_Fortran_COMPILER_ID STREQUAL LLVMFlang) set_target_properties(example PROPERTIES LINKER_LANGUAGE Fortran) + target_link_options(example PRIVATE -fno-fortran-main) endif() # This is the only thing required to set up compiler/linker flags diff --git a/lib/kokkos/example/tutorial/01_hello_world/hello_world.cpp b/lib/kokkos/example/tutorial/01_hello_world/hello_world.cpp index 22b8b6d63c..3104003fb4 100644 --- a/lib/kokkos/example/tutorial/01_hello_world/hello_world.cpp +++ b/lib/kokkos/example/tutorial/01_hello_world/hello_world.cpp @@ -16,7 +16,6 @@ #include #include -#include // // "Hello world" parallel_for example: @@ -25,12 +24,12 @@ // using a functor to define the loop body // 3. Shut down Kokkos // -// If Kokkos was built with C++11 enabled, try comparing this example -// to 01_hello_world_lambda. The latter uses C++11 lambdas (anonymous -// functions) to define the loop body of the parallel_for. That makes -// the code much more concise and readable. On the other hand, -// breaking out the loop body into an explicit functor makes it easier -// to test the loop independently of the parallel pattern. +// Try comparing this example to 01_hello_world_lambda, which uses +// C++11 lambdas (anonymous functions) to define the loop body of the +// parallel_for. That makes the code much more concise and readable. +// On the other hand, breaking out the loop body into an explicit +// functor makes it easier to test the loop independently of the +// parallel pattern. // // Functor that defines the parallel_for's loop body. @@ -72,11 +71,9 @@ int main(int argc, char* argv[]) { // start with "--kokkos-". Kokkos::initialize(argc, argv); - // Print the name of Kokkos' default execution space. We're using - // typeid here, so the name might get a bit mangled by the linker, - // but you should still be able to figure out what it is. + // Print the name of Kokkos' default execution space. printf("Hello World on Kokkos execution space %s\n", - typeid(Kokkos::DefaultExecutionSpace).name()); + Kokkos::DefaultExecutionSpace::name()); // Run the above functor on the default Kokkos execution space in // parallel, with a parallel for loop count of 15. diff --git a/lib/kokkos/example/tutorial/01_hello_world_lambda/hello_world_lambda.cpp b/lib/kokkos/example/tutorial/01_hello_world_lambda/hello_world_lambda.cpp index 909765e1fc..ad2c258c0f 100644 --- a/lib/kokkos/example/tutorial/01_hello_world_lambda/hello_world_lambda.cpp +++ b/lib/kokkos/example/tutorial/01_hello_world_lambda/hello_world_lambda.cpp @@ -16,7 +16,6 @@ #include #include -#include // // "Hello world" parallel_for example: @@ -25,10 +24,9 @@ // using a C++11 lambda to define the loop body // 3. Shut down Kokkos // -// This example only builds if C++11 is enabled. Compare this example -// to 01_hello_world, which uses functors (explicitly defined classes) -// to define the loop body of the parallel_for. Both functors and -// lambdas have their places. +// Compare this example to 01_hello_world, which uses functors +// (explicitly defined classes) to define the loop body of the +// parallel_for. Both functors and lambdas have their places. // int main(int argc, char* argv[]) { @@ -41,11 +39,9 @@ int main(int argc, char* argv[]) { // start with "--kokkos-". Kokkos::initialize(argc, argv); - // Print the name of Kokkos' default execution space. We're using - // typeid here, so the name might get a bit mangled by the linker, - // but you should still be able to figure out what it is. + // Print the name of Kokkos' default execution space. printf("Hello World on Kokkos execution space %s\n", - typeid(Kokkos::DefaultExecutionSpace).name()); + Kokkos::DefaultExecutionSpace::name()); // Run lambda on the default Kokkos execution space in parallel, // with a parallel for loop count of 15. The lambda's argument is diff --git a/lib/kokkos/example/tutorial/02_simple_reduce_lambda/simple_reduce_lambda.cpp b/lib/kokkos/example/tutorial/02_simple_reduce_lambda/simple_reduce_lambda.cpp index 5cae6da16c..1ca30e07e8 100644 --- a/lib/kokkos/example/tutorial/02_simple_reduce_lambda/simple_reduce_lambda.cpp +++ b/lib/kokkos/example/tutorial/02_simple_reduce_lambda/simple_reduce_lambda.cpp @@ -24,9 +24,8 @@ // using a C++11 lambda to define the loop body // 3. Shut down Kokkos // -// This example only builds if C++11 is enabled. Compare this example -// to 02_simple_reduce, which uses a functor to define the loop body -// of the parallel_reduce. +// Compare this example to 02_simple_reduce, which uses a functor to +// define the loop body of the parallel_reduce. // int main(int argc, char* argv[]) { diff --git a/lib/kokkos/generate_makefile.bash b/lib/kokkos/generate_makefile.bash index 70dd61f9af..25370daa3f 100755 --- a/lib/kokkos/generate_makefile.bash +++ b/lib/kokkos/generate_makefile.bash @@ -164,7 +164,6 @@ display_help_text() { echo " AMD_GFX942 = AMD GPU MI300 GFX942" echo " AMD_GFX1030 = AMD GPU V620/W6800 GFX1030" echo " AMD_GFX1100 = AMD GPU RX 7900 XT(X) GFX1100" - echo " AMD_GFX1103 = AMD APU Radeon 740M/760M/780M/880M/890M GFX1103" echo " [ARM]" echo " ARMV80 = ARMv8.0 Compatible CPU" echo " ARMV81 = ARMv8.1 Compatible CPU" diff --git a/lib/kokkos/master_history.txt b/lib/kokkos/master_history.txt index 31be925325..a0e83bef23 100644 --- a/lib/kokkos/master_history.txt +++ b/lib/kokkos/master_history.txt @@ -37,3 +37,4 @@ tag: 4.2.00 date: 11:09:2023 master: 1a3ea28f release: abe01c88 tag: 4.2.01 date: 01:30:2024 master: 71a9bcae release: 221e5f7a tag: 4.3.00 date: 04:03:2024 master: e0dc0128 release: f08217a4 tag: 4.3.01 date: 05:07:2024 master: 486cc745 release: 262d2d6e +tag: 4.4.00 date: 08:08:2024 master: 6ecdf605 release: 6068673c diff --git a/lib/kokkos/simd/src/Kokkos_SIMD.hpp b/lib/kokkos/simd/src/Kokkos_SIMD.hpp index 57d4afd88b..5e34e51989 100644 --- a/lib/kokkos/simd/src/Kokkos_SIMD.hpp +++ b/lib/kokkos/simd/src/Kokkos_SIMD.hpp @@ -183,15 +183,18 @@ template class data_types {}; #if defined(KOKKOS_ARCH_AVX512XEON) -using host_abi_set = abi_set>; +using host_abi_set = abi_set, + simd_abi::avx512_fixed_size<16>>; using data_type_set = data_types; #elif defined(KOKKOS_ARCH_AVX2) -using host_abi_set = abi_set>; +using host_abi_set = abi_set, + simd_abi::avx2_fixed_size<8>>; using data_type_set = data_types; #elif defined(KOKKOS_ARCH_ARM_NEON) -using host_abi_set = abi_set>; +using host_abi_set = abi_set, + simd_abi::neon_fixed_size<4>>; using data_type_set = data_types; #else diff --git a/lib/kokkos/simd/src/Kokkos_SIMD_AVX2.hpp b/lib/kokkos/simd/src/Kokkos_SIMD_AVX2.hpp index 6d0956f383..27c8af79ab 100644 --- a/lib/kokkos/simd/src/Kokkos_SIMD_AVX2.hpp +++ b/lib/kokkos/simd/src/Kokkos_SIMD_AVX2.hpp @@ -228,6 +228,106 @@ class simd_mask> { } }; +template <> +class simd_mask> { + __m256 m_value; + + public: + class reference { + __m256& m_mask; + int m_lane; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION __m256 bit_mask() const { + // FIXME_HIP ROCm 5.6, 5.7, and 6.0 can't compile with the intrinsic used + // here. +#ifdef KOKKOS_IMPL_WORKAROUND_ROCM_AVX2_ISSUE + return _mm256_cvtepi32_ps(_mm256_setr_epi32( +#else + return _mm256_castsi256_ps(_mm256_setr_epi32( +#endif + -std::int32_t(m_lane == 0), -std::int32_t(m_lane == 1), + -std::int32_t(m_lane == 2), -std::int32_t(m_lane == 3), + -std::int32_t(m_lane == 4), -std::int32_t(m_lane == 5), + -std::int32_t(m_lane == 6), -std::int32_t(m_lane == 7))); + } + + public: + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION reference(__m256& mask_arg, + int lane_arg) + : m_mask(mask_arg), m_lane(lane_arg) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION reference + operator=(bool value) const { + if (value) { + m_mask = _mm256_or_ps(bit_mask(), m_mask); + } else { + m_mask = _mm256_andnot_ps(bit_mask(), m_mask); + } + return *this; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION operator bool() const { + return (_mm256_movemask_ps(m_mask) & (1 << m_lane)) != 0; + } + }; + using value_type = bool; + using abi_type = simd_abi::avx2_fixed_size<8>; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask() = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION explicit simd_mask(value_type value) + : m_value(_mm256_castsi256_ps(_mm256_set1_epi32(-std::int32_t(value)))) {} + template >, + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit simd_mask( + G&& gen) noexcept + : m_value(_mm256_castsi256_ps(_mm256_setr_epi32( + -std::int32_t(gen(std::integral_constant())), + -std::int32_t(gen(std::integral_constant())), + -std::int32_t(gen(std::integral_constant())), + -std::int32_t(gen(std::integral_constant())), + -std::int32_t(gen(std::integral_constant())), + -std::int32_t(gen(std::integral_constant())), + -std::int32_t(gen(std::integral_constant())), + -std::int32_t(gen(std::integral_constant()))))) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION static constexpr std::size_t size() { + return 8; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit simd_mask( + __m256 const& value_in) + : m_value(value_in) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit operator __m256() + const { + return m_value; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION reference operator[](std::size_t i) { + return reference(m_value, int(i)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION value_type + operator[](std::size_t i) const { + return static_cast( + reference(const_cast<__m256&>(m_value), int(i))); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask + operator||(simd_mask const& other) const { + return simd_mask(_mm256_or_ps(m_value, other.m_value)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask + operator&&(simd_mask const& other) const { + return simd_mask(_mm256_and_ps(m_value, other.m_value)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask operator!() const { + auto const true_value = static_cast<__m256>(simd_mask(true)); + return simd_mask(_mm256_andnot_ps(m_value, true_value)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION bool operator==( + simd_mask const& other) const { + return _mm256_movemask_ps(m_value) == _mm256_movemask_ps(other.m_value); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION bool operator!=( + simd_mask const& other) const { + return !operator==(other); + } +}; + template <> class simd_mask> { __m128i m_value; @@ -324,6 +424,109 @@ class simd_mask> { } }; +template <> +class simd_mask> { + __m256i m_value; + + public: + class reference { + __m256i& m_mask; + int m_lane; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION __m256i bit_mask() const { + return _mm256_setr_epi32( + -std::int32_t(m_lane == 0), -std::int32_t(m_lane == 1), + -std::int32_t(m_lane == 2), -std::int32_t(m_lane == 3), + -std::int32_t(m_lane == 4), -std::int32_t(m_lane == 5), + -std::int32_t(m_lane == 6), -std::int32_t(m_lane == 7)); + } + + public: + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION reference(__m256i& mask_arg, + int lane_arg) + : m_mask(mask_arg), m_lane(lane_arg) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION reference + operator=(bool value) const { + if (value) { + m_mask = _mm256_or_si256(bit_mask(), m_mask); + } else { + m_mask = _mm256_andnot_si256(bit_mask(), m_mask); + } + return *this; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION operator bool() const { + return (_mm256_movemask_ps(_mm256_castsi256_ps(m_mask)) & + (1 << m_lane)) != 0; + } + }; + using value_type = bool; + using abi_type = simd_abi::avx2_fixed_size<8>; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask() = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask(simd_mask const&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask(simd_mask&&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION explicit simd_mask(value_type value) + : m_value(_mm256_set1_epi32(-std::int32_t(value))) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION static constexpr std::size_t size() { + return 8; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit simd_mask( + __m256i const& value_in) + : m_value(value_in) {} + template >, + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit simd_mask( + G&& gen) noexcept + : m_value(_mm256_setr_epi32( + -std::int32_t(gen(std::integral_constant())), + -std::int32_t(gen(std::integral_constant())), + -std::int32_t(gen(std::integral_constant())), + -std::int32_t(gen(std::integral_constant())), + -std::int32_t(gen(std::integral_constant())), + -std::int32_t(gen(std::integral_constant())), + -std::int32_t(gen(std::integral_constant())), + -std::int32_t(gen(std::integral_constant())))) {} + template + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask( + simd_mask const& other) { + for (std::size_t i = 0; i < size(); ++i) (*this)[i] = other[i]; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit operator __m256i() + const { + return m_value; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION reference operator[](std::size_t i) { + return reference(m_value, int(i)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION value_type + operator[](std::size_t i) const { + return static_cast( + reference(const_cast<__m256i&>(m_value), int(i))); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask + operator||(simd_mask const& other) const { + return simd_mask(_mm256_or_si256(m_value, other.m_value)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask + operator&&(simd_mask const& other) const { + return simd_mask(_mm256_and_si256(m_value, other.m_value)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask operator!() const { + auto const true_value = static_cast<__m256i>(simd_mask(true)); + return simd_mask(_mm256_andnot_si256(m_value, true_value)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION bool operator==( + simd_mask const& other) const { + return _mm256_movemask_ps(_mm256_castsi256_ps(m_value)) == + _mm256_movemask_ps(_mm256_castsi256_ps(other.m_value)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION bool operator!=( + simd_mask const& other) const { + return !operator==(other); + } +}; + template <> class simd_mask> { __m256i m_value; @@ -800,11 +1003,11 @@ class simd> { KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION static constexpr std::size_t size() { return 4; } - template , - bool> = false> + template , + bool> = false> KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(U&& value) : m_value(_mm_set1_ps(value_type(value))) {} - template >, @@ -1030,6 +1233,264 @@ namespace Experimental { static_cast<__m128>(c), static_cast<__m128>(b), static_cast<__m128>(a))); } +template <> +class simd> { + __m256 m_value; + + public: + using value_type = float; + using abi_type = simd_abi::avx2_fixed_size<8>; + using mask_type = simd_mask; + using reference = value_type&; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd() = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(simd const&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(simd&&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd& operator=(simd const&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd& operator=(simd&&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION static constexpr std::size_t size() { + return 8; + } + template , + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(U&& value) + : m_value(_mm256_set1_ps(value_type(value))) {} + template >, + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(G&& gen) + : m_value(_mm256_setr_ps(gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()))) { + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit simd( + __m256 const& value_in) + : m_value(value_in) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION reference operator[](std::size_t i) { + return reinterpret_cast(&m_value)[i]; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION value_type + operator[](std::size_t i) const { + return reinterpret_cast(&m_value)[i]; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_from(value_type const* ptr, + element_aligned_tag) { + m_value = _mm256_loadu_ps(ptr); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_from(value_type const* ptr, + vector_aligned_tag) { + m_value = _mm256_load_ps(ptr); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_to( + value_type* ptr, element_aligned_tag) const { + _mm256_storeu_ps(ptr, m_value); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_to(value_type* ptr, + vector_aligned_tag) const { + _mm256_store_ps(ptr, m_value); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit operator __m256() + const { + return m_value; + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd operator-() const + noexcept { + return simd(_mm256_sub_ps(_mm256_set1_ps(0.0), m_value)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator*( + simd const& lhs, simd const& rhs) noexcept { + return simd(_mm256_mul_ps(lhs.m_value, rhs.m_value)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator/( + simd const& lhs, simd const& rhs) noexcept { + return simd(_mm256_div_ps(lhs.m_value, rhs.m_value)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator+( + simd const& lhs, simd const& rhs) noexcept { + return simd(_mm256_add_ps(lhs.m_value, rhs.m_value)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator-( + simd const& lhs, simd const& rhs) noexcept { + return simd(_mm256_sub_ps(lhs.m_value, rhs.m_value)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator<(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm256_cmp_ps(static_cast<__m256>(lhs), + static_cast<__m256>(rhs), _CMP_LT_OS)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator>(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm256_cmp_ps(static_cast<__m256>(lhs), + static_cast<__m256>(rhs), _CMP_GT_OS)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator<=(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm256_cmp_ps(static_cast<__m256>(lhs), + static_cast<__m256>(rhs), _CMP_LE_OS)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator>=(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm256_cmp_ps(static_cast<__m256>(lhs), + static_cast<__m256>(rhs), _CMP_GE_OS)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator==(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm256_cmp_ps(static_cast<__m256>(lhs), + static_cast<__m256>(rhs), _CMP_EQ_OS)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator!=(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm256_cmp_ps(static_cast<__m256>(lhs), + static_cast<__m256>(rhs), _CMP_NEQ_OS)); + } +}; + +} // namespace Experimental + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION Experimental::simd< + float, Experimental::simd_abi::avx2_fixed_size<8>> +copysign( + Experimental::simd> const& + a, + Experimental::simd> const& + b) { + __m256 const sign_mask = _mm256_set1_ps(-0.0); + return Experimental::simd>( + _mm256_xor_ps(_mm256_andnot_ps(sign_mask, static_cast<__m256>(a)), + _mm256_and_ps(sign_mask, static_cast<__m256>(b)))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + abs(Experimental::simd< + float, Experimental::simd_abi::avx2_fixed_size<8>> const& a) { + __m256 const sign_mask = _mm256_set1_ps(-0.0); + return Experimental::simd>( + _mm256_andnot_ps(sign_mask, static_cast<__m256>(a))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + floor(Experimental::simd< + float, Experimental::simd_abi::avx2_fixed_size<8>> const& a) { + return Experimental::simd>( + _mm256_round_ps(static_cast<__m256>(a), + (_MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + ceil(Experimental::simd< + float, Experimental::simd_abi::avx2_fixed_size<8>> const& a) { + return Experimental::simd>( + _mm256_round_ps(static_cast<__m256>(a), + (_MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + round(Experimental::simd< + float, Experimental::simd_abi::avx2_fixed_size<8>> const& a) { + return Experimental::simd>( + _mm256_round_ps(static_cast<__m256>(a), + (_MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + trunc(Experimental::simd< + float, Experimental::simd_abi::avx2_fixed_size<8>> const& a) { + return Experimental::simd>( + _mm256_round_ps(static_cast<__m256>(a), + (_MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + sqrt(Experimental::simd< + float, Experimental::simd_abi::avx2_fixed_size<8>> const& a) { + return Experimental::simd>( + _mm256_sqrt_ps(static_cast<__m256>(a))); +} + +#ifdef __INTEL_COMPILER + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + cbrt(Experimental::simd< + float, Experimental::simd_abi::avx2_fixed_size<8>> const& a) { + return Experimental::simd>( + _mm256_cbrt_ps(static_cast<__m256>(a))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + exp(Experimental::simd< + float, Experimental::simd_abi::avx2_fixed_size<8>> const& a) { + return Experimental::simd>( + _mm256_exp_ps(static_cast<__m256>(a))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + log(Experimental::simd< + float, Experimental::simd_abi::avx2_fixed_size<8>> const& a) { + return Experimental::simd>( + _mm256_log_ps(static_cast<__m256>(a))); +} + +#endif + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION Experimental::simd< + float, Experimental::simd_abi::avx2_fixed_size<8>> +fma(Experimental::simd> const& + a, + Experimental::simd> const& + b, + Experimental::simd> const& + c) { + return Experimental::simd>( + _mm256_fmadd_ps(static_cast<__m256>(a), static_cast<__m256>(b), + static_cast<__m256>(c))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION Experimental::simd< + float, Experimental::simd_abi::avx2_fixed_size<8>> +max(Experimental::simd> const& + a, + Experimental::simd> const& + b) { + return Experimental::simd>( + _mm256_max_ps(static_cast<__m256>(a), static_cast<__m256>(b))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION Experimental::simd< + float, Experimental::simd_abi::avx2_fixed_size<8>> +min(Experimental::simd> const& + a, + Experimental::simd> const& + b) { + return Experimental::simd>( + _mm256_min_ps(static_cast<__m256>(a), static_cast<__m256>(b))); +} + +namespace Experimental { + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + simd> + condition(simd_mask> const& a, + simd> const& b, + simd> const& c) { + return simd>(_mm256_blendv_ps( + static_cast<__m256>(c), static_cast<__m256>(b), static_cast<__m256>(a))); +} + template <> class simd> { __m128i m_value; @@ -1229,6 +1690,207 @@ namespace Experimental { _mm_castsi128_ps(static_cast<__m128i>(a))))); } +template <> +class simd> { + __m256i m_value; + + public: + using value_type = std::int32_t; + using abi_type = simd_abi::avx2_fixed_size<8>; + using mask_type = simd_mask; + using reference = value_type&; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd() = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(simd const&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(simd&&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd& operator=(simd const&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd& operator=(simd&&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION static constexpr std::size_t size() { + return 8; + } + template , + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(U&& value) + : m_value(_mm256_set1_epi32(value_type(value))) {} + template >, + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit simd( + G&& gen) noexcept + : m_value( + _mm256_setr_epi32(gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()))) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit simd( + __m256i const& value_in) + : m_value(value_in) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION reference operator[](std::size_t i) { + return reinterpret_cast(&m_value)[i]; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION value_type + operator[](std::size_t i) const { + return reinterpret_cast(&m_value)[i]; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_from(value_type const* ptr, + element_aligned_tag) { + // FIXME_HIP ROCm 5.6, 5.7, and 6.0 can't compile with the intrinsic used + // here. +#ifdef KOKKOS_IMPL_WORKAROUND_ROCM_AVX2_ISSUE + m_value = _mm256_loadu_si256(reinterpret_cast<__m256i const*>(ptr)); +#else + m_value = _mm256_maskload_epi32(ptr, static_cast<__m256i>(mask_type(true))); +#endif + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_from(value_type const* ptr, + vector_aligned_tag) { + // FIXME_HIP ROCm 5.6, 5.7, and 6.0 can't compile with the intrinsic used + // here. +#ifdef KOKKOS_IMPL_WORKAROUND_ROCM_AVX2_ISSUE + m_value = _mm256_loadu_si256(reinterpret_cast<__m256i const*>(ptr)); +#else + m_value = _mm256_maskload_epi32(ptr, static_cast<__m256i>(mask_type(true))); +#endif + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_to( + value_type* ptr, element_aligned_tag) const { + _mm256_maskstore_epi32(ptr, static_cast<__m256i>(mask_type(true)), m_value); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_to(value_type* ptr, + vector_aligned_tag) const { + _mm256_maskstore_epi32(ptr, static_cast<__m256i>(mask_type(true)), m_value); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit operator __m256i() + const { + return m_value; + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator==(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm256_cmpeq_epi32(static_cast<__m256i>(lhs), + static_cast<__m256i>(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator>(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm256_cmpgt_epi32(static_cast<__m256i>(lhs), + static_cast<__m256i>(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator<(simd const& lhs, simd const& rhs) noexcept { + return !(lhs >= rhs); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator<=(simd const& lhs, simd const& rhs) noexcept { + return (lhs < rhs) || (lhs == rhs); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator>=(simd const& lhs, simd const& rhs) noexcept { + return (lhs > rhs) || (lhs == rhs); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator!=(simd const& lhs, simd const& rhs) noexcept { + return !(lhs == rhs); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator-( + simd const& lhs, simd const& rhs) noexcept { + return simd( + _mm256_sub_epi32(static_cast<__m256i>(lhs), static_cast<__m256i>(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator+( + simd const& lhs, simd const& rhs) noexcept { + return simd( + _mm256_add_epi32(static_cast<__m256i>(lhs), static_cast<__m256i>(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator*( + simd const& lhs, simd const& rhs) noexcept { + return simd(_mm256_mullo_epi32(static_cast<__m256i>(lhs), + static_cast<__m256i>(rhs))); + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator>>( + simd const& lhs, int rhs) noexcept { + return simd(_mm256_srai_epi32(static_cast<__m256i>(lhs), rhs)); + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator>>( + simd const& lhs, simd const& rhs) noexcept { + return simd(_mm256_srav_epi32(static_cast<__m256i>(lhs), + static_cast<__m256i>(rhs))); + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator<<( + simd const& lhs, int rhs) noexcept { + return simd(_mm256_slli_epi32(static_cast<__m256i>(lhs), rhs)); + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator<<( + simd const& lhs, simd const& rhs) noexcept { + return simd(_mm256_sllv_epi32(static_cast<__m256i>(lhs), + static_cast<__m256i>(rhs))); + } +}; + +} // namespace Experimental + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + abs(Experimental::simd< + std::int32_t, Experimental::simd_abi::avx2_fixed_size<8>> const& a) { + __m256i const rhs = static_cast<__m256i>(a); + return Experimental::simd>( + _mm256_abs_epi32(rhs)); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + floor(Experimental::simd< + std::int32_t, Experimental::simd_abi::avx2_fixed_size<8>> const& a) { + return Experimental::simd>( + _mm256_cvtepi32_ps(static_cast<__m256i>(a))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + ceil(Experimental::simd< + std::int32_t, Experimental::simd_abi::avx2_fixed_size<8>> const& a) { + return Experimental::simd>( + _mm256_cvtepi32_ps(static_cast<__m256i>(a))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + round(Experimental::simd< + std::int32_t, Experimental::simd_abi::avx2_fixed_size<8>> const& a) { + return Experimental::simd>( + _mm256_cvtepi32_ps(static_cast<__m256i>(a))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + trunc(Experimental::simd< + std::int32_t, Experimental::simd_abi::avx2_fixed_size<8>> const& a) { + return Experimental::simd>( + _mm256_cvtepi32_ps(static_cast<__m256i>(a))); +} + +namespace Experimental { + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + simd> + condition(simd_mask> const& a, + simd> const& b, + simd> const& c) { + return simd>(_mm256_castps_si256( + _mm256_blendv_ps(_mm256_castsi256_ps(static_cast<__m256i>(c)), + _mm256_castsi256_ps(static_cast<__m256i>(b)), + _mm256_castsi256_ps(static_cast<__m256i>(a))))); +} + template <> class simd> { __m256i m_value; @@ -1515,6 +2177,16 @@ class simd> { static_cast<__m256i>(mask_type(true))); #endif } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_to( + value_type* ptr, element_aligned_tag) const { + _mm256_maskstore_epi64(reinterpret_cast(ptr), + static_cast<__m256i>(mask_type(true)), m_value); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_to(value_type* ptr, + vector_aligned_tag) const { + _mm256_maskstore_epi64(reinterpret_cast(ptr), + static_cast<__m256i>(mask_type(true)), m_value); + } KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit operator __m256i() const { return m_value; @@ -1821,6 +2493,94 @@ class where_expression>, } }; +template <> +class const_where_expression>, + simd>> { + public: + using abi_type = simd_abi::avx2_fixed_size<8>; + using value_type = simd; + using mask_type = simd_mask; + + protected: + value_type& m_value; + mask_type const& m_mask; + + public: + const_where_expression(mask_type const& mask_arg, value_type const& value_arg) + : m_value(const_cast(value_arg)), m_mask(mask_arg) {} + + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_to(float* mem, element_aligned_tag) const { + _mm256_maskstore_ps(mem, _mm256_castps_si256(static_cast<__m256>(m_mask)), + static_cast<__m256>(m_value)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_to(float* mem, vector_aligned_tag) const { + _mm256_maskstore_ps(mem, _mm256_castps_si256(static_cast<__m256>(m_mask)), + static_cast<__m256>(m_value)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void scatter_to( + float* mem, + simd> const& index) const { + for (std::size_t lane = 0; lane < value_type::size(); ++lane) { + if (m_mask[lane]) mem[index[lane]] = m_value[lane]; + } + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION value_type const& + impl_get_value() const { + return m_value; + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION mask_type const& + impl_get_mask() const { + return m_mask; + } +}; + +template <> +class where_expression>, + simd>> + : public const_where_expression< + simd_mask>, + simd>> { + public: + where_expression( + simd_mask> const& mask_arg, + simd>& value_arg) + : const_where_expression(mask_arg, value_arg) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_from(float const* mem, element_aligned_tag) { + m_value = value_type(_mm256_maskload_ps( + mem, _mm256_castps_si256(static_cast<__m256>(m_mask)))); + } + void copy_from(float const* mem, vector_aligned_tag) { + m_value = value_type(_mm256_maskload_ps( + mem, _mm256_castps_si256(static_cast<__m256>(m_mask)))); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void gather_from( + float const* mem, + simd> const& index) { + m_value = value_type(_mm256_mask_i32gather_ps( + static_cast<__m256>(m_value), mem, static_cast<__m256i>(index), + static_cast<__m256>(m_mask), 4)); + } + template >>, + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void operator=(U&& x) { + auto const x_as_value_type = + static_cast>>( + std::forward(x)); + m_value = simd>(_mm256_blendv_ps( + static_cast<__m256>(m_value), static_cast<__m256>(x_as_value_type), + static_cast<__m256>(m_mask))); + } +}; + template <> class const_where_expression< simd_mask>, @@ -1923,6 +2683,109 @@ class where_expression>, } }; +template <> +class const_where_expression< + simd_mask>, + simd>> { + public: + using abi_type = simd_abi::avx2_fixed_size<8>; + using value_type = simd; + using mask_type = simd_mask; + + protected: + value_type& m_value; + mask_type const& m_mask; + + public: + const_where_expression(mask_type const& mask_arg, value_type const& value_arg) + : m_value(const_cast(value_arg)), m_mask(mask_arg) {} + + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_to(std::int32_t* mem, element_aligned_tag) const { + _mm256_maskstore_epi32(mem, static_cast<__m256i>(m_mask), + static_cast<__m256i>(m_value)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_to(std::int32_t* mem, vector_aligned_tag) const { + _mm256_maskstore_epi32(mem, static_cast<__m256i>(m_mask), + static_cast<__m256i>(m_value)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void scatter_to( + std::int32_t* mem, + simd> const& index) const { + for (std::size_t lane = 0; lane < value_type::size(); ++lane) { + if (m_mask[lane]) mem[index[lane]] = m_value[lane]; + } + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION value_type const& + impl_get_value() const { + return m_value; + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION mask_type const& + impl_get_mask() const { + return m_mask; + } +}; + +template <> +class where_expression>, + simd>> + : public const_where_expression< + simd_mask>, + simd>> { + public: + where_expression( + simd_mask> const& mask_arg, + simd>& value_arg) + : const_where_expression(mask_arg, value_arg) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_from(std::int32_t const* mem, element_aligned_tag) { +#ifdef KOKKOS_IMPL_WORKAROUND_ROCM_AVX2_ISSUE + __m256i tmp = _mm256_loadu_si256(reinterpret_cast<__m256i const*>(mem)); + m_value = value_type(_mm256_and_si256(tmp, static_cast<__m256i>(m_mask))); +#else + m_value = + value_type(_mm256_maskload_epi32(mem, static_cast<__m256i>(m_mask))); +#endif + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_from(std::int32_t const* mem, vector_aligned_tag) { +#ifdef KOKKOS_IMPL_WORKAROUND_ROCM_AVX2_ISSUE + __m256i tmp = _mm256_load_si256(reinterpret_cast<__m256i const*>(mem)); + m_value = value_type(_mm256_and_si256(tmp, static_cast<__m256i>(m_mask))); +#else + m_value = + value_type(_mm256_maskload_epi32(mem, static_cast<__m256i>(m_mask))); +#endif + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void gather_from( + std::int32_t const* mem, + simd> const& index) { + m_value = value_type(_mm256_mask_i32gather_epi32( + static_cast<__m256i>(m_value), mem, static_cast<__m256i>(index), + static_cast<__m256i>(m_mask), 4)); + } + template < + class U, + std::enable_if_t>>, + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void operator=(U&& x) { + auto const x_as_value_type = + static_cast>>( + std::forward(x)); + m_value = simd>( + _mm256_castps_si256(_mm256_blendv_ps( + _mm256_castsi256_ps(static_cast<__m256i>(m_value)), + _mm256_castsi256_ps(static_cast<__m256i>(x_as_value_type)), + _mm256_castsi256_ps(static_cast<__m256i>(m_mask))))); + } +}; + template <> class const_where_expression< simd_mask>, diff --git a/lib/kokkos/simd/src/Kokkos_SIMD_AVX512.hpp b/lib/kokkos/simd/src/Kokkos_SIMD_AVX512.hpp index 7fa35c204a..84e8af3cd7 100644 --- a/lib/kokkos/simd/src/Kokkos_SIMD_AVX512.hpp +++ b/lib/kokkos/simd/src/Kokkos_SIMD_AVX512.hpp @@ -140,6 +140,122 @@ class simd_mask> { } }; +template +class simd_mask> { + __mmask16 m_value; + + public: + class reference { + __mmask16& m_mask; + int m_lane; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION __mmask16 bit_mask() const { + return __mmask16(std::int32_t(1 << m_lane)); + } + + public: + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION reference(__mmask16& mask_arg, + int lane_arg) + : m_mask(mask_arg), m_lane(lane_arg) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION reference + operator=(bool value) const { + if (value) { + m_mask |= bit_mask(); + } else { + m_mask &= ~bit_mask(); + } + return *this; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION operator bool() const { + return (m_mask & bit_mask()) != 0; + } + }; + using value_type = bool; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask() = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION explicit simd_mask(value_type value) + : m_value(-std::int32_t(value)) {} + template + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask( + simd_mask> const& other) + : m_value(static_cast<__mmask16>(other)) {} + template >, + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask(G&& gen) : m_value(false) { + reference(m_value, int(0)) = + static_cast(gen(std::integral_constant())); + reference(m_value, int(1)) = + static_cast(gen(std::integral_constant())); + reference(m_value, int(2)) = + static_cast(gen(std::integral_constant())); + reference(m_value, int(3)) = + static_cast(gen(std::integral_constant())); + reference(m_value, int(4)) = + static_cast(gen(std::integral_constant())); + reference(m_value, int(5)) = + static_cast(gen(std::integral_constant())); + reference(m_value, int(6)) = + static_cast(gen(std::integral_constant())); + reference(m_value, int(7)) = + static_cast(gen(std::integral_constant())); + reference(m_value, int(8)) = + static_cast(gen(std::integral_constant())); + reference(m_value, int(9)) = + static_cast(gen(std::integral_constant())); + reference(m_value, int(10)) = + static_cast(gen(std::integral_constant())); + reference(m_value, int(11)) = + static_cast(gen(std::integral_constant())); + reference(m_value, int(12)) = + static_cast(gen(std::integral_constant())); + reference(m_value, int(13)) = + static_cast(gen(std::integral_constant())); + reference(m_value, int(14)) = + static_cast(gen(std::integral_constant())); + reference(m_value, int(15)) = + static_cast(gen(std::integral_constant())); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION static constexpr std::size_t size() { + return 16; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit simd_mask( + __mmask16 const& value_in) + : m_value(value_in) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit operator __mmask16() + const { + return m_value; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION reference operator[](std::size_t i) { + return reference(m_value, int(i)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION value_type + operator[](std::size_t i) const { + auto const bit_mask = __mmask16(std::int32_t(1 << i)); + return (m_value & bit_mask) != 0; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask + operator||(simd_mask const& other) const { + return simd_mask(_kor_mask16(m_value, other.m_value)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask + operator&&(simd_mask const& other) const { + return simd_mask(_kand_mask16(m_value, other.m_value)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask operator!() const { + static const __mmask16 true_value(static_cast<__mmask16>(simd_mask(true))); + return simd_mask(_kxor_mask16(true_value, m_value)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION bool operator==( + simd_mask const& other) const { + return m_value == other.m_value; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION bool operator!=( + simd_mask const& other) const { + return m_value != other.m_value; + } +}; + template <> class simd> { __m512d m_value; @@ -700,6 +816,280 @@ simd> condition( static_cast<__m256>(b))); } +template <> +class simd> { + __m512 m_value; + + public: + using value_type = float; + using abi_type = simd_abi::avx512_fixed_size<16>; + using mask_type = simd_mask; + using reference = value_type&; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd() = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(simd const&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(simd&&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd& operator=(simd const&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd& operator=(simd&&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION static constexpr std::size_t size() { + return 16; + } + template , + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(U&& value) + : m_value(_mm512_set1_ps(value_type(value))) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit simd( + __m512 const& value_in) + : m_value(value_in) {} + template >, + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(G&& gen) + : m_value( + _mm512_setr_ps(gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()))) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION reference operator[](std::size_t i) { + return reinterpret_cast(&m_value)[i]; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION value_type + operator[](std::size_t i) const { + return reinterpret_cast(&m_value)[i]; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_from(value_type const* ptr, + element_aligned_tag) { + m_value = _mm512_loadu_ps(ptr); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_from(value_type const* ptr, + vector_aligned_tag) { + m_value = _mm512_load_ps(ptr); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_to( + value_type* ptr, element_aligned_tag) const { + _mm512_storeu_ps(ptr, m_value); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_to(value_type* ptr, + vector_aligned_tag) const { + _mm512_store_ps(ptr, m_value); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit operator __m512() + const { + return m_value; + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd operator-() const + noexcept { + return simd(_mm512_sub_ps(_mm512_set1_ps(0.0), m_value)); + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator*( + simd const& lhs, simd const& rhs) noexcept { + return simd(_mm512_mul_ps(lhs.m_value, rhs.m_value)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator/( + simd const& lhs, simd const& rhs) noexcept { + return simd(_mm512_div_ps(lhs.m_value, rhs.m_value)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator+( + simd const& lhs, simd const& rhs) noexcept { + return simd(_mm512_add_ps(lhs.m_value, rhs.m_value)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator-( + simd const& lhs, simd const& rhs) noexcept { + return simd(_mm512_sub_ps(lhs.m_value, rhs.m_value)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator<(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm512_cmp_ps_mask(lhs.m_value, rhs.m_value, _CMP_LT_OS)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator>(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm512_cmp_ps_mask(lhs.m_value, rhs.m_value, _CMP_GT_OS)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator<=(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm512_cmp_ps_mask(lhs.m_value, rhs.m_value, _CMP_LE_OS)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator>=(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm512_cmp_ps_mask(lhs.m_value, rhs.m_value, _CMP_GE_OS)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator==(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm512_cmp_ps_mask(lhs.m_value, rhs.m_value, _CMP_EQ_OS)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator!=(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm512_cmp_ps_mask(lhs.m_value, rhs.m_value, _CMP_NEQ_OS)); + } +}; + +} // namespace Experimental + +KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION +Experimental::simd> +copysign(Experimental::simd< + float, Experimental::simd_abi::avx512_fixed_size<16>> const& a, + Experimental::simd< + float, Experimental::simd_abi::avx512_fixed_size<16>> const& b) { + __m512 const sign_mask = _mm512_set1_ps(-0.0); + return Experimental::simd>( + _mm512_xor_ps(_mm512_andnot_ps(sign_mask, static_cast<__m512>(a)), + _mm512_and_ps(sign_mask, static_cast<__m512>(b)))); +} + +KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION +Experimental::simd> abs( + Experimental::simd< + float, Experimental::simd_abi::avx512_fixed_size<16>> const& a) { + __m512 const sign_mask = _mm512_set1_ps(-0.0); + return Experimental::simd>( + _mm512_andnot_ps(sign_mask, static_cast<__m512>(a))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + floor(Experimental::simd< + float, Experimental::simd_abi::avx512_fixed_size<16>> const& a) { + __m512 const val = static_cast<__m512>(a); + return Experimental::simd>( + _mm512_roundscale_ps(val, _MM_FROUND_TO_NEG_INF)); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + ceil(Experimental::simd< + float, Experimental::simd_abi::avx512_fixed_size<16>> const& a) { + __m512 const val = static_cast<__m512>(a); + return Experimental::simd>( + _mm512_roundscale_ps(val, _MM_FROUND_TO_POS_INF)); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + round(Experimental::simd< + float, Experimental::simd_abi::avx512_fixed_size<16>> const& a) { + __m512 const val = static_cast<__m512>(a); + return Experimental::simd>( + _mm512_roundscale_ps(val, _MM_FROUND_TO_NEAREST_INT)); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + trunc(Experimental::simd< + float, Experimental::simd_abi::avx512_fixed_size<16>> const& a) { + __m512 const val = static_cast<__m512>(a); + return Experimental::simd>( + _mm512_roundscale_ps(val, _MM_FROUND_TO_ZERO)); +} + +KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION +Experimental::simd> sqrt( + Experimental::simd< + float, Experimental::simd_abi::avx512_fixed_size<16>> const& a) { + return Experimental::simd>( + _mm512_sqrt_ps(static_cast<__m512>(a))); +} + +#ifdef __INTEL_COMPILER + +KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION +Experimental::simd> cbrt( + Experimental::simd< + float, Experimental::simd_abi::avx512_fixed_size<16>> const& a) { + return Experimental::simd>( + _mm512_cbrt_ps(static_cast<__m512>(a))); +} + +KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION +Experimental::simd> exp( + Experimental::simd< + float, Experimental::simd_abi::avx512_fixed_size<16>> const& a) { + return Experimental::simd>( + _mm512_exp_ps(static_cast<__m512>(a))); +} + +KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION +Experimental::simd> log( + Experimental::simd< + float, Experimental::simd_abi::avx512_fixed_size<16>> const& a) { + return Experimental::simd>( + _mm512_log_ps(static_cast<__m512>(a))); +} + +#endif + +KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION +Experimental::simd> fma( + Experimental::simd> const& a, + Experimental::simd> const& b, + Experimental::simd< + float, Experimental::simd_abi::avx512_fixed_size<16>> const& c) { + return Experimental::simd>( + _mm512_fmadd_ps(static_cast<__m512>(a), static_cast<__m512>(b), + static_cast<__m512>(c))); +} + +KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION +Experimental::simd> max( + Experimental::simd> const& a, + Experimental::simd< + float, Experimental::simd_abi::avx512_fixed_size<16>> const& b) { + return Experimental::simd>( + _mm512_max_ps(static_cast<__m512>(a), static_cast<__m512>(b))); +} + +KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION +Experimental::simd> min( + Experimental::simd> const& a, + Experimental::simd< + float, Experimental::simd_abi::avx512_fixed_size<16>> const& b) { + return Experimental::simd>( + _mm512_min_ps(static_cast<__m512>(a), static_cast<__m512>(b))); +} + +namespace Experimental { + +KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION +simd> condition( + simd_mask> const& a, + simd> const& b, + simd> const& c) { + return simd>( + _mm512_mask_blend_ps(static_cast<__mmask16>(a), static_cast<__m512>(c), + static_cast<__m512>(b))); +} + template <> class simd> { __m256i m_value; @@ -907,6 +1297,222 @@ namespace Experimental { static_cast<__m256i>(b))); } +template <> +class simd> { + __m512i m_value; + + public: + using value_type = std::int32_t; + using abi_type = simd_abi::avx512_fixed_size<16>; + using mask_type = simd_mask; + using reference = value_type&; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd() = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(simd const&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(simd&&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd& operator=(simd const&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd& operator=(simd&&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION static constexpr std::size_t size() { + return 16; + } + template , + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(U&& value) + : m_value(_mm512_set1_epi32(value_type(value))) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit simd( + __m512i const& value_in) + : m_value(value_in) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION explicit simd( + simd const& other); + template ()); } + std::is_invocable_r_v>, + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit simd( + G&& gen) noexcept + : m_value(_mm512_setr_epi32( + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()))) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION reference operator[](std::size_t i) { + return reinterpret_cast(&m_value)[i]; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION value_type + operator[](std::size_t i) const { + return reinterpret_cast(&m_value)[i]; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_to( + value_type* ptr, element_aligned_tag) const { + _mm512_mask_storeu_epi32(ptr, static_cast<__mmask16>(mask_type(true)), + m_value); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_to(value_type* ptr, + vector_aligned_tag) const { + _mm512_mask_store_epi32(ptr, static_cast<__mmask16>(mask_type(true)), + m_value); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_from(value_type const* ptr, + element_aligned_tag) { + m_value = _mm512_mask_loadu_epi32( + _mm512_set1_epi32(0), static_cast<__mmask16>(mask_type(true)), ptr); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_from(value_type const* ptr, + vector_aligned_tag) { + m_value = _mm512_mask_load_epi32( + _mm512_set1_epi32(0), static_cast<__mmask16>(mask_type(true)), ptr); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit operator __m512i() + const { + return m_value; + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd operator-() const + noexcept { + return simd(_mm512_sub_epi32(_mm512_set1_epi32(0), m_value)); + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator*( + simd const& lhs, simd const& rhs) noexcept { + return simd(_mm512_mullo_epi32(static_cast<__m512i>(lhs), + static_cast<__m512i>(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator+( + simd const& lhs, simd const& rhs) noexcept { + return simd>( + _mm512_add_epi32(static_cast<__m512i>(lhs), static_cast<__m512i>(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator-( + simd const& lhs, simd const& rhs) noexcept { + return simd>( + _mm512_sub_epi32(static_cast<__m512i>(lhs), static_cast<__m512i>(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator<(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm512_cmplt_epi32_mask(static_cast<__m512i>(lhs), + static_cast<__m512i>(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator>(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm512_cmplt_epi32_mask(static_cast<__m512i>(rhs), + static_cast<__m512i>(lhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator<=(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm512_cmple_epi32_mask(static_cast<__m512i>(lhs), + static_cast<__m512i>(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator>=(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm512_cmple_epi32_mask(static_cast<__m512i>(rhs), + static_cast<__m512i>(lhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator==(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm512_cmpeq_epi32_mask(static_cast<__m512i>(lhs), + static_cast<__m512i>(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator!=(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm512_cmpneq_epi32_mask(static_cast<__m512i>(lhs), + static_cast<__m512i>(rhs))); + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator>>( + simd const& lhs, int rhs) noexcept { + return simd(_mm512_srai_epi32(static_cast<__m512i>(lhs), rhs)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator>>( + simd const& lhs, simd const& rhs) noexcept { + return simd(_mm512_srav_epi32(static_cast<__m512i>(lhs), + static_cast<__m512i>(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator<<( + simd const& lhs, int rhs) noexcept { + return simd(_mm512_slli_epi32(static_cast<__m512i>(lhs), rhs)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator<<( + simd const& lhs, simd const& rhs) noexcept { + return simd(_mm512_sllv_epi32(static_cast<__m512i>(lhs), + static_cast<__m512i>(rhs))); + } +}; + +} // namespace Experimental + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION Experimental::simd< + std::int32_t, Experimental::simd_abi::avx512_fixed_size<16>> +abs(Experimental::simd< + std::int32_t, Experimental::simd_abi::avx512_fixed_size<16>> const& a) { + __m512i const rhs = static_cast<__m512i>(a); + return Experimental::simd>( + _mm512_abs_epi32(rhs)); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION Experimental::simd< + float, Experimental::simd_abi::avx512_fixed_size<16>> +floor(Experimental::simd< + std::int32_t, Experimental::simd_abi::avx512_fixed_size<16>> const& a) { + return Experimental::simd>( + _mm512_cvtepi32_ps(static_cast<__m512i>(a))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION Experimental::simd< + float, Experimental::simd_abi::avx512_fixed_size<16>> +ceil(Experimental::simd< + std::int32_t, Experimental::simd_abi::avx512_fixed_size<16>> const& a) { + return Experimental::simd>( + _mm512_cvtepi32_ps(static_cast<__m512i>(a))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION Experimental::simd< + float, Experimental::simd_abi::avx512_fixed_size<16>> +round(Experimental::simd< + std::int32_t, Experimental::simd_abi::avx512_fixed_size<16>> const& a) { + return Experimental::simd>( + _mm512_cvtepi32_ps(static_cast<__m512i>(a))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION Experimental::simd< + float, Experimental::simd_abi::avx512_fixed_size<16>> +trunc(Experimental::simd< + std::int32_t, Experimental::simd_abi::avx512_fixed_size<16>> const& a) { + return Experimental::simd>( + _mm512_cvtepi32_ps(static_cast<__m512i>(a))); +} + +namespace Experimental { + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + simd> + condition(simd_mask> const& a, + simd> const& b, + simd> const& c) { + return simd>( + _mm512_mask_blend_epi32(static_cast<__mmask16>(a), + static_cast<__m512i>(c), + static_cast<__m512i>(b))); +} + template <> class simd> { __m256i m_value; @@ -960,16 +1566,6 @@ class simd> { operator[](std::size_t i) const { return reinterpret_cast(&m_value)[i]; } - KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_from(value_type const* ptr, - element_aligned_tag) { - m_value = _mm256_mask_loadu_epi32( - _mm256_set1_epi32(0), static_cast<__mmask8>(mask_type(true)), ptr); - } - KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_from(value_type const* ptr, - vector_aligned_tag) { - m_value = _mm256_mask_load_epi32( - _mm256_set1_epi32(0), static_cast<__mmask8>(mask_type(true)), ptr); - } KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_to( value_type* ptr, element_aligned_tag) const { _mm256_mask_storeu_epi32(ptr, static_cast<__mmask8>(mask_type(true)), @@ -980,10 +1576,21 @@ class simd> { _mm256_mask_store_epi32(ptr, static_cast<__mmask8>(mask_type(true)), m_value); } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_from(value_type const* ptr, + element_aligned_tag) { + m_value = _mm256_mask_loadu_epi32( + _mm256_set1_epi32(0), static_cast<__mmask8>(mask_type(true)), ptr); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_from(value_type const* ptr, + vector_aligned_tag) { + m_value = _mm256_mask_load_epi32( + _mm256_set1_epi32(0), static_cast<__mmask8>(mask_type(true)), ptr); + } KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit operator __m256i() const { return m_value; } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator*( simd const& lhs, simd const& rhs) noexcept { return simd(_mm256_mullo_epi32(static_cast<__m256i>(lhs), @@ -1108,6 +1715,217 @@ namespace Experimental { static_cast<__m256i>(b))); } +template <> +class simd> { + __m512i m_value; + + public: + using value_type = std::uint32_t; + using abi_type = simd_abi::avx512_fixed_size<16>; + using mask_type = simd_mask; + using reference = value_type&; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd() = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(simd const&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(simd&&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd& operator=(simd const&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd& operator=(simd&&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION static constexpr std::size_t size() { + return 16; + } + template , + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(U&& value) + : m_value(_mm512_set1_epi32( + Kokkos::bit_cast(value_type(value)))) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit simd( + __m512i const& value_in) + : m_value(value_in) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION explicit simd( + simd> const& other) + : m_value(static_cast<__m512i>(other)) {} + template ()); } + std::is_invocable_r_v>, + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit simd( + G&& gen) noexcept + : m_value(_mm512_setr_epi32( + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()), + gen(std::integral_constant()))) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION reference operator[](std::size_t i) { + return reinterpret_cast(&m_value)[i]; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION value_type + operator[](std::size_t i) const { + return reinterpret_cast(&m_value)[i]; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_from(value_type const* ptr, + element_aligned_tag) { + m_value = _mm512_mask_loadu_epi32( + _mm512_set1_epi32(0), static_cast<__mmask16>(mask_type(true)), ptr); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_from(value_type const* ptr, + vector_aligned_tag) { + m_value = _mm512_mask_load_epi32( + _mm512_set1_epi32(0), static_cast<__mmask16>(mask_type(true)), ptr); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_to( + value_type* ptr, element_aligned_tag) const { + _mm512_mask_storeu_epi32(ptr, static_cast<__mmask16>(mask_type(true)), + m_value); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_to(value_type* ptr, + vector_aligned_tag) const { + _mm512_mask_store_epi32(ptr, static_cast<__mmask16>(mask_type(true)), + m_value); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit operator __m512i() + const { + return m_value; + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator*( + simd const& lhs, simd const& rhs) noexcept { + return simd(_mm512_mullo_epi32(static_cast<__m512i>(lhs), + static_cast<__m512i>(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator+( + simd const& lhs, simd const& rhs) noexcept { + return simd( + _mm512_add_epi32(static_cast<__m512i>(lhs), static_cast<__m512i>(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator-( + simd const& lhs, simd const& rhs) noexcept { + return simd( + _mm512_sub_epi32(static_cast<__m512i>(lhs), static_cast<__m512i>(rhs))); + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator<(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm512_cmplt_epu32_mask(static_cast<__m512i>(lhs), + static_cast<__m512i>(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator>(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm512_cmplt_epu32_mask(static_cast<__m512i>(rhs), + static_cast<__m512i>(lhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator<=(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm512_cmple_epu32_mask(static_cast<__m512i>(lhs), + static_cast<__m512i>(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator>=(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm512_cmple_epu32_mask(static_cast<__m512i>(rhs), + static_cast<__m512i>(lhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator==(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm512_cmpeq_epu32_mask(static_cast<__m512i>(lhs), + static_cast<__m512i>(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator!=(simd const& lhs, simd const& rhs) noexcept { + return mask_type(_mm512_cmpneq_epu32_mask(static_cast<__m512i>(lhs), + static_cast<__m512i>(rhs))); + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator>>( + simd const& lhs, int rhs) noexcept { + return simd(_mm512_srli_epi32(static_cast<__m512i>(lhs), rhs)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator>>( + simd const& lhs, simd const& rhs) noexcept { + return simd(_mm512_srlv_epi32(static_cast<__m512i>(lhs), + static_cast<__m512i>(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator<<( + simd const& lhs, int rhs) noexcept { + return simd(_mm512_slli_epi32(static_cast<__m512i>(lhs), rhs)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator<<( + simd const& lhs, simd const& rhs) noexcept { + return simd(_mm512_sllv_epi32(static_cast<__m512i>(lhs), + static_cast<__m512i>(rhs))); + } +}; + +} // namespace Experimental + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION Experimental::simd< + std::uint32_t, Experimental::simd_abi::avx512_fixed_size<16>> +abs(Experimental::simd< + std::uint32_t, Experimental::simd_abi::avx512_fixed_size<16>> const& a) { + return a; +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION Experimental::simd< + float, Experimental::simd_abi::avx512_fixed_size<16>> +floor(Experimental::simd< + std::uint32_t, Experimental::simd_abi::avx512_fixed_size<16>> const& a) { + return Experimental::simd>( + _mm512_cvtepu32_ps(static_cast<__m512i>(a))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION Experimental::simd< + float, Experimental::simd_abi::avx512_fixed_size<16>> +ceil(Experimental::simd< + std::uint32_t, Experimental::simd_abi::avx512_fixed_size<16>> const& a) { + return Experimental::simd>( + _mm512_cvtepu32_ps(static_cast<__m512i>(a))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION Experimental::simd< + float, Experimental::simd_abi::avx512_fixed_size<16>> +round(Experimental::simd< + std::uint32_t, Experimental::simd_abi::avx512_fixed_size<16>> const& a) { + return Experimental::simd>( + _mm512_cvtepu32_ps(static_cast<__m512i>(a))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION Experimental::simd< + float, Experimental::simd_abi::avx512_fixed_size<16>> +trunc(Experimental::simd< + std::uint32_t, Experimental::simd_abi::avx512_fixed_size<16>> const& a) { + return Experimental::simd>( + _mm512_cvtepu32_ps(static_cast<__m512i>(a))); +} + +namespace Experimental { + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + simd> + condition( + simd_mask> const& a, + simd> const& b, + simd> const& c) { + return simd>( + _mm512_mask_blend_epi32(static_cast<__mmask16>(a), + static_cast<__m512i>(c), + static_cast<__m512i>(b))); +} + template <> class simd> { __m512i m_value; @@ -1716,6 +2534,95 @@ class where_expression>, } }; +template <> +class const_where_expression>, + simd>> { + public: + using abi_type = simd_abi::avx512_fixed_size<16>; + using value_type = simd; + using mask_type = simd_mask; + + protected: + value_type& m_value; + mask_type const& m_mask; + + public: + const_where_expression(mask_type const& mask_arg, value_type const& value_arg) + : m_value(const_cast(value_arg)), m_mask(mask_arg) {} + + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_to(float* mem, element_aligned_tag) const { + _mm512_mask_storeu_ps(mem, static_cast<__mmask16>(m_mask), + static_cast<__m512>(m_value)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_to(float* mem, vector_aligned_tag) const { + _mm512_mask_store_ps(mem, static_cast<__mmask16>(m_mask), + static_cast<__m512>(m_value)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void scatter_to( + float* mem, + simd> const& index) const { + _mm512_mask_i32scatter_ps(mem, static_cast<__mmask16>(m_mask), + static_cast<__m512i>(index), + static_cast<__m512>(m_value), 4); + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION value_type const& + impl_get_value() const { + return m_value; + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION mask_type const& + impl_get_mask() const { + return m_mask; + } +}; + +template <> +class where_expression>, + simd>> + : public const_where_expression< + simd_mask>, + simd>> { + public: + where_expression( + simd_mask> const& mask_arg, + simd>& value_arg) + : const_where_expression(mask_arg, value_arg) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_from(float const* mem, element_aligned_tag) { + m_value = value_type(_mm512_mask_loadu_ps( + _mm512_set1_ps(0.0), static_cast<__mmask16>(m_mask), mem)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_from(float const* mem, vector_aligned_tag) { + m_value = value_type(_mm512_mask_load_ps( + _mm512_set1_ps(0.0), static_cast<__mmask16>(m_mask), mem)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void gather_from( + float const* mem, + simd> const& index) { + m_value = value_type(_mm512_mask_i32gather_ps( + static_cast<__m512>(m_value), static_cast<__mmask16>(m_mask), + static_cast<__m512i>(index), mem, 4)); + } + template >>, + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void operator=(U&& x) { + auto const x_as_value_type = + static_cast>>( + std::forward(x)); + m_value = simd>(_mm512_mask_blend_ps( + static_cast<__mmask16>(m_mask), static_cast<__m512>(m_value), + static_cast<__m512>(x_as_value_type))); + } +}; + template <> class const_where_expression< simd_mask>, @@ -1810,6 +2717,98 @@ class where_expression>, } }; +template <> +class const_where_expression< + simd_mask>, + simd>> { + public: + using abi_type = simd_abi::avx512_fixed_size<16>; + using value_type = simd; + using mask_type = simd_mask; + + protected: + value_type& m_value; + mask_type const& m_mask; + + public: + const_where_expression(mask_type const& mask_arg, value_type const& value_arg) + : m_value(const_cast(value_arg)), m_mask(mask_arg) {} + + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_to(std::int32_t* mem, element_aligned_tag) const { + _mm512_mask_storeu_epi32(mem, static_cast<__mmask16>(m_mask), + static_cast<__m512i>(m_value)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_to(std::int32_t* mem, vector_aligned_tag) const { + _mm512_mask_store_epi32(mem, static_cast<__mmask16>(m_mask), + static_cast<__m512i>(m_value)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void scatter_to( + std::int32_t* mem, + simd> const& index) const { + _mm512_mask_i32scatter_epi32(mem, static_cast<__mmask16>(m_mask), + static_cast<__m512i>(index), + static_cast<__m512i>(m_value), 4); + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION value_type const& + impl_get_value() const { + return m_value; + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION mask_type const& + impl_get_mask() const { + return m_mask; + } +}; + +template <> +class where_expression>, + simd>> + : public const_where_expression< + simd_mask>, + simd>> { + public: + where_expression( + simd_mask> const& mask_arg, + simd>& value_arg) + : const_where_expression(mask_arg, value_arg) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_from(std::int32_t const* mem, element_aligned_tag) { + m_value = value_type(_mm512_mask_loadu_epi32( + _mm512_set1_epi32(0), static_cast<__mmask16>(m_mask), mem)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_from(std::int32_t const* mem, vector_aligned_tag) { + m_value = value_type(_mm512_mask_load_epi32( + _mm512_set1_epi32(0), static_cast<__mmask16>(m_mask), mem)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void gather_from( + std::int32_t const* mem, + simd> const& index) { + m_value = value_type(_mm512_mask_i32gather_epi32( + static_cast<__m512i>(m_value), static_cast<__mmask16>(m_mask), + static_cast<__m512i>(index), mem, 4)); + } + template >>, + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void operator=(U&& x) { + auto const x_as_value_type = + static_cast>>( + std::forward(x)); + m_value = simd>( + _mm512_mask_blend_epi32(static_cast<__mmask16>(m_mask), + static_cast<__m512i>(m_value), + static_cast<__m512i>(x_as_value_type))); + } +}; + template <> class const_where_expression< simd_mask>, @@ -1905,6 +2904,99 @@ class where_expression>, } }; +template <> +class const_where_expression< + simd_mask>, + simd>> { + public: + using abi_type = simd_abi::avx512_fixed_size<16>; + using value_type = simd; + using mask_type = simd_mask; + + protected: + value_type& m_value; + mask_type const& m_mask; + + public: + const_where_expression(mask_type const& mask_arg, value_type const& value_arg) + : m_value(const_cast(value_arg)), m_mask(mask_arg) {} + + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_to(std::uint32_t* mem, element_aligned_tag) const { + _mm512_mask_storeu_epi32(mem, static_cast<__mmask16>(m_mask), + static_cast<__m512i>(m_value)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_to(std::uint32_t* mem, vector_aligned_tag) const { + _mm512_mask_store_epi32(mem, static_cast<__mmask16>(m_mask), + static_cast<__m512i>(m_value)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void scatter_to( + std::uint32_t* mem, + simd> const& index) const { + _mm512_mask_i32scatter_epi32(mem, static_cast<__mmask16>(m_mask), + static_cast<__m512i>(index), + static_cast<__m512i>(m_value), 4); + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION value_type const& + impl_get_value() const { + return m_value; + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION mask_type const& + impl_get_mask() const { + return m_mask; + } +}; + +template <> +class where_expression< + simd_mask>, + simd>> + : public const_where_expression< + simd_mask>, + simd>> { + public: + where_expression( + simd_mask> const& mask_arg, + simd>& value_arg) + : const_where_expression(mask_arg, value_arg) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_from(std::uint32_t const* mem, element_aligned_tag) { + m_value = value_type(_mm512_mask_loadu_epi32( + _mm512_set1_epi32(0), static_cast<__mmask16>(m_mask), mem)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_from(std::uint32_t const* mem, vector_aligned_tag) { + m_value = value_type(_mm512_mask_load_epi32( + _mm512_set1_epi32(0), static_cast<__mmask16>(m_mask), mem)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void gather_from( + std::uint32_t const* mem, + simd> const& index) { + m_value = value_type(_mm512_mask_i32gather_epi32( + static_cast<__m512i>(m_value), static_cast<__mmask16>(m_mask), + static_cast<__m512i>(index), mem, 4)); + } + template >>, + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void operator=(U&& x) { + auto const x_as_value_type = + static_cast>>( + std::forward(x)); + m_value = simd>( + _mm512_mask_blend_epi32(static_cast<__mmask16>(m_mask), + static_cast<__m512i>(m_value), + static_cast<__m512i>(x_as_value_type))); + } +}; + template <> class const_where_expression< simd_mask>, diff --git a/lib/kokkos/simd/src/Kokkos_SIMD_NEON.hpp b/lib/kokkos/simd/src/Kokkos_SIMD_NEON.hpp index efc81135d1..8cb0cc75fc 100644 --- a/lib/kokkos/simd/src/Kokkos_SIMD_NEON.hpp +++ b/lib/kokkos/simd/src/Kokkos_SIMD_NEON.hpp @@ -42,11 +42,11 @@ class neon_fixed_size {}; namespace Impl { -template +template class neon_mask; template -class neon_mask { +class neon_mask { uint64x2_t m_value; public: @@ -104,12 +104,13 @@ class neon_mask { } template KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION neon_mask( - neon_mask const& other) { + neon_mask const& other) { operator[](0) = bool(other[0]); operator[](1) = bool(other[1]); } template - KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION neon_mask(neon_mask const& other) + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION neon_mask( + neon_mask const& other) : neon_mask(static_cast(other)) {} KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION static constexpr std::size_t size() { return 2; @@ -158,7 +159,7 @@ class neon_mask { }; template -class neon_mask { +class neon_mask { uint32x2_t m_value; public: @@ -211,10 +212,12 @@ class neon_mask { m_value, 1); } template - KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION neon_mask(neon_mask const& other) + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION neon_mask( + neon_mask const& other) : m_value(vqmovn_u64(static_cast(other))) {} template - KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION neon_mask(neon_mask const& other) + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION neon_mask( + neon_mask const& other) : m_value(static_cast(other)) {} KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION static constexpr std::size_t size() { return 2; @@ -260,14 +263,125 @@ class neon_mask { } }; +template +class neon_mask { + uint32x4_t m_value; + + public: + class reference { + uint32x4_t& m_mask; + int m_lane; + + public: + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION reference(uint32x4_t& mask_arg, + int lane_arg) + : m_mask(mask_arg), m_lane(lane_arg) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION reference + operator=(bool value) const { + switch (m_lane) { + case 0: + m_mask = vsetq_lane_u32(value ? 0xFFFFFFFFU : 0, m_mask, 0); + break; + case 1: + m_mask = vsetq_lane_u32(value ? 0xFFFFFFFFU : 0, m_mask, 1); + break; + case 2: + m_mask = vsetq_lane_u32(value ? 0xFFFFFFFFU : 0, m_mask, 2); + break; + case 3: + m_mask = vsetq_lane_u32(value ? 0xFFFFFFFFU : 0, m_mask, 3); + break; + } + return *this; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION operator bool() const { + switch (m_lane) { + case 0: return vgetq_lane_u32(m_mask, 0) != 0; + case 1: return vgetq_lane_u32(m_mask, 1) != 0; + case 2: return vgetq_lane_u32(m_mask, 2) != 0; + case 3: return vgetq_lane_u32(m_mask, 3) != 0; + } + return false; + } + }; + using value_type = bool; + using abi_type = simd_abi::neon_fixed_size<4>; + using implementation_type = uint32x4_t; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION neon_mask() = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION explicit neon_mask(value_type value) + : m_value(vmovq_n_u32(value ? 0xFFFFFFFFU : 0)) {} + template >, + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit neon_mask( + G&& gen) noexcept { + m_value = vsetq_lane_u32( + (gen(std::integral_constant()) ? 0xFFFFFFFFU : 0), + m_value, 0); + m_value = vsetq_lane_u32( + (gen(std::integral_constant()) ? 0xFFFFFFFFU : 0), + m_value, 1); + m_value = vsetq_lane_u32( + (gen(std::integral_constant()) ? 0xFFFFFFFFU : 0), + m_value, 2); + m_value = vsetq_lane_u32( + (gen(std::integral_constant()) ? 0xFFFFFFFFU : 0), + m_value, 3); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION static constexpr std::size_t size() { + return 4; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit neon_mask( + uint32x4_t const& value_in) + : m_value(value_in) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit operator uint32x4_t() + const { + return m_value; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION reference operator[](std::size_t i) { + return reference(m_value, int(i)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION value_type + operator[](std::size_t i) const { + return static_cast( + reference(const_cast(m_value), int(i))); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION Derived + operator||(neon_mask const& other) const { + return Derived(vorrq_u32(m_value, other.m_value)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION Derived + operator&&(neon_mask const& other) const { + return Derived(vandq_u32(m_value, other.m_value)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION Derived operator!() const { + auto const true_value = static_cast(neon_mask(true)); + return Derived(veorq_u32(m_value, true_value)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION bool operator==( + neon_mask const& other) const { + uint32x4_t const elementwise_equality = vceqq_u32(m_value, other.m_value); + uint64x2_t const overall_equality_neon = + vreinterpretq_u64_u32(elementwise_equality); + return (overall_equality_neon[0] == 0xFFFFFFFFFFFFFFFFULL) && + (overall_equality_neon[1] == 0xFFFFFFFFFFFFFFFFULL); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION bool operator!=( + neon_mask const& other) const { + return !operator==(other); + } +}; + } // namespace Impl template class simd_mask> : public Impl::neon_mask>, - sizeof(T) * 8> { + sizeof(T) * 8, 2> { using base_type = Impl::neon_mask>, - sizeof(T) * 8>; + sizeof(T) * 8, 2>; public: using implementation_type = typename base_type::implementation_type; @@ -291,6 +405,35 @@ class simd_mask> : base_type(gen) {} }; +template +class simd_mask> + : public Impl::neon_mask>, + sizeof(T) * 8, 4> { + using base_type = Impl::neon_mask>, + sizeof(T) * 8, 4>; + + public: + using implementation_type = typename base_type::implementation_type; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask() = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION explicit simd_mask(bool value) + : base_type(value) {} + template + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask( + simd_mask> const& other) + : base_type(other) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit simd_mask( + implementation_type const& value) + : base_type(value) {} + template >, + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit simd_mask( + G&& gen) noexcept + : base_type(gen) {} +}; + template <> class simd> { float64x2_t m_value; @@ -788,6 +931,256 @@ namespace Experimental { static_cast(c))); } +template <> +class simd> { + float32x4_t m_value; + + public: + using value_type = float; + using abi_type = simd_abi::neon_fixed_size<4>; + using mask_type = simd_mask; + class reference { + float32x4_t& m_value; + int m_lane; + + public: + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION reference(float32x4_t& value_arg, + int lane_arg) + : m_value(value_arg), m_lane(lane_arg) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION reference + operator=(float value) const { + switch (m_lane) { + case 0: m_value = vsetq_lane_f32(value, m_value, 0); break; + case 1: m_value = vsetq_lane_f32(value, m_value, 1); break; + case 2: m_value = vsetq_lane_f32(value, m_value, 2); break; + case 3: m_value = vsetq_lane_f32(value, m_value, 3); break; + } + return *this; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION operator float() const { + switch (m_lane) { + case 0: return vgetq_lane_f32(m_value, 0); + case 1: return vgetq_lane_f32(m_value, 1); + case 2: return vgetq_lane_f32(m_value, 2); + case 3: return vgetq_lane_f32(m_value, 3); + } + return 0; + } + }; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd() = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(simd const&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(simd&&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd& operator=(simd const&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd& operator=(simd&&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION static constexpr std::size_t size() { + return 4; + } + template , + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(U&& value) + : m_value(vmovq_n_f32(value_type(value))) {} + template >, + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(G&& gen) { + m_value = vsetq_lane_f32(gen(std::integral_constant()), + m_value, 0); + m_value = vsetq_lane_f32(gen(std::integral_constant()), + m_value, 1); + m_value = vsetq_lane_f32(gen(std::integral_constant()), + m_value, 2); + m_value = vsetq_lane_f32(gen(std::integral_constant()), + m_value, 3); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit simd( + float32x4_t const& value_in) + : m_value(value_in) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION reference operator[](std::size_t i) { + return reference(m_value, int(i)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION value_type + operator[](std::size_t i) const { + return reference(const_cast(this)->m_value, int(i)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_from(value_type const* ptr, + element_aligned_tag) { + m_value = vld1q_f32(ptr); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_from(value_type const* ptr, + vector_aligned_tag) { + m_value = vld1q_f32(ptr); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_to( + value_type* ptr, element_aligned_tag) const { + vst1q_f32(ptr, m_value); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_to(value_type* ptr, + vector_aligned_tag) const { + vst1q_f32(ptr, m_value); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit + operator float32x4_t() const { + return m_value; + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd operator-() const + noexcept { + return simd(vnegq_f32(m_value)); + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator*( + simd const& lhs, simd const& rhs) noexcept { + return simd(vmulq_f32(lhs.m_value, rhs.m_value)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator/( + simd const& lhs, simd const& rhs) noexcept { + return simd(vdivq_f32(lhs.m_value, rhs.m_value)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator+( + simd const& lhs, simd const& rhs) noexcept { + return simd(vaddq_f32(lhs.m_value, rhs.m_value)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator-( + simd const& lhs, simd const& rhs) noexcept { + return simd(vsubq_f32(lhs.m_value, rhs.m_value)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator<(simd const& lhs, simd const& rhs) noexcept { + return mask_type(vcltq_f32(lhs.m_value, rhs.m_value)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator>(simd const& lhs, simd const& rhs) noexcept { + return mask_type(vcgtq_f32(lhs.m_value, rhs.m_value)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator<=(simd const& lhs, simd const& rhs) noexcept { + return mask_type(vcleq_f32(lhs.m_value, rhs.m_value)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator>=(simd const& lhs, simd const& rhs) noexcept { + return mask_type(vcgeq_f32(lhs.m_value, rhs.m_value)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator==(simd const& lhs, simd const& rhs) noexcept { + return mask_type(vceqq_f32(lhs.m_value, rhs.m_value)); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator!=(simd const& lhs, simd const& rhs) noexcept { + return !(lhs == rhs); + } +}; + +} // namespace Experimental + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + abs(Experimental::simd< + float, Experimental::simd_abi::neon_fixed_size<4>> const& a) { + return Experimental::simd>( + vabsq_f32(static_cast(a))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + floor(Experimental::simd< + float, Experimental::simd_abi::neon_fixed_size<4>> const& a) { + return Experimental::simd>( + vrndmq_f32(static_cast(a))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + ceil(Experimental::simd< + float, Experimental::simd_abi::neon_fixed_size<4>> const& a) { + return Experimental::simd>( + vrndpq_f32(static_cast(a))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + round(Experimental::simd< + float, Experimental::simd_abi::neon_fixed_size<4>> const& a) { + return Experimental::simd>( + vrndxq_f32(static_cast(a))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + trunc(Experimental::simd< + float, Experimental::simd_abi::neon_fixed_size<4>> const& a) { + return Experimental::simd>( + vrndq_f32(static_cast(a))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION Experimental::simd< + float, Experimental::simd_abi::neon_fixed_size<4>> +copysign( + Experimental::simd> const& + a, + Experimental::simd> const& + b) { + uint32x4_t const sign_mask = vreinterpretq_u32_f32(vmovq_n_f32(-0.0)); + return Experimental::simd>( + vreinterpretq_f32_u32(vorrq_u32( + vreinterpretq_u32_f32(static_cast(abs(a))), + vandq_u32(sign_mask, + vreinterpretq_u32_f32(static_cast(b)))))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + sqrt(Experimental::simd< + float, Experimental::simd_abi::neon_fixed_size<4>> const& a) { + return Experimental::simd>( + vsqrtq_f32(static_cast(a))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION Experimental::simd< + float, Experimental::simd_abi::neon_fixed_size<4>> +fma(Experimental::simd> const& + a, + Experimental::simd> const& + b, + Experimental::simd> const& + c) { + return Experimental::simd>( + vfmaq_f32(static_cast(c), static_cast(b), + static_cast(a))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION Experimental::simd< + float, Experimental::simd_abi::neon_fixed_size<4>> +max(Experimental::simd> const& + a, + Experimental::simd> const& + b) { + return Experimental::simd>( + vmaxq_f32(static_cast(a), static_cast(b))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION Experimental::simd< + float, Experimental::simd_abi::neon_fixed_size<4>> +min(Experimental::simd> const& + a, + Experimental::simd> const& + b) { + return Experimental::simd>( + vminq_f32(static_cast(a), static_cast(b))); +} + +namespace Experimental { + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + simd> + condition(simd_mask> const& a, + simd> const& b, + simd> const& c) { + return simd>( + vbslq_f32(static_cast(a), static_cast(b), + static_cast(c))); +} + template <> class simd> { int32x2_t m_value; @@ -1000,6 +1393,226 @@ namespace Experimental { static_cast(c))); } +template <> +class simd> { + int32x4_t m_value; + + public: + using value_type = std::int32_t; + using abi_type = simd_abi::neon_fixed_size<4>; + using mask_type = simd_mask; + class reference { + int32x4_t& m_value; + int m_lane; + + public: + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION reference(int32x4_t& value_arg, + int lane_arg) + : m_value(value_arg), m_lane(lane_arg) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION reference + operator=(std::int32_t value) const { + switch (m_lane) { + case 0: m_value = vsetq_lane_s32(value, m_value, 0); break; + case 1: m_value = vsetq_lane_s32(value, m_value, 1); break; + case 2: m_value = vsetq_lane_s32(value, m_value, 2); break; + case 3: m_value = vsetq_lane_s32(value, m_value, 3); break; + } + return *this; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION operator std::int32_t() const { + switch (m_lane) { + case 0: return vgetq_lane_s32(m_value, 0); + case 1: return vgetq_lane_s32(m_value, 1); + case 2: return vgetq_lane_s32(m_value, 2); + case 3: return vgetq_lane_s32(m_value, 3); + } + return 0; + } + }; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd() = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(simd const&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(simd&&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd& operator=(simd const&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd& operator=(simd&&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION static constexpr std::size_t size() { + return 4; + } + template , + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd(U&& value) + : m_value(vmovq_n_s32(value_type(value))) {} + template >, + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit simd( + G&& gen) noexcept { + m_value = vsetq_lane_s32(gen(std::integral_constant()), + m_value, 0); + m_value = vsetq_lane_s32(gen(std::integral_constant()), + m_value, 1); + m_value = vsetq_lane_s32(gen(std::integral_constant()), + m_value, 2); + m_value = vsetq_lane_s32(gen(std::integral_constant()), + m_value, 3); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit simd( + int32x4_t const& value_in) + : m_value(value_in) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION explicit simd( + simd const& other); + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION reference operator[](std::size_t i) { + return reference(m_value, int(i)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION value_type + operator[](std::size_t i) const { + return reference(const_cast(this)->m_value, int(i)); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_from(value_type const* ptr, + element_aligned_tag) { + m_value = vld1q_s32(ptr); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_from(value_type const* ptr, + vector_aligned_tag) { + m_value = vld1q_s32(ptr); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_to( + value_type* ptr, element_aligned_tag) const { + vst1q_s32(ptr, m_value); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void copy_to(value_type* ptr, + vector_aligned_tag) const { + vst1q_s32(ptr, m_value); + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION constexpr explicit operator int32x4_t() + const { + return m_value; + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd operator-() const + noexcept { + return simd(vnegq_s32(m_value)); + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator-( + simd const& lhs, simd const& rhs) noexcept { + return simd( + vsubq_s32(static_cast(lhs), static_cast(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator+( + simd const& lhs, simd const& rhs) noexcept { + return simd( + vaddq_s32(static_cast(lhs), static_cast(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator*( + simd const& lhs, simd const& rhs) noexcept { + return simd( + vmulq_s32(static_cast(lhs), static_cast(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator==(simd const& lhs, simd const& rhs) noexcept { + return mask_type( + vceqq_s32(static_cast(lhs), static_cast(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator>(simd const& lhs, simd const& rhs) noexcept { + return mask_type( + vcgtq_s32(static_cast(lhs), static_cast(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator<(simd const& lhs, simd const& rhs) noexcept { + return mask_type( + vcltq_s32(static_cast(lhs), static_cast(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator<=(simd const& lhs, simd const& rhs) noexcept { + return mask_type( + vcleq_s32(static_cast(lhs), static_cast(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator>=(simd const& lhs, simd const& rhs) noexcept { + return mask_type( + vcgeq_s32(static_cast(lhs), static_cast(rhs))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend mask_type + operator!=(simd const& lhs, simd const& rhs) noexcept { + return !(lhs == rhs); + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator>>( + simd const& lhs, int rhs) noexcept { + return simd(vshlq_s32(static_cast(lhs), + vnegq_s32(vmovq_n_s32(std::int32_t(rhs))))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator>>( + simd const& lhs, simd const& rhs) noexcept { + return simd(vshlq_s32(static_cast(lhs), + vnegq_s32(static_cast(rhs)))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator<<( + simd const& lhs, int rhs) noexcept { + return simd( + vshlq_s32(static_cast(lhs), vmovq_n_s32(std::int32_t(rhs)))); + } + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION friend simd operator<<( + simd const& lhs, simd const& rhs) noexcept { + return simd( + vshlq_s32(static_cast(lhs), static_cast(rhs))); + } +}; + +} // namespace Experimental + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + abs(Experimental::simd< + std::int32_t, Experimental::simd_abi::neon_fixed_size<4>> const& a) { + return Experimental::simd>( + vabsq_s32(static_cast(a))); +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + floor(Experimental::simd< + std::int32_t, Experimental::simd_abi::neon_fixed_size<4>> const& a) { + return a; +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + ceil(Experimental::simd< + std::int32_t, Experimental::simd_abi::neon_fixed_size<4>> const& a) { + return a; +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + round(Experimental::simd< + std::int32_t, Experimental::simd_abi::neon_fixed_size<4>> const& a) { + return a; +} + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + Experimental::simd> + trunc(Experimental::simd< + std::int32_t, Experimental::simd_abi::neon_fixed_size<4>> const& a) { + return a; +} + +namespace Experimental { + +[[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + simd> + condition(simd_mask> const& a, + simd> const& b, + simd> const& c) { + return simd>( + vbslq_s32(static_cast(a), static_cast(b), + static_cast(c))); +} + template <> class simd> { int64x2_t m_value; @@ -1593,6 +2206,106 @@ class where_expression>, } }; +template <> +class const_where_expression>, + simd>> { + public: + using abi_type = simd_abi::neon_fixed_size<4>; + using value_type = simd; + using mask_type = simd_mask; + + protected: + value_type& m_value; + mask_type const& m_mask; + + public: + const_where_expression(mask_type const& mask_arg, value_type const& value_arg) + : m_value(const_cast(value_arg)), m_mask(mask_arg) {} + + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_to(float* mem, element_aligned_tag) const { + if (m_mask[0]) mem[0] = m_value[0]; + if (m_mask[1]) mem[1] = m_value[1]; + if (m_mask[2]) mem[2] = m_value[2]; + if (m_mask[3]) mem[3] = m_value[3]; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_to(float* mem, vector_aligned_tag) const { + if (m_mask[0]) mem[0] = m_value[0]; + if (m_mask[1]) mem[1] = m_value[1]; + if (m_mask[2]) mem[2] = m_value[2]; + if (m_mask[3]) mem[3] = m_value[3]; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void scatter_to( + float* mem, + simd> const& index) const { + if (m_mask[0]) mem[index[0]] = m_value[0]; + if (m_mask[1]) mem[index[1]] = m_value[1]; + if (m_mask[2]) mem[index[2]] = m_value[2]; + if (m_mask[3]) mem[index[3]] = m_value[3]; + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION value_type const& + impl_get_value() const { + return m_value; + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION mask_type const& + impl_get_mask() const { + return m_mask; + } +}; + +template <> +class where_expression>, + simd>> + : public const_where_expression< + simd_mask>, + simd>> { + public: + where_expression( + simd_mask> const& mask_arg, + simd>& value_arg) + : const_where_expression(mask_arg, value_arg) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_from(float const* mem, element_aligned_tag) { + if (m_mask[0]) m_value[0] = mem[0]; + if (m_mask[1]) m_value[1] = mem[1]; + if (m_mask[2]) m_value[2] = mem[2]; + if (m_mask[3]) m_value[3] = mem[3]; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_from(float const* mem, vector_aligned_tag) { + if (m_mask[0]) m_value[0] = mem[0]; + if (m_mask[1]) m_value[1] = mem[1]; + if (m_mask[2]) m_value[2] = mem[2]; + if (m_mask[3]) m_value[3] = mem[3]; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void gather_from( + float const* mem, + simd> const& index) { + if (m_mask[0]) m_value[0] = mem[index[0]]; + if (m_mask[1]) m_value[1] = mem[index[1]]; + if (m_mask[2]) m_value[2] = mem[index[2]]; + if (m_mask[3]) m_value[3] = mem[index[3]]; + } + template >>, + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void operator=(U&& x) { + auto const x_as_value_type = + static_cast>>( + std::forward(x)); + m_value = static_cast>>( + vbslq_f32(static_cast(m_mask), + static_cast(x_as_value_type), + static_cast(m_value))); + } +}; + template <> class const_where_expression< simd_mask>, @@ -1686,6 +2399,108 @@ class where_expression>, } }; +template <> +class const_where_expression< + simd_mask>, + simd>> { + public: + using abi_type = simd_abi::neon_fixed_size<4>; + using value_type = simd; + using mask_type = simd_mask; + + protected: + value_type& m_value; + mask_type const& m_mask; + + public: + const_where_expression(mask_type const& mask_arg, value_type const& value_arg) + : m_value(const_cast(value_arg)), m_mask(mask_arg) {} + + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_to(std::int32_t* mem, element_aligned_tag) const { + if (m_mask[0]) mem[0] = m_value[0]; + if (m_mask[1]) mem[1] = m_value[1]; + if (m_mask[2]) mem[2] = m_value[2]; + if (m_mask[3]) mem[3] = m_value[3]; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_to(std::int32_t* mem, vector_aligned_tag) const { + if (m_mask[0]) mem[0] = m_value[0]; + if (m_mask[1]) mem[1] = m_value[1]; + if (m_mask[2]) mem[2] = m_value[2]; + if (m_mask[3]) mem[3] = m_value[3]; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void scatter_to( + std::int32_t* mem, + simd> const& index) const { + if (m_mask[0]) mem[index[0]] = m_value[0]; + if (m_mask[1]) mem[index[1]] = m_value[1]; + if (m_mask[2]) mem[index[2]] = m_value[2]; + if (m_mask[3]) mem[index[3]] = m_value[3]; + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION value_type const& + impl_get_value() const { + return m_value; + } + + [[nodiscard]] KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION mask_type const& + impl_get_mask() const { + return m_mask; + } +}; + +template <> +class where_expression>, + simd>> + : public const_where_expression< + simd_mask>, + simd>> { + public: + where_expression( + simd_mask> const& mask_arg, + simd>& value_arg) + : const_where_expression(mask_arg, value_arg) {} + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_from(std::int32_t const* mem, element_aligned_tag) { + if (m_mask[0]) m_value[0] = mem[0]; + if (m_mask[1]) m_value[1] = mem[1]; + if (m_mask[2]) m_value[2] = mem[2]; + if (m_mask[3]) m_value[3] = mem[3]; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void copy_from(std::int32_t const* mem, vector_aligned_tag) { + if (m_mask[0]) m_value[0] = mem[0]; + if (m_mask[1]) m_value[1] = mem[1]; + if (m_mask[2]) m_value[2] = mem[2]; + if (m_mask[3]) m_value[3] = mem[3]; + } + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION + void gather_from( + std::int32_t const* mem, + simd> const& index) { + if (m_mask[0]) m_value[0] = mem[index[0]]; + if (m_mask[1]) m_value[1] = mem[index[1]]; + if (m_mask[2]) m_value[2] = mem[index[2]]; + if (m_mask[3]) m_value[3] = mem[index[3]]; + } + template < + class U, + std::enable_if_t< + std::is_convertible_v>>, + bool> = false> + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION void operator=(U&& x) { + auto const x_as_value_type = + static_cast>>( + std::forward(x)); + m_value = static_cast>>( + vbslq_s32(static_cast(m_mask), + static_cast(x_as_value_type), + static_cast(m_value))); + } +}; + template <> class const_where_expression< simd_mask>, diff --git a/lib/kokkos/simd/unit_tests/CMakeLists.txt b/lib/kokkos/simd/unit_tests/CMakeLists.txt index 75d557e8b5..109effc710 100644 --- a/lib/kokkos/simd/unit_tests/CMakeLists.txt +++ b/lib/kokkos/simd/unit_tests/CMakeLists.txt @@ -1,7 +1,9 @@ KOKKOS_INCLUDE_DIRECTORIES(${KOKKOS_SOURCE_DIR}/simd/unit_tests/include) -KOKKOS_ADD_EXECUTABLE_AND_TEST( - UnitTest_SIMD - SOURCES - UnitTestMain.cpp - TestSIMD.cpp) +IF((NOT (Kokkos_ENABLE_CUDA AND WIN32))) + KOKKOS_ADD_EXECUTABLE_AND_TEST( + UnitTest_SIMD + SOURCES + UnitTestMain.cpp + TestSIMD.cpp) +ENDIF() diff --git a/lib/kokkos/simd/unit_tests/include/SIMDTesting_Ops.hpp b/lib/kokkos/simd/unit_tests/include/SIMDTesting_Ops.hpp index c587ccf304..74141f2531 100644 --- a/lib/kokkos/simd/unit_tests/include/SIMDTesting_Ops.hpp +++ b/lib/kokkos/simd/unit_tests/include/SIMDTesting_Ops.hpp @@ -81,7 +81,9 @@ class absolutes { auto on_host(T const& a) const { if constexpr (std::is_signed_v) { #if defined(KOKKOS_ENABLE_DEPRECATED_CODE_4) + KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_PUSH() return Kokkos::Experimental::abs(a); + KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_POP() #else return Kokkos::abs(a); #endif diff --git a/lib/kokkos/simd/unit_tests/include/SIMDTesting_Utilities.hpp b/lib/kokkos/simd/unit_tests/include/SIMDTesting_Utilities.hpp index d36e1e5afc..9719855f0f 100644 --- a/lib/kokkos/simd/unit_tests/include/SIMDTesting_Utilities.hpp +++ b/lib/kokkos/simd/unit_tests/include/SIMDTesting_Utilities.hpp @@ -135,8 +135,8 @@ class load_masked { for (std::size_t i = 0; i < n; ++i) { mask[i] = true; } + result = T(0); where(mask, result).copy_from(mem, Kokkos::Experimental::simd_flag_default); - where(!mask, result) = 0; return true; } template @@ -181,4 +181,14 @@ class load_as_scalars { } }; +// Simple check to loosely test that T is a complete type. +// Some capabilities are only defined for specific data type and abi pairs (i.e. +// extended vector width); this is used to exclude pairs that +// are not defined from being tested. +template +constexpr bool is_type_v = false; + +template +constexpr bool is_type_v = true; + #endif diff --git a/lib/kokkos/simd/unit_tests/include/TestSIMD_Condition.hpp b/lib/kokkos/simd/unit_tests/include/TestSIMD_Condition.hpp index f8d8cc70fa..bf22cf3352 100644 --- a/lib/kokkos/simd/unit_tests/include/TestSIMD_Condition.hpp +++ b/lib/kokkos/simd/unit_tests/include/TestSIMD_Condition.hpp @@ -22,21 +22,23 @@ template inline void host_check_condition() { - using simd_type = typename Kokkos::Experimental::simd; - using mask_type = typename simd_type::mask_type; + if constexpr (is_type_v>) { + using simd_type = typename Kokkos::Experimental::simd; + using mask_type = typename simd_type::mask_type; - auto condition_op = [](mask_type const& mask, simd_type const& a, - simd_type const& b) { - return Kokkos::Experimental::condition(mask, a, b); - }; + auto condition_op = [](mask_type const& mask, simd_type const& a, + simd_type const& b) { + return Kokkos::Experimental::condition(mask, a, b); + }; - simd_type value_a(16); - simd_type value_b(20); + simd_type value_a(16); + simd_type value_b(20); - auto condition_result = condition_op(mask_type(false), value_a, value_b); - EXPECT_TRUE(all_of(condition_result == value_b)); - condition_result = condition_op(mask_type(true), value_a, value_b); - EXPECT_TRUE(all_of(condition_result == value_a)); + auto condition_result = condition_op(mask_type(false), value_a, value_b); + EXPECT_TRUE(all_of(condition_result == value_b)); + condition_result = condition_op(mask_type(true), value_a, value_b); + EXPECT_TRUE(all_of(condition_result == value_a)); + } } template @@ -54,22 +56,24 @@ inline void host_check_condition_all_abis( template KOKKOS_INLINE_FUNCTION void device_check_condition() { - using simd_type = typename Kokkos::Experimental::simd; - using mask_type = typename simd_type::mask_type; - kokkos_checker checker; + if constexpr (is_type_v>) { + using simd_type = typename Kokkos::Experimental::simd; + using mask_type = typename simd_type::mask_type; + kokkos_checker checker; - auto condition_op = [](mask_type const& mask, simd_type const& a, - simd_type const& b) { - return Kokkos::Experimental::condition(mask, a, b); - }; + auto condition_op = [](mask_type const& mask, simd_type const& a, + simd_type const& b) { + return Kokkos::Experimental::condition(mask, a, b); + }; - simd_type value_a(16); - simd_type value_b(20); + simd_type value_a(16); + simd_type value_b(20); - auto condition_result = condition_op(mask_type(false), value_a, value_b); - checker.truth(all_of(condition_result == value_b)); - condition_result = condition_op(mask_type(true), value_a, value_b); - checker.truth(all_of(condition_result == value_a)); + auto condition_result = condition_op(mask_type(false), value_a, value_b); + checker.truth(all_of(condition_result == value_b)); + condition_result = condition_op(mask_type(true), value_a, value_b); + checker.truth(all_of(condition_result == value_a)); + } } template diff --git a/lib/kokkos/simd/unit_tests/include/TestSIMD_Conversions.hpp b/lib/kokkos/simd/unit_tests/include/TestSIMD_Conversions.hpp index b98871bbab..20b0729762 100644 --- a/lib/kokkos/simd/unit_tests/include/TestSIMD_Conversions.hpp +++ b/lib/kokkos/simd/unit_tests/include/TestSIMD_Conversions.hpp @@ -22,40 +22,42 @@ template inline void host_check_conversions() { - { - auto a = Kokkos::Experimental::simd(1); - auto b = Kokkos::Experimental::simd(a); - EXPECT_TRUE(all_of(b == decltype(b)(1))); - } - { - auto a = Kokkos::Experimental::simd(1); - auto b = Kokkos::Experimental::simd(a); - EXPECT_TRUE(all_of(b == decltype(b)(1))); - } - { - auto a = Kokkos::Experimental::simd(1); - auto b = Kokkos::Experimental::simd(a); - EXPECT_TRUE(all_of(b == decltype(b)(1))); - } - { - auto a = Kokkos::Experimental::simd_mask(true); - auto b = Kokkos::Experimental::simd_mask(a); - EXPECT_TRUE(b == decltype(b)(true)); - } - { - auto a = Kokkos::Experimental::simd_mask(true); - auto b = Kokkos::Experimental::simd_mask(a); - EXPECT_TRUE(b == decltype(b)(true)); - } - { - auto a = Kokkos::Experimental::simd_mask(true); - auto b = Kokkos::Experimental::simd_mask(a); - EXPECT_TRUE(b == decltype(b)(true)); - } - { - auto a = Kokkos::Experimental::simd_mask(true); - auto b = Kokkos::Experimental::simd_mask(a); - EXPECT_TRUE(b == decltype(b)(true)); + if constexpr (is_type_v>) { + { + auto a = Kokkos::Experimental::simd(1); + auto b = Kokkos::Experimental::simd(a); + EXPECT_TRUE(all_of(b == decltype(b)(1))); + } + { + auto a = Kokkos::Experimental::simd(1); + auto b = Kokkos::Experimental::simd(a); + EXPECT_TRUE(all_of(b == decltype(b)(1))); + } + { + auto a = Kokkos::Experimental::simd(1); + auto b = Kokkos::Experimental::simd(a); + EXPECT_TRUE(all_of(b == decltype(b)(1))); + } + { + auto a = Kokkos::Experimental::simd_mask(true); + auto b = Kokkos::Experimental::simd_mask(a); + EXPECT_TRUE(b == decltype(b)(true)); + } + { + auto a = Kokkos::Experimental::simd_mask(true); + auto b = Kokkos::Experimental::simd_mask(a); + EXPECT_TRUE(b == decltype(b)(true)); + } + { + auto a = Kokkos::Experimental::simd_mask(true); + auto b = Kokkos::Experimental::simd_mask(a); + EXPECT_TRUE(b == decltype(b)(true)); + } + { + auto a = Kokkos::Experimental::simd_mask(true); + auto b = Kokkos::Experimental::simd_mask(a); + EXPECT_TRUE(b == decltype(b)(true)); + } } } @@ -67,41 +69,43 @@ inline void host_check_conversions_all_abis( template KOKKOS_INLINE_FUNCTION void device_check_conversions() { - kokkos_checker checker; - { - auto a = Kokkos::Experimental::simd(1); - auto b = Kokkos::Experimental::simd(a); - checker.truth(all_of(b == decltype(b)(1))); - } - { - auto a = Kokkos::Experimental::simd(1); - auto b = Kokkos::Experimental::simd(a); - checker.truth(all_of(b == decltype(b)(1))); - } - { - auto a = Kokkos::Experimental::simd(1); - auto b = Kokkos::Experimental::simd(a); - checker.truth(all_of(b == decltype(b)(1))); - } - { - auto a = Kokkos::Experimental::simd_mask(true); - auto b = Kokkos::Experimental::simd_mask(a); - checker.truth(b == decltype(b)(true)); - } - { - auto a = Kokkos::Experimental::simd_mask(true); - auto b = Kokkos::Experimental::simd_mask(a); - checker.truth(b == decltype(b)(true)); - } - { - auto a = Kokkos::Experimental::simd_mask(true); - auto b = Kokkos::Experimental::simd_mask(a); - checker.truth(b == decltype(b)(true)); - } - { - auto a = Kokkos::Experimental::simd_mask(true); - auto b = Kokkos::Experimental::simd_mask(a); - checker.truth(b == decltype(b)(true)); + if constexpr (is_type_v>) { + kokkos_checker checker; + { + auto a = Kokkos::Experimental::simd(1); + auto b = Kokkos::Experimental::simd(a); + checker.truth(all_of(b == decltype(b)(1))); + } + { + auto a = Kokkos::Experimental::simd(1); + auto b = Kokkos::Experimental::simd(a); + checker.truth(all_of(b == decltype(b)(1))); + } + { + auto a = Kokkos::Experimental::simd(1); + auto b = Kokkos::Experimental::simd(a); + checker.truth(all_of(b == decltype(b)(1))); + } + { + auto a = Kokkos::Experimental::simd_mask(true); + auto b = Kokkos::Experimental::simd_mask(a); + checker.truth(b == decltype(b)(true)); + } + { + auto a = Kokkos::Experimental::simd_mask(true); + auto b = Kokkos::Experimental::simd_mask(a); + checker.truth(b == decltype(b)(true)); + } + { + auto a = Kokkos::Experimental::simd_mask(true); + auto b = Kokkos::Experimental::simd_mask(a); + checker.truth(b == decltype(b)(true)); + } + { + auto a = Kokkos::Experimental::simd_mask(true); + auto b = Kokkos::Experimental::simd_mask(a); + checker.truth(b == decltype(b)(true)); + } } } diff --git a/lib/kokkos/simd/unit_tests/include/TestSIMD_GeneratorCtors.hpp b/lib/kokkos/simd/unit_tests/include/TestSIMD_GeneratorCtors.hpp index 23e3826c75..1a61fd9cbb 100644 --- a/lib/kokkos/simd/unit_tests/include/TestSIMD_GeneratorCtors.hpp +++ b/lib/kokkos/simd/unit_tests/include/TestSIMD_GeneratorCtors.hpp @@ -22,49 +22,51 @@ template inline void host_check_gen_ctor() { - using simd_type = Kokkos::Experimental::simd; - using mask_type = typename simd_type::mask_type; - constexpr std::size_t lanes = simd_type::size(); + if constexpr (is_type_v>) { + using simd_type = Kokkos::Experimental::simd; + using mask_type = typename simd_type::mask_type; + constexpr std::size_t lanes = simd_type::size(); - DataType init[lanes]; - DataType expected[lanes]; - mask_type init_mask(false); + DataType init[lanes]; + DataType expected[lanes]; + mask_type init_mask(false); - for (std::size_t i = 0; i < lanes; ++i) { - if (i % 3 == 0) init_mask[i] = true; - init[i] = 7; - expected[i] = (init_mask[i]) ? init[i] * 9 : init[i]; - } + for (std::size_t i = 0; i < lanes; ++i) { + if (i % 3 == 0) init_mask[i] = true; + init[i] = 7; + expected[i] = (init_mask[i]) ? init[i] * 9 : init[i]; + } - simd_type rhs; - rhs.copy_from(init, Kokkos::Experimental::simd_flag_default); + simd_type rhs; + rhs.copy_from(init, Kokkos::Experimental::simd_flag_default); - simd_type blend; - blend.copy_from(expected, Kokkos::Experimental::simd_flag_default); + simd_type blend; + blend.copy_from(expected, Kokkos::Experimental::simd_flag_default); #if !(defined(KOKKOS_ENABLE_CUDA) && defined(KOKKOS_COMPILER_MSVC)) - if constexpr (std::is_same_v) { - simd_type basic(KOKKOS_LAMBDA(std::size_t i) { return init[i]; }); - host_check_equality(basic, rhs, lanes); + if constexpr (std::is_same_v) { + simd_type basic(KOKKOS_LAMBDA(std::size_t i) { return init[i]; }); + host_check_equality(basic, rhs, lanes); - simd_type lhs(KOKKOS_LAMBDA(std::size_t i) { return init[i] * 9; }); - mask_type mask(KOKKOS_LAMBDA(std::size_t i) { return init_mask[i]; }); - simd_type result( - KOKKOS_LAMBDA(std::size_t i) { return (mask[i]) ? lhs[i] : rhs[i]; }); + simd_type lhs(KOKKOS_LAMBDA(std::size_t i) { return init[i] * 9; }); + mask_type mask(KOKKOS_LAMBDA(std::size_t i) { return init_mask[i]; }); + simd_type result( + KOKKOS_LAMBDA(std::size_t i) { return (mask[i]) ? lhs[i] : rhs[i]; }); - host_check_equality(blend, result, lanes); - } else { - simd_type basic([=](std::size_t i) { return init[i]; }); - host_check_equality(basic, rhs, lanes); + host_check_equality(blend, result, lanes); + } else { + simd_type basic([=](std::size_t i) { return init[i]; }); + host_check_equality(basic, rhs, lanes); - simd_type lhs([=](std::size_t i) { return init[i] * 9; }); - mask_type mask([=](std::size_t i) { return init_mask[i]; }); - simd_type result( - [=](std::size_t i) { return (mask[i]) ? lhs[i] : rhs[i]; }); + simd_type lhs([=](std::size_t i) { return init[i] * 9; }); + mask_type mask([=](std::size_t i) { return init_mask[i]; }); + simd_type result( + [=](std::size_t i) { return (mask[i]) ? lhs[i] : rhs[i]; }); - host_check_equality(blend, result, lanes); - } + host_check_equality(blend, result, lanes); + } #endif + } } template @@ -82,32 +84,34 @@ inline void host_check_gen_ctors_all_abis( template KOKKOS_INLINE_FUNCTION void device_check_gen_ctor() { - using simd_type = Kokkos::Experimental::simd; - using mask_type = typename simd_type::mask_type; - constexpr std::size_t lanes = simd_type::size(); + if constexpr (is_type_v>) { + using simd_type = Kokkos::Experimental::simd; + using mask_type = typename simd_type::mask_type; + constexpr std::size_t lanes = simd_type::size(); - DataType init[lanes]; - DataType expected[lanes]; - mask_type mask(false); + DataType init[lanes]; + DataType expected[lanes]; + mask_type mask(false); - for (std::size_t i = 0; i < lanes; ++i) { - if (i % 3 == 0) mask[i] = true; - init[i] = 7; - expected[i] = (mask[i]) ? init[i] * 9 : init[i]; + for (std::size_t i = 0; i < lanes; ++i) { + if (i % 3 == 0) mask[i] = true; + init[i] = 7; + expected[i] = (mask[i]) ? init[i] * 9 : init[i]; + } + + simd_type basic(KOKKOS_LAMBDA(std::size_t i) { return init[i]; }); + simd_type rhs; + rhs.copy_from(init, Kokkos::Experimental::simd_flag_default); + device_check_equality(basic, rhs, lanes); + + simd_type lhs(KOKKOS_LAMBDA(std::size_t i) { return init[i] * 9; }); + simd_type result( + KOKKOS_LAMBDA(std::size_t i) { return (mask[i]) ? lhs[i] : rhs[i]; }); + + simd_type blend; + blend.copy_from(expected, Kokkos::Experimental::simd_flag_default); + device_check_equality(result, blend, lanes); } - - simd_type basic(KOKKOS_LAMBDA(std::size_t i) { return init[i]; }); - simd_type rhs; - rhs.copy_from(init, Kokkos::Experimental::simd_flag_default); - device_check_equality(basic, rhs, lanes); - - simd_type lhs(KOKKOS_LAMBDA(std::size_t i) { return init[i] * 9; }); - simd_type result( - KOKKOS_LAMBDA(std::size_t i) { return (mask[i]) ? lhs[i] : rhs[i]; }); - - simd_type blend; - blend.copy_from(expected, Kokkos::Experimental::simd_flag_default); - device_check_equality(result, blend, lanes); } template diff --git a/lib/kokkos/simd/unit_tests/include/TestSIMD_MaskOps.hpp b/lib/kokkos/simd/unit_tests/include/TestSIMD_MaskOps.hpp index a93c52e9a8..c3d4ac594d 100644 --- a/lib/kokkos/simd/unit_tests/include/TestSIMD_MaskOps.hpp +++ b/lib/kokkos/simd/unit_tests/include/TestSIMD_MaskOps.hpp @@ -22,25 +22,27 @@ template inline void host_check_mask_ops() { - using mask_type = Kokkos::Experimental::simd_mask; + if constexpr (is_type_v>) { + using mask_type = Kokkos::Experimental::simd_mask; - EXPECT_FALSE(none_of(mask_type(true))); - EXPECT_TRUE(none_of(mask_type(false))); - EXPECT_TRUE(all_of(mask_type(true))); - EXPECT_FALSE(all_of(mask_type(false))); - EXPECT_TRUE(any_of(mask_type(true))); - EXPECT_FALSE(any_of(mask_type(false))); + EXPECT_FALSE(none_of(mask_type(true))); + EXPECT_TRUE(none_of(mask_type(false))); + EXPECT_TRUE(all_of(mask_type(true))); + EXPECT_FALSE(all_of(mask_type(false))); + EXPECT_TRUE(any_of(mask_type(true))); + EXPECT_FALSE(any_of(mask_type(false))); - for (std::size_t i = 0; i < mask_type::size(); ++i) { - mask_type test_mask(KOKKOS_LAMBDA(std::size_t j) { return i == j; }); + for (std::size_t i = 0; i < mask_type::size(); ++i) { + mask_type test_mask(KOKKOS_LAMBDA(std::size_t j) { return i == j; }); - EXPECT_TRUE(any_of(test_mask)); - EXPECT_FALSE(none_of(test_mask)); + EXPECT_TRUE(any_of(test_mask)); + EXPECT_FALSE(none_of(test_mask)); - if constexpr (mask_type::size() > 1) { - EXPECT_FALSE(all_of(test_mask)); - } else { - EXPECT_TRUE(all_of(test_mask)); + if constexpr (mask_type::size() > 1) { + EXPECT_FALSE(all_of(test_mask)); + } else { + EXPECT_TRUE(all_of(test_mask)); + } } } } @@ -60,25 +62,27 @@ inline void host_check_mask_ops_all_abis( template KOKKOS_INLINE_FUNCTION void device_check_mask_ops() { - using mask_type = Kokkos::Experimental::simd_mask; - kokkos_checker checker; - checker.truth(!none_of(mask_type(true))); - checker.truth(none_of(mask_type(false))); - checker.truth(all_of(mask_type(true))); - checker.truth(!all_of(mask_type(false))); - checker.truth(any_of(mask_type(true))); - checker.truth(!any_of(mask_type(false))); + if constexpr (is_type_v>) { + using mask_type = Kokkos::Experimental::simd_mask; + kokkos_checker checker; + checker.truth(!none_of(mask_type(true))); + checker.truth(none_of(mask_type(false))); + checker.truth(all_of(mask_type(true))); + checker.truth(!all_of(mask_type(false))); + checker.truth(any_of(mask_type(true))); + checker.truth(!any_of(mask_type(false))); - for (std::size_t i = 0; i < mask_type::size(); ++i) { - mask_type test_mask(KOKKOS_LAMBDA(std::size_t j) { return i == j; }); + for (std::size_t i = 0; i < mask_type::size(); ++i) { + mask_type test_mask(KOKKOS_LAMBDA(std::size_t j) { return i == j; }); - checker.truth(any_of(test_mask)); - checker.truth(!none_of(test_mask)); + checker.truth(any_of(test_mask)); + checker.truth(!none_of(test_mask)); - if constexpr (mask_type::size() > 1) { - checker.truth(!all_of(test_mask)); - } else { - checker.truth(all_of(test_mask)); + if constexpr (mask_type::size() > 1) { + checker.truth(!all_of(test_mask)); + } else { + checker.truth(all_of(test_mask)); + } } } } diff --git a/lib/kokkos/simd/unit_tests/include/TestSIMD_MathOps.hpp b/lib/kokkos/simd/unit_tests/include/TestSIMD_MathOps.hpp index 59f2f6c18f..4891a54f6c 100644 --- a/lib/kokkos/simd/unit_tests/include/TestSIMD_MathOps.hpp +++ b/lib/kokkos/simd/unit_tests/include/TestSIMD_MathOps.hpp @@ -121,31 +121,34 @@ inline void host_check_abi_size() { template inline void host_check_math_ops() { - constexpr size_t n = 11; - constexpr size_t alignment = - Kokkos::Experimental::simd::size() * sizeof(DataType); + if constexpr (is_type_v>) { + constexpr size_t alignment = + Kokkos::Experimental::simd::size() * sizeof(DataType); - host_check_abi_size(); + host_check_abi_size(); - if constexpr (!std::is_integral_v) { - alignas(alignment) DataType const first_args[n] = { - 0.1, 0.4, 0.5, 0.7, 1.0, 1.5, -2.0, 10.0, 0.0, 1.2, -2.8}; - alignas(alignment) DataType const second_args[n] = { - 1.0, 0.2, 1.1, 1.8, -0.1, -3.0, -2.4, 1.0, 13.0, -3.2, -2.1}; - host_check_all_math_ops(first_args, second_args); - } else { - if constexpr (std::is_signed_v) { - alignas(alignment) - DataType const first_args[n] = {1, 2, -1, 10, 0, 1, -2, 10, 0, 1, -2}; - alignas(alignment) DataType const second_args[n] = {1, 2, 1, 1, 1, -3, - -2, 1, 13, -3, -2}; + if constexpr (!std::is_integral_v) { + alignas(alignment) DataType const first_args[] = { + 0.1, 0.4, 0.5, 0.7, 1.0, 1.5, -2.0, 10.0, + 0.0, 1.2, -2.8, 3.0, 4.0, -0.1, 5.0, -0.2}; + alignas(alignment) DataType const second_args[] = { + 1.0, 0.2, 1.1, 1.8, -0.1, -3.0, -2.4, 1.0, + 13.0, -3.2, -2.1, 3.0, -15.0, -0.5, -0.2, -0.2}; host_check_all_math_ops(first_args, second_args); } else { - alignas(alignment) - DataType const first_args[n] = {1, 2, 1, 10, 0, 1, 2, 10, 0, 1, 2}; - alignas(alignment) - DataType const second_args[n] = {1, 2, 1, 1, 1, 3, 2, 1, 13, 3, 2}; - host_check_all_math_ops(first_args, second_args); + if constexpr (std::is_signed_v) { + alignas(alignment) DataType const first_args[] = { + 1, 2, -1, 10, 0, 1, -2, 10, 0, 1, -2, -3, 7, 4, -9, -15}; + alignas(alignment) DataType const second_args[] = { + 1, 2, 1, 1, 1, -3, -2, 1, 13, -3, -2, 10, -15, 7, 2, -10}; + host_check_all_math_ops(first_args, second_args); + } else { + alignas(alignment) DataType const first_args[] = { + 1, 2, 1, 10, 0, 1, 2, 10, 0, 1, 2, 11, 5, 8, 2, 14}; + alignas(alignment) DataType const second_args[] = { + 1, 2, 1, 1, 1, 3, 2, 1, 13, 3, 2, 3, 6, 20, 5, 14}; + host_check_all_math_ops(first_args, second_args); + } } } } @@ -253,25 +256,31 @@ KOKKOS_INLINE_FUNCTION void device_check_abi_size() { template KOKKOS_INLINE_FUNCTION void device_check_math_ops() { - constexpr size_t n = 11; + if constexpr (is_type_v>) { + device_check_abi_size(); - device_check_abi_size(); - - if constexpr (!std::is_integral_v) { - DataType const first_args[n] = {0.1, 0.4, 0.5, 0.7, 1.0, 1.5, - -2.0, 10.0, 0.0, 1.2, -2.8}; - DataType const second_args[n] = {1.0, 0.2, 1.1, 1.8, -0.1, -3.0, - -2.4, 1.0, 13.0, -3.2, -2.1}; - device_check_all_math_ops(first_args, second_args); - } else { - if constexpr (std::is_signed_v) { - DataType const first_args[n] = {1, 2, -1, 10, 0, 1, -2, 10, 0, 1, -2}; - DataType const second_args[n] = {1, 2, 1, 1, 1, -3, -2, 1, 13, -3, -2}; + if constexpr (!std::is_integral_v) { + DataType const first_args[] = {0.1, 0.4, 0.5, 0.7, 1.0, 1.5, + -2.0, 10.0, 0.0, 1.2, -2.8, 3.0, + 4.0, -0.1, 5.0, -0.2}; + DataType const second_args[] = {1.0, 0.2, 1.1, 1.8, -0.1, -3.0, + -2.4, 1.0, 13.0, -3.2, -2.1, 3.0, + -15.0, -0.5, -0.2, -0.2}; device_check_all_math_ops(first_args, second_args); } else { - DataType const first_args[n] = {1, 2, 1, 10, 0, 1, 2, 10, 0, 1, 2}; - DataType const second_args[n] = {1, 2, 1, 1, 1, 3, 2, 1, 13, 3, 2}; - device_check_all_math_ops(first_args, second_args); + if constexpr (std::is_signed_v) { + DataType const first_args[] = {1, 2, -1, 10, 0, 1, -2, 10, + 0, 1, -2, -3, 7, 4, -9, -15}; + DataType const second_args[] = {1, 2, 1, 1, 1, -3, -2, 1, + 13, -3, -2, 10, -15, 7, 2, -10}; + device_check_all_math_ops(first_args, second_args); + } else { + DataType const first_args[] = {1, 2, 1, 10, 0, 1, 2, 10, + 0, 1, 2, 11, 5, 8, 2, 14}; + DataType const second_args[] = {1, 2, 1, 1, 1, 3, 2, 1, + 13, 3, 2, 3, 6, 20, 5, 14}; + device_check_all_math_ops(first_args, second_args); + } } } } diff --git a/lib/kokkos/simd/unit_tests/include/TestSIMD_Reductions.hpp b/lib/kokkos/simd/unit_tests/include/TestSIMD_Reductions.hpp index b3c7ac9a01..a3e796a030 100644 --- a/lib/kokkos/simd/unit_tests/include/TestSIMD_Reductions.hpp +++ b/lib/kokkos/simd/unit_tests/include/TestSIMD_Reductions.hpp @@ -65,14 +65,18 @@ inline void host_check_all_reductions(const DataType (&args)[n]) { template inline void host_check_reductions() { - constexpr size_t n = 11; + if constexpr (is_type_v>) { + constexpr size_t n = 16; - if constexpr (std::is_signed_v) { - DataType const args[n] = {1, 2, -1, 10, 0, 1, -2, 10, 0, 1, -2}; - host_check_all_reductions(args); - } else { - DataType const args[n] = {1, 2, 1, 10, 0, 1, 2, 10, 0, 1, 2}; - host_check_all_reductions(args); + if constexpr (std::is_signed_v) { + DataType const args[n] = {1, 2, -1, 10, 0, 1, -2, 10, + 0, 1, -2, -15, 5, 17, -22, 20}; + host_check_all_reductions(args); + } else { + DataType const args[n] = {1, 2, 1, 10, 0, 1, 2, 10, + 0, 1, 2, 15, 5, 17, 22, 20}; + host_check_all_reductions(args); + } } } @@ -135,14 +139,18 @@ KOKKOS_INLINE_FUNCTION void device_check_all_reductions( template KOKKOS_INLINE_FUNCTION void device_check_reductions() { - constexpr size_t n = 11; + if constexpr (is_type_v>) { + constexpr size_t n = 16; - if constexpr (std::is_signed_v) { - DataType const args[n] = {1, 2, -1, 10, 0, 1, -2, 10, 0, 1, -2}; - device_check_all_reductions(args); - } else { - DataType const args[n] = {1, 2, 1, 10, 0, 1, 2, 10, 0, 1, 2}; - device_check_all_reductions(args); + if constexpr (std::is_signed_v) { + DataType const args[n] = {1, 2, -1, 10, 0, 1, -2, 10, + 0, 1, -2, -15, 5, 17, -22, 20}; + device_check_all_reductions(args); + } else { + DataType const args[n] = {1, 2, 1, 10, 0, 1, 2, 10, + 0, 1, 2, 15, 5, 17, 22, 20}; + device_check_all_reductions(args); + } } } diff --git a/lib/kokkos/simd/unit_tests/include/TestSIMD_ShiftOps.hpp b/lib/kokkos/simd/unit_tests/include/TestSIMD_ShiftOps.hpp index ffdd2cba4a..7329f08501 100644 --- a/lib/kokkos/simd/unit_tests/include/TestSIMD_ShiftOps.hpp +++ b/lib/kokkos/simd/unit_tests/include/TestSIMD_ShiftOps.hpp @@ -103,34 +103,35 @@ inline void host_check_shift_op_all_loaders(ShiftOp shift_op, template inline void host_check_shift_ops() { - if constexpr (std::is_integral_v) { - using simd_type = Kokkos::Experimental::simd; - constexpr std::size_t width = simd_type::size(); - constexpr std::size_t num_cases = 8; - constexpr size_t alignment = - Kokkos::Experimental::simd::size() * sizeof(DataType); + if constexpr (is_type_v>) { + if constexpr (std::is_integral_v) { + using simd_type = Kokkos::Experimental::simd; + constexpr std::size_t width = simd_type::size(); + constexpr std::size_t num_cases = 16; + constexpr size_t alignment = + Kokkos::Experimental::simd::size() * sizeof(DataType); - DataType max = std::numeric_limits::max(); + DataType max = std::numeric_limits::max(); - alignas(alignment) DataType shift_by[num_cases] = { - 0, 1, 3, width / 2, width / 2 + 1, width - 1, width, width + 1}; - alignas(alignment) DataType test_vals[width]; - for (std::size_t i = 0; i < width; ++i) { - DataType inc = max / width; - test_vals[i] = i * inc + 1; - } + alignas(alignment) DataType shift_by[num_cases] = { + 0, 1, 3, width / 2, width / 2 + 1, width - 1, width, width + 1, + 0, 1, 3, width / 2, width / 2 + 1, width - 1, width, width + 1}; + alignas(alignment) DataType test_vals[width]; + for (std::size_t i = 0; i < width; ++i) { + DataType inc = max / width; + test_vals[i] = i * inc + 1; + } - host_check_shift_op_all_loaders(shift_right(), test_vals, shift_by, - num_cases); - host_check_shift_op_all_loaders(shift_left(), test_vals, shift_by, - num_cases); - - if constexpr (std::is_signed_v) { - for (std::size_t i = 0; i < width; ++i) test_vals[i] *= -1; host_check_shift_op_all_loaders(shift_right(), test_vals, shift_by, num_cases); host_check_shift_op_all_loaders(shift_left(), test_vals, shift_by, num_cases); + + if constexpr (std::is_signed_v) { + for (std::size_t i = 0; i < width; ++i) test_vals[i] *= -1; + host_check_shift_op_all_loaders(shift_right(), test_vals, shift_by, + num_cases); + } } } } @@ -224,33 +225,34 @@ KOKKOS_INLINE_FUNCTION void device_check_shift_op_all_loaders( template KOKKOS_INLINE_FUNCTION void device_check_shift_ops() { - if constexpr (std::is_integral_v) { - using simd_type = Kokkos::Experimental::simd; - constexpr std::size_t width = simd_type::size(); - constexpr std::size_t num_cases = 8; + if constexpr (is_type_v>) { + if constexpr (std::is_integral_v) { + using simd_type = Kokkos::Experimental::simd; + constexpr std::size_t width = simd_type::size(); + constexpr std::size_t num_cases = 16; - DataType max = Kokkos::reduction_identity::max(); + DataType max = Kokkos::reduction_identity::max(); - DataType shift_by[num_cases] = { - 0, 1, 3, width / 2, width / 2 + 1, width - 1, width, width + 1}; - DataType test_vals[width]; + DataType shift_by[num_cases] = { + 0, 1, 3, width / 2, width / 2 + 1, width - 1, width, width + 1, + 0, 1, 3, width / 2, width / 2 + 1, width - 1, width, width + 1}; + DataType test_vals[width]; - for (std::size_t i = 0; i < width; ++i) { - DataType inc = max / width; - test_vals[i] = i * inc + 1; - } + for (std::size_t i = 0; i < width; ++i) { + DataType inc = max / width; + test_vals[i] = i * inc + 1; + } - device_check_shift_op_all_loaders(shift_right(), test_vals, shift_by, - num_cases); - device_check_shift_op_all_loaders(shift_left(), test_vals, shift_by, - num_cases); - - if constexpr (std::is_signed_v) { - for (std::size_t i = 0; i < width; ++i) test_vals[i] *= -1; device_check_shift_op_all_loaders(shift_right(), test_vals, shift_by, num_cases); device_check_shift_op_all_loaders(shift_left(), test_vals, shift_by, num_cases); + + if constexpr (std::is_signed_v) { + for (std::size_t i = 0; i < width; ++i) test_vals[i] *= -1; + device_check_shift_op_all_loaders(shift_right(), test_vals, + shift_by, num_cases); + } } } } diff --git a/lib/kokkos/simd/unit_tests/include/TestSIMD_WhereExpressions.hpp b/lib/kokkos/simd/unit_tests/include/TestSIMD_WhereExpressions.hpp index 152fd9e984..904b2c665e 100644 --- a/lib/kokkos/simd/unit_tests/include/TestSIMD_WhereExpressions.hpp +++ b/lib/kokkos/simd/unit_tests/include/TestSIMD_WhereExpressions.hpp @@ -22,60 +22,66 @@ template inline void host_check_where_expr_scatter_to() { - using simd_type = Kokkos::Experimental::simd; - using index_type = Kokkos::Experimental::simd; - using mask_type = typename simd_type::mask_type; + if constexpr (is_type_v>) { + using simd_type = Kokkos::Experimental::simd; + using index_type = Kokkos::Experimental::simd; + using mask_type = typename simd_type::mask_type; - std::size_t nlanes = simd_type::size(); - DataType init[] = {11, 13, 17, 19, 23, 29, 31, 37}; - simd_type src; - src.copy_from(init, Kokkos::Experimental::simd_flag_default); + std::size_t nlanes = simd_type::size(); + DataType init[] = {11, 13, 17, 19, 23, 29, 31, 37, + 53, 71, 79, 83, 89, 93, 97, 103}; + simd_type src; + src.copy_from(init, Kokkos::Experimental::simd_flag_default); - for (std::size_t idx = 0; idx < nlanes; ++idx) { - mask_type mask(true); - mask[idx] = false; + for (std::size_t idx = 0; idx < nlanes; ++idx) { + mask_type mask(true); + mask[idx] = false; - DataType dst[8] = {0}; - index_type index; - simd_type expected_result; - for (std::size_t i = 0; i < nlanes; ++i) { - dst[i] = (2 + (i * 2)); - index[i] = i; - expected_result[i] = (mask[i]) ? src[index[i]] : dst[i]; + DataType dst[simd_type::size()] = {0}; + index_type index; + simd_type expected_result; + for (std::size_t i = 0; i < nlanes; ++i) { + dst[i] = (2 + (i * 2)); + index[i] = i; + expected_result[i] = (mask[i]) ? src[index[i]] : dst[i]; + } + where(mask, src).scatter_to(dst, index); + + simd_type dst_simd; + dst_simd.copy_from(dst, Kokkos::Experimental::simd_flag_default); + + host_check_equality(expected_result, dst_simd, nlanes); } - where(mask, src).scatter_to(dst, index); - - simd_type dst_simd; - dst_simd.copy_from(dst, Kokkos::Experimental::simd_flag_default); - - host_check_equality(expected_result, dst_simd, nlanes); } } template inline void host_check_where_expr_gather_from() { - using simd_type = Kokkos::Experimental::simd; - using index_type = Kokkos::Experimental::simd; - using mask_type = typename simd_type::mask_type; + if constexpr (is_type_v>) { + using simd_type = Kokkos::Experimental::simd; + using index_type = Kokkos::Experimental::simd; + using mask_type = typename simd_type::mask_type; - std::size_t nlanes = simd_type::size(); - DataType src[] = {11, 13, 17, 19, 23, 29, 31, 37}; + std::size_t nlanes = simd_type::size(); + DataType src[] = {11, 13, 17, 19, 23, 29, 31, 37, + 53, 71, 79, 83, 89, 93, 97, 103}; - for (std::size_t idx = 0; idx < nlanes; ++idx) { - mask_type mask(true); - mask[idx] = false; + for (std::size_t idx = 0; idx < nlanes; ++idx) { + mask_type mask(true); + mask[idx] = false; - simd_type dst; - index_type index; - simd_type expected_result; - for (std::size_t i = 0; i < nlanes; ++i) { - dst[i] = (2 + (i * 2)); - index[i] = i; - expected_result[i] = (mask[i]) ? src[index[i]] : dst[i]; + simd_type dst; + index_type index; + simd_type expected_result; + for (std::size_t i = 0; i < nlanes; ++i) { + dst[i] = (2 + (i * 2)); + index[i] = i; + expected_result[i] = (mask[i]) ? src[index[i]] : dst[i]; + } + where(mask, dst).gather_from(src, index); + + host_check_equality(expected_result, dst, nlanes); } - where(mask, dst).gather_from(src, index); - - host_check_equality(expected_result, dst, nlanes); } } @@ -100,33 +106,36 @@ inline void host_check_where_expr_all_abis( template KOKKOS_INLINE_FUNCTION void device_check_where_expr_scatter_to() { - using simd_type = Kokkos::Experimental::simd; - using index_type = Kokkos::Experimental::simd; - using mask_type = typename simd_type::mask_type; + if constexpr (is_type_v>) { + using simd_type = Kokkos::Experimental::simd; + using index_type = Kokkos::Experimental::simd; + using mask_type = typename simd_type::mask_type; - std::size_t nlanes = simd_type::size(); - DataType init[] = {11, 13, 17, 19, 23, 29, 31, 37}; - simd_type src; - src.copy_from(init, Kokkos::Experimental::simd_flag_default); + std::size_t nlanes = simd_type::size(); + DataType init[] = {11, 13, 17, 19, 23, 29, 31, 37, + 53, 71, 79, 83, 89, 93, 97, 103}; + simd_type src; + src.copy_from(init, Kokkos::Experimental::simd_flag_default); - for (std::size_t idx = 0; idx < nlanes; ++idx) { - mask_type mask(true); - mask[idx] = false; + for (std::size_t idx = 0; idx < nlanes; ++idx) { + mask_type mask(true); + mask[idx] = false; - DataType dst[8] = {0}; - index_type index; - simd_type expected_result; - for (std::size_t i = 0; i < nlanes; ++i) { - dst[i] = (2 + (i * 2)); - index[i] = i; - expected_result[i] = (mask[i]) ? src[index[i]] : dst[i]; + DataType dst[simd_type::size()] = {0}; + index_type index; + simd_type expected_result; + for (std::size_t i = 0; i < nlanes; ++i) { + dst[i] = (2 + (i * 2)); + index[i] = i; + expected_result[i] = (mask[i]) ? src[index[i]] : dst[i]; + } + where(mask, src).scatter_to(dst, index); + + simd_type dst_simd; + dst_simd.copy_from(dst, Kokkos::Experimental::simd_flag_default); + + device_check_equality(expected_result, dst_simd, nlanes); } - where(mask, src).scatter_to(dst, index); - - simd_type dst_simd; - dst_simd.copy_from(dst, Kokkos::Experimental::simd_flag_default); - - device_check_equality(expected_result, dst_simd, nlanes); } } @@ -137,7 +146,8 @@ KOKKOS_INLINE_FUNCTION void device_check_where_expr_gather_from() { using mask_type = typename simd_type::mask_type; std::size_t nlanes = simd_type::size(); - DataType src[] = {11, 13, 17, 19, 23, 29, 31, 37}; + DataType src[] = {11, 13, 17, 19, 23, 29, 31, 37, + 53, 71, 79, 83, 89, 93, 97, 103}; for (std::size_t idx = 0; idx < nlanes; ++idx) { mask_type mask(true); diff --git a/lib/kokkos/tpls/desul/include/desul/atomics/Adapt_HIP.hpp b/lib/kokkos/tpls/desul/include/desul/atomics/Adapt_HIP.hpp new file mode 100644 index 0000000000..0eab27fe98 --- /dev/null +++ b/lib/kokkos/tpls/desul/include/desul/atomics/Adapt_HIP.hpp @@ -0,0 +1,77 @@ +/* +Copyright (c) 2019, Lawrence Livermore National Security, LLC +and DESUL project contributors. See the COPYRIGHT file for details. +Source: https://github.com/desul/desul + +SPDX-License-Identifier: (BSD-3-Clause) +*/ + +#ifndef DESUL_ATOMICS_ADAPT_HIP_HPP_ +#define DESUL_ATOMICS_ADAPT_HIP_HPP_ + +#include + +namespace desul { +namespace Impl { + +// FIXME same code as GCCMemoryOrder +template +struct HIPMemoryOrder; + +template <> +struct HIPMemoryOrder { + static constexpr int value = __ATOMIC_RELAXED; +}; + +template <> +struct HIPMemoryOrder { + static constexpr int value = __ATOMIC_ACQUIRE; +}; + +template <> +struct HIPMemoryOrder { + static constexpr int value = __ATOMIC_RELEASE; +}; + +template <> +struct HIPMemoryOrder { + static constexpr int value = __ATOMIC_ACQ_REL; +}; + +template <> +struct HIPMemoryOrder { + static constexpr int value = __ATOMIC_SEQ_CST; +}; + +// __HIP_MEMORY_SCOPE_SYSTEM +// __HIP_MEMORY_SCOPE_AGENT +// __HIP_MEMORY_SCOPE_WORKGROUP +// __HIP_MEMORY_SCOPE_WAVEFRONT +// __HIP_MEMORY_SCOPE_SINGLETHREAD +template +struct HIPMemoryScope; + +template <> +struct HIPMemoryScope { + static constexpr int value = __HIP_MEMORY_SCOPE_WORKGROUP; +}; + +template <> +struct HIPMemoryScope { + static constexpr int value = __HIP_MEMORY_SCOPE_AGENT; +}; + +template <> +struct HIPMemoryScope { + static constexpr int value = __HIP_MEMORY_SCOPE_SYSTEM; +}; + +template <> +struct HIPMemoryScope { + static constexpr int value = __HIP_MEMORY_SCOPE_SYSTEM; +}; + +} // namespace Impl +} // namespace desul + +#endif diff --git a/lib/kokkos/tpls/desul/include/desul/atomics/Atomic_Ref.hpp b/lib/kokkos/tpls/desul/include/desul/atomics/Atomic_Ref.hpp index 3d69dcf6c5..e7f9239e03 100644 --- a/lib/kokkos/tpls/desul/include/desul/atomics/Atomic_Ref.hpp +++ b/lib/kokkos/tpls/desul/include/desul/atomics/Atomic_Ref.hpp @@ -6,533 +6,95 @@ Source: https://github.com/desul/desul SPDX-License-Identifier: (BSD-3-Clause) */ -#ifndef DESUL_ATOMIC_REF_IMPL_HPP_ -#define DESUL_ATOMIC_REF_IMPL_HPP_ +#ifndef DESUL_ATOMIC_REF_HPP_ +#define DESUL_ATOMIC_REF_HPP_ -#include #include #include #include -#include -#include namespace desul { -namespace Impl { -// TODO current implementation is missing the following: -// * member functions -// * wait -// * notify_one -// * notify_all - -template {}, - bool = std::is_floating_point{}> -struct basic_atomic_ref; - -// base class for non-integral, non-floating-point, non-pointer types template -struct basic_atomic_ref { - static_assert(std::is_trivially_copyable{}, ""); - - private: - T* _ptr; - - // 1/2/4/8/16-byte types must be aligned to at least their size - static constexpr int _min_alignment = (sizeof(T) & (sizeof(T) - 1)) || sizeof(T) > 16 - ? 0 - : sizeof(T); +class AtomicRef { + T* ptr_; public: using value_type = T; + using memory_order = MemoryOrder; + using memory_scope = MemoryScope; - static constexpr bool is_always_lock_free = atomic_always_lock_free(sizeof(T)); + DESUL_FUNCTION explicit AtomicRef(T& obj) : ptr_(&obj) {} - static constexpr std::size_t required_alignment = _min_alignment > alignof(T) - ? _min_alignment - : alignof(T); - - basic_atomic_ref() = delete; - basic_atomic_ref& operator=(basic_atomic_ref const&) = delete; - - basic_atomic_ref(basic_atomic_ref const&) = default; - - explicit basic_atomic_ref(T& obj) : _ptr(std::addressof(obj)) {} - - T operator=(T desired) const noexcept { - this->store(desired); + DESUL_FUNCTION T operator=(T desired) const noexcept { + store(desired); return desired; } - operator T() const noexcept { return this->load(); } + DESUL_FUNCTION operator T() const noexcept { return load(); } - template - DESUL_FUNCTION void store(T desired, - _MemoryOrder order = _MemoryOrder()) const noexcept { - atomic_store(_ptr, desired, order, MemoryScope()); + DESUL_FUNCTION T load() const noexcept { + return desul::atomic_load(ptr_, MemoryOrder(), MemoryScope()); } - template - DESUL_FUNCTION T load(_MemoryOrder order = _MemoryOrder()) const noexcept { - return atomic_load(_ptr, order, MemoryScope()); + DESUL_FUNCTION void store(T desired) const noexcept { + return desul::atomic_store(ptr_, desired, MemoryOrder(), MemoryScope()); } - template - DESUL_FUNCTION T exchange(T desired, - _MemoryOrder order = _MemoryOrder()) const noexcept { - return atomic_load(_ptr, desired, order, MemoryScope()); + DESUL_FUNCTION T exchange(T desired) const noexcept { + return desul::atomic_exchange(ptr_, desired, MemoryOrder(), MemoryScope()); } - DESUL_FUNCTION bool is_lock_free() const noexcept { - return atomic_is_lock_free(); + // TODO compare_exchange_{weak,strong} and is_lock_free + +#define DESUL_IMPL_DEFINE_ATOMIC_FETCH_OP(FETCH_OP, OP_FETCH) \ + DESUL_FUNCTION T FETCH_OP(T arg) const noexcept { \ + return desul::atomic_##FETCH_OP(ptr_, arg, MemoryOrder(), MemoryScope()); \ + } \ + DESUL_FUNCTION T OP_FETCH(T arg) const noexcept { \ + return desul::atomic_##OP_FETCH(ptr_, arg, MemoryOrder(), MemoryScope()); \ } - template - DESUL_FUNCTION bool compare_exchange_weak(T& expected, - T desired, - SuccessMemoryOrder success, - FailureMemoryOrder failure) const noexcept { - return atomic_compare_exchange_weak( - _ptr, expected, desired, success, failure, MemoryScope()); - } - - template - DESUL_FUNCTION bool compare_exchange_weak( - T& expected, T desired, _MemoryOrder order = _MemoryOrder()) const noexcept { - return compare_exchange_weak(expected, - desired, - order, - cmpexch_failure_memory_order<_MemoryOrder>(), - MemoryScope()); - } - - template - DESUL_FUNCTION bool compare_exchange_strong( - T& expected, - T desired, - SuccessMemoryOrder success, - FailureMemoryOrder failure) const noexcept { - return atomic_compare_exchange_strong( - _ptr, expected, desired, success, failure, MemoryScope()); - } - - template - DESUL_FUNCTION bool compare_exchange_strong( - T& expected, T desired, _MemoryOrder order = _MemoryOrder()) const noexcept { - return compare_exchange_strong(expected, - desired, - order, - cmpexch_failure_memory_order<_MemoryOrder>(), - MemoryScope()); - } -}; - -// base class for atomic_ref -template -struct basic_atomic_ref { - static_assert(std::is_integral{}, ""); - - private: - T* _ptr; - - public: - using value_type = T; - using difference_type = value_type; - - static constexpr bool is_always_lock_free = atomic_always_lock_free(sizeof(T)); - - static constexpr std::size_t required_alignment = sizeof(T) > alignof(T) ? sizeof(T) - : alignof(T); - - basic_atomic_ref() = delete; - basic_atomic_ref& operator=(basic_atomic_ref const&) = delete; - - explicit basic_atomic_ref(T& obj) : _ptr(&obj) {} - - basic_atomic_ref(basic_atomic_ref const&) = default; - - T operator=(T desired) const noexcept { - this->store(desired); - return desired; - } - - operator T() const noexcept { return this->load(); } - - template - DESUL_FUNCTION void store(T desired, - _MemoryOrder order = _MemoryOrder()) const noexcept { - atomic_store(_ptr, desired, order, MemoryScope()); - } - - template - DESUL_FUNCTION T load(_MemoryOrder order = _MemoryOrder()) const noexcept { - return atomic_load(_ptr, order, MemoryScope()); - } - - template - DESUL_FUNCTION T exchange(T desired, - _MemoryOrder order = _MemoryOrder()) const noexcept { - return atomic_load(_ptr, desired, order, MemoryScope()); - } - - DESUL_FUNCTION bool is_lock_free() const noexcept { - return atomic_is_lock_free(); - } - - template - DESUL_FUNCTION bool compare_exchange_weak(T& expected, - T desired, - SuccessMemoryOrder success, - FailureMemoryOrder failure) const noexcept { - return atomic_compare_exchange_weak( - _ptr, expected, desired, success, failure, MemoryScope()); - } - - template - DESUL_FUNCTION bool compare_exchange_weak( - T& expected, T desired, _MemoryOrder order = _MemoryOrder()) const noexcept { - return compare_exchange_weak(expected, - desired, - order, - cmpexch_failure_memory_order<_MemoryOrder>(), - MemoryScope()); - } - - template - DESUL_FUNCTION bool compare_exchange_strong( - T& expected, - T desired, - SuccessMemoryOrder success, - FailureMemoryOrder failure) const noexcept { - return atomic_compare_exchange_strong( - _ptr, expected, desired, success, failure, MemoryScope()); - } - - template - DESUL_FUNCTION bool compare_exchange_strong( - T& expected, T desired, _MemoryOrder order = _MemoryOrder()) const noexcept { - return compare_exchange_strong(expected, - desired, - order, - cmpexch_failure_memory_order<_MemoryOrder>(), - MemoryScope()); - } - - template - DESUL_FUNCTION value_type - fetch_add(value_type arg, _MemoryOrder order = _MemoryOrder()) const noexcept { - return atomic_fetch_add(_ptr, arg, order, MemoryScope()); - } - - template - DESUL_FUNCTION value_type - fetch_sub(value_type arg, _MemoryOrder order = _MemoryOrder()) const noexcept { - return atomic_fetch_sub(_ptr, arg, order, MemoryScope()); - } - - template - DESUL_FUNCTION value_type - fetch_and(value_type arg, _MemoryOrder order = _MemoryOrder()) const noexcept { - return atomic_fetch_and(_ptr, arg, order, MemoryScope()); - } - - template - DESUL_FUNCTION value_type - fetch_or(value_type arg, _MemoryOrder order = _MemoryOrder()) const noexcept { - return atomic_fetch_or(_ptr, arg, order, MemoryScope()); - } - - template - DESUL_FUNCTION value_type - fetch_xor(value_type arg, _MemoryOrder order = _MemoryOrder()) const noexcept { - return atomic_fetch_xor(_ptr, arg, order, MemoryScope()); - } - - DESUL_FUNCTION value_type operator++() const noexcept { - return atomic_add_fetch(_ptr, value_type(1), MemoryOrder(), MemoryScope()); - } - - DESUL_FUNCTION value_type operator++(int) const noexcept { return fetch_add(1); } - - DESUL_FUNCTION value_type operator--() const noexcept { - return atomic_sub_fetch(_ptr, value_type(1), MemoryOrder(), MemoryScope()); - } - - DESUL_FUNCTION value_type operator--(int) const noexcept { return fetch_sub(1); } - - DESUL_FUNCTION value_type operator+=(value_type arg) const noexcept { - atomic_add_fetch(_ptr, arg, MemoryOrder(), MemoryScope()); - } - - DESUL_FUNCTION value_type operator-=(value_type arg) const noexcept { - atomic_sub_fetch(_ptr, arg, MemoryOrder(), MemoryScope()); - } - - DESUL_FUNCTION value_type operator&=(value_type arg) const noexcept { - atomic_and_fetch(_ptr, arg, MemoryOrder(), MemoryScope()); - } - - DESUL_FUNCTION value_type operator|=(value_type arg) const noexcept { - atomic_or_fetch(_ptr, arg, MemoryOrder(), MemoryScope()); - } - - DESUL_FUNCTION value_type operator^=(value_type arg) const noexcept { - atomic_xor_fetch(_ptr, arg, MemoryOrder(), MemoryScope()); - } -}; - -// base class for atomic_ref -template -struct basic_atomic_ref { - static_assert(std::is_floating_point{}, ""); - - private: - T* _ptr; - - public: - using value_type = T; - using difference_type = value_type; - - static constexpr bool is_always_lock_free = atomic_always_lock_free(sizeof(T)); - - static constexpr std::size_t required_alignment = alignof(T); - - basic_atomic_ref() = delete; - basic_atomic_ref& operator=(basic_atomic_ref const&) = delete; - - explicit basic_atomic_ref(T& obj) : _ptr(&obj) {} - - basic_atomic_ref(basic_atomic_ref const&) = default; - - T operator=(T desired) const noexcept { - this->store(desired); - return desired; - } - - operator T() const noexcept { return this->load(); } - - template - DESUL_FUNCTION void store(T desired, - _MemoryOrder order = _MemoryOrder()) const noexcept { - atomic_store(_ptr, desired, order, MemoryScope()); - } - - template - DESUL_FUNCTION T load(_MemoryOrder order = _MemoryOrder()) const noexcept { - return atomic_load(_ptr, order, MemoryScope()); - } - - template - DESUL_FUNCTION T exchange(T desired, - _MemoryOrder order = _MemoryOrder()) const noexcept { - return atomic_load(_ptr, desired, order, MemoryScope()); - } - - DESUL_FUNCTION bool is_lock_free() const noexcept { - return atomic_is_lock_free(); - } - - template - DESUL_FUNCTION bool compare_exchange_weak(T& expected, - T desired, - SuccessMemoryOrder success, - FailureMemoryOrder failure) const noexcept { - return atomic_compare_exchange_weak( - _ptr, expected, desired, success, failure, MemoryScope()); - } - - template - DESUL_FUNCTION bool compare_exchange_weak( - T& expected, T desired, _MemoryOrder order = _MemoryOrder()) const noexcept { - return compare_exchange_weak(expected, - desired, - order, - cmpexch_failure_memory_order<_MemoryOrder>(), - MemoryScope()); - } - - template - DESUL_FUNCTION bool compare_exchange_strong( - T& expected, - T desired, - SuccessMemoryOrder success, - FailureMemoryOrder failure) const noexcept { - return atomic_compare_exchange_strong( - _ptr, expected, desired, success, failure, MemoryScope()); - } - - template - DESUL_FUNCTION bool compare_exchange_strong( - T& expected, T desired, _MemoryOrder order = _MemoryOrder()) const noexcept { - return compare_exchange_strong(expected, - desired, - order, - cmpexch_failure_memory_order<_MemoryOrder>(), - MemoryScope()); - } - - template - DESUL_FUNCTION value_type - fetch_add(value_type arg, _MemoryOrder order = _MemoryOrder()) const noexcept { - return atomic_fetch_add(_ptr, arg, order, MemoryScope()); - } - - template - DESUL_FUNCTION value_type - fetch_sub(value_type arg, _MemoryOrder order = _MemoryOrder()) const noexcept { - return atomic_fetch_sub(_ptr, arg, order, MemoryScope()); - } - - DESUL_FUNCTION value_type operator+=(value_type arg) const noexcept { - atomic_add_fetch(_ptr, arg, MemoryOrder(), MemoryScope()); - } - - DESUL_FUNCTION value_type operator-=(value_type arg) const noexcept { - atomic_sub_fetch(_ptr, arg, MemoryOrder(), MemoryScope()); - } -}; - -// base class for atomic_ref -template -struct basic_atomic_ref { - private: - T** _ptr; - - public: - using value_type = T*; - using difference_type = std::ptrdiff_t; - - static constexpr bool is_always_lock_free = atomic_always_lock_free(sizeof(T)); - - static constexpr std::size_t required_alignment = alignof(T*); - - basic_atomic_ref() = delete; - basic_atomic_ref& operator=(basic_atomic_ref const&) = delete; - - explicit basic_atomic_ref(T*& arg) : _ptr(std::addressof(arg)) {} - - basic_atomic_ref(basic_atomic_ref const&) = default; - - T* operator=(T* desired) const noexcept { - this->store(desired); - return desired; - } - - operator T*() const noexcept { return this->load(); } - - template - DESUL_FUNCTION void store(T* desired, - _MemoryOrder order = _MemoryOrder()) const noexcept { - atomic_store(_ptr, desired, order, MemoryScope()); - } - - template - DESUL_FUNCTION T* load(_MemoryOrder order = _MemoryOrder()) const noexcept { - return atomic_load(_ptr, order, MemoryScope()); - } - - template - DESUL_FUNCTION T* exchange(T* desired, - _MemoryOrder order = _MemoryOrder()) const noexcept { - return atomic_load(_ptr, desired, order, MemoryScope()); - } - - DESUL_FUNCTION bool is_lock_free() const noexcept { - return atomic_is_lock_free(); - } - - template - DESUL_FUNCTION bool compare_exchange_weak(T*& expected, - T* desired, - SuccessMemoryOrder success, - FailureMemoryOrder failure) const noexcept { - return atomic_compare_exchange_weak( - _ptr, expected, desired, success, failure, MemoryScope()); - } - - template - DESUL_FUNCTION bool compare_exchange_weak( - T*& expected, T* desired, _MemoryOrder order = _MemoryOrder()) const noexcept { - return compare_exchange_weak(expected, - desired, - order, - cmpexch_failure_memory_order<_MemoryOrder>(), - MemoryScope()); - } - - template - DESUL_FUNCTION bool compare_exchange_strong( - T*& expected, - T* desired, - SuccessMemoryOrder success, - FailureMemoryOrder failure) const noexcept { - return atomic_compare_exchange_strong( - _ptr, expected, desired, success, failure, MemoryScope()); - } - - template - DESUL_FUNCTION bool compare_exchange_strong( - T*& expected, T* desired, _MemoryOrder order = _MemoryOrder()) const noexcept { - return compare_exchange_strong(expected, - desired, - order, - cmpexch_failure_memory_order<_MemoryOrder>(), - MemoryScope()); - } - - template - DESUL_FUNCTION value_type - fetch_add(difference_type d, _MemoryOrder order = _MemoryOrder()) const noexcept { - return atomic_fetch_add(_ptr, _type_size(d), order, MemoryScope()); - } - - template - DESUL_FUNCTION value_type - fetch_sub(difference_type d, _MemoryOrder order = _MemoryOrder()) const noexcept { - return atomic_fetch_sub(_ptr, _type_size(d), order, MemoryScope()); - } - - DESUL_FUNCTION value_type operator++() const noexcept { - return atomic_add_fetch(_ptr, _type_size(1), MemoryOrder(), MemoryScope()); - } - - DESUL_FUNCTION value_type operator++(int) const noexcept { return fetch_add(1); } - - DESUL_FUNCTION value_type operator--() const noexcept { - return atomic_sub_fetch(_ptr, _type_size(1), MemoryOrder(), MemoryScope()); - } - - DESUL_FUNCTION value_type operator--(int) const noexcept { return fetch_sub(1); } - - DESUL_FUNCTION value_type operator+=(difference_type d) const noexcept { - atomic_add_fetch(_ptr, _type_size(d), MemoryOrder(), MemoryScope()); - } - - DESUL_FUNCTION value_type operator-=(difference_type d) const noexcept { - atomic_sub_fetch(_ptr, _type_size(d), MemoryOrder(), MemoryScope()); - } - - private: - static constexpr std::ptrdiff_t _type_size(std::ptrdiff_t d) noexcept { - static_assert(std::is_object{}, ""); - return d * sizeof(T); - } -}; - -} // namespace Impl - -template -struct scoped_atomic_ref : Impl::basic_atomic_ref { - explicit scoped_atomic_ref(T& obj) noexcept - : Impl::basic_atomic_ref(obj) {} - - scoped_atomic_ref& operator=(scoped_atomic_ref const&) = delete; - - scoped_atomic_ref(scoped_atomic_ref const&) = default; - - using Impl::basic_atomic_ref::operator=; +#define DESUL_IMPL_DEFINE_ATOMIC_COMPOUND_ASSIGNMENT_OP(COMPD_ASGMT, OP_FETCH) \ + DESUL_FUNCTION T operator COMPD_ASGMT(T arg) const noexcept { return OP_FETCH(arg); } + + DESUL_IMPL_DEFINE_ATOMIC_FETCH_OP(fetch_add, add_fetch) + DESUL_IMPL_DEFINE_ATOMIC_COMPOUND_ASSIGNMENT_OP(+=, add_fetch) + DESUL_IMPL_DEFINE_ATOMIC_FETCH_OP(fetch_sub, sub_fetch) + DESUL_IMPL_DEFINE_ATOMIC_COMPOUND_ASSIGNMENT_OP(-=, sub_fetch) + DESUL_IMPL_DEFINE_ATOMIC_FETCH_OP(fetch_min, min_fetch) + DESUL_IMPL_DEFINE_ATOMIC_FETCH_OP(fetch_max, max_fetch) + DESUL_IMPL_DEFINE_ATOMIC_FETCH_OP(fetch_mul, mul_fetch) + DESUL_IMPL_DEFINE_ATOMIC_COMPOUND_ASSIGNMENT_OP(*=, mul_fetch) + DESUL_IMPL_DEFINE_ATOMIC_FETCH_OP(fetch_div, div_fetch) + DESUL_IMPL_DEFINE_ATOMIC_COMPOUND_ASSIGNMENT_OP(/=, div_fetch) + DESUL_IMPL_DEFINE_ATOMIC_FETCH_OP(fetch_mod, mod_fetch) + DESUL_IMPL_DEFINE_ATOMIC_COMPOUND_ASSIGNMENT_OP(%=, mod_fetch) + DESUL_IMPL_DEFINE_ATOMIC_FETCH_OP(fetch_and, and_fetch) + DESUL_IMPL_DEFINE_ATOMIC_COMPOUND_ASSIGNMENT_OP(&=, and_fetch) + DESUL_IMPL_DEFINE_ATOMIC_FETCH_OP(fetch_or, or_fetch) + DESUL_IMPL_DEFINE_ATOMIC_COMPOUND_ASSIGNMENT_OP(|=, or_fetch) + DESUL_IMPL_DEFINE_ATOMIC_FETCH_OP(fetch_xor, xor_fetch) + DESUL_IMPL_DEFINE_ATOMIC_COMPOUND_ASSIGNMENT_OP(^=, xor_fetch) + DESUL_IMPL_DEFINE_ATOMIC_FETCH_OP(fetch_nand, nand_fetch) + +#undef DESUL_IMPL_DEFINE_ATOMIC_COMPOUND_ASSIGNMENT_OP +#undef DESUL_IMPL_DEFINE_ATOMIC_FETCH_OP + +#define DESUL_IMPL_DEFINE_ATOMIC_INCREMENT_DECREMENT(OPER, NAME) \ + DESUL_FUNCTION T fetch_##NAME() const noexcept { \ + return desul::atomic_fetch_##NAME(ptr_, MemoryOrder(), MemoryScope()); \ + } \ + DESUL_FUNCTION T NAME##_fetch() const noexcept { \ + return desul::atomic_##NAME##_fetch(ptr_, MemoryOrder(), MemoryScope()); \ + } \ + DESUL_FUNCTION T operator OPER() const noexcept { return NAME##_fetch(); } \ + DESUL_FUNCTION T operator OPER(int) const noexcept { return fetch_##NAME(); } + + DESUL_IMPL_DEFINE_ATOMIC_INCREMENT_DECREMENT(++, inc) + DESUL_IMPL_DEFINE_ATOMIC_INCREMENT_DECREMENT(--, dec) + +#undef DESUL_IMPL_DEFINE_ATOMIC_INCREMENT_DECREMENT }; } // namespace desul diff --git a/lib/kokkos/tpls/desul/include/desul/atomics/Compare_Exchange_HIP.hpp b/lib/kokkos/tpls/desul/include/desul/atomics/Compare_Exchange_HIP.hpp index 8c909bacdf..0ade34f25d 100644 --- a/lib/kokkos/tpls/desul/include/desul/atomics/Compare_Exchange_HIP.hpp +++ b/lib/kokkos/tpls/desul/include/desul/atomics/Compare_Exchange_HIP.hpp @@ -9,6 +9,7 @@ SPDX-License-Identifier: (BSD-3-Clause) #ifndef DESUL_ATOMICS_COMPARE_EXCHANGE_HIP_HPP_ #define DESUL_ATOMICS_COMPARE_EXCHANGE_HIP_HPP_ +#include #include #include #include @@ -17,130 +18,40 @@ SPDX-License-Identifier: (BSD-3-Clause) namespace desul { namespace Impl { -template -__device__ std::enable_if_t device_atomic_compare_exchange( - T* const dest, T compare, T value, MemoryOrderRelaxed, MemoryScope) { - static_assert(sizeof(unsigned int) == 4, - "this function assumes an unsigned int is 32-bit"); - unsigned int return_val = atomicCAS(reinterpret_cast(dest), - reinterpret_cast(compare), - reinterpret_cast(value)); - return reinterpret_cast(return_val); -} -template -__device__ std::enable_if_t device_atomic_compare_exchange( - T* const dest, T compare, T value, MemoryOrderRelaxed, MemoryScope) { - static_assert(sizeof(unsigned long long int) == 8, - "this function assumes an unsigned long long is 64-bit"); - unsigned long long int return_val = - atomicCAS(reinterpret_cast(dest), - reinterpret_cast(compare), - reinterpret_cast(value)); - return reinterpret_cast(return_val); -} +template +struct atomic_exchange_available_hip { + constexpr static bool value = + ((sizeof(T) == 1 && alignof(T) == 1) || (sizeof(T) == 4 && alignof(T) == 4) || + (sizeof(T) == 8 && alignof(T) == 8)) && + std::is_trivially_copyable::value; +}; -template -__device__ std::enable_if_t +template +__device__ std::enable_if_t::value, T> device_atomic_compare_exchange( - T* const dest, T compare, T value, MemoryOrderRelease, MemoryScope) { - T return_val = atomic_compare_exchange( - dest, compare, value, MemoryOrderRelaxed(), MemoryScope()); - atomic_thread_fence(MemoryOrderRelease(), MemoryScope()); - return return_val; + T* const dest, T compare, T value, MemoryOrder, MemoryScope) { + (void)__hip_atomic_compare_exchange_strong( + dest, + &compare, + value, + HIPMemoryOrder::value, + HIPMemoryOrder>::value, + HIPMemoryScope::value); + return compare; } -template -__device__ std::enable_if_t -device_atomic_compare_exchange( - T* const dest, T compare, T value, MemoryOrderAcquire, MemoryScope) { - atomic_thread_fence(MemoryOrderAcquire(), MemoryScope()); - T return_val = atomic_compare_exchange( - dest, compare, value, MemoryOrderRelaxed(), MemoryScope()); - return return_val; -} - -template -__device__ std::enable_if_t -device_atomic_compare_exchange( - T* const dest, T compare, T value, MemoryOrderAcqRel, MemoryScope) { - atomic_thread_fence(MemoryOrderAcquire(), MemoryScope()); - T return_val = atomic_compare_exchange( - dest, compare, value, MemoryOrderRelaxed(), MemoryScope()); - atomic_thread_fence(MemoryOrderRelease(), MemoryScope()); - return return_val; -} - -template -__device__ std::enable_if_t device_atomic_exchange( - T* const dest, T value, MemoryOrderRelaxed, MemoryScope) { - static_assert(sizeof(unsigned int) == 4, - "this function assumes an unsigned int is 32-bit"); - unsigned int return_val = atomicExch(reinterpret_cast(dest), - reinterpret_cast(value)); - return reinterpret_cast(return_val); -} -template -__device__ std::enable_if_t device_atomic_exchange( - T* const dest, T value, MemoryOrderRelaxed, MemoryScope) { - static_assert(sizeof(unsigned long long int) == 8, - "this function assumes an unsigned long long is 64-bit"); - unsigned long long int return_val = - atomicExch(reinterpret_cast(dest), - reinterpret_cast(value)); - return reinterpret_cast(return_val); -} - -template -__device__ std::enable_if_t device_atomic_exchange( - T* const dest, T compare, T value, MemoryOrderRelease, MemoryScope) { - T return_val = device_atomic_compare_exchange( - dest, compare, value, MemoryOrderRelaxed(), MemoryScope()); - device_atomic_thread_fence(MemoryOrderRelease(), MemoryScope()); - return reinterpret_cast(return_val); -} - -template -__device__ std::enable_if_t device_atomic_exchange( - T* const dest, T /*compare*/, T value, MemoryOrderAcquire, MemoryScope) { - device_atomic_thread_fence(MemoryOrderAcquire(), MemoryScope()); - T return_val = - device_atomic_exchange(dest, value, MemoryOrderRelaxed(), MemoryScope()); - return reinterpret_cast(return_val); -} - -template -__device__ std::enable_if_t device_atomic_exchange( - T* const dest, T value, MemoryOrderAcqRel, MemoryScope) { - device_atomic_thread_fence(MemoryOrderAcquire(), MemoryScope()); - T return_val = - device_atomic_exchange(dest, value, MemoryOrderRelaxed(), MemoryScope()); - device_atomic_thread_fence(MemoryOrderRelease(), MemoryScope()); - return reinterpret_cast(return_val); -} - -template -__device__ std::enable_if_t device_atomic_exchange( - T* const dest, T value, MemoryOrderSeqCst, MemoryScope) { - device_atomic_thread_fence(MemoryOrderAcquire(), MemoryScope()); - T return_val = - device_atomic_exchange(dest, value, MemoryOrderRelaxed(), MemoryScope()); - device_atomic_thread_fence(MemoryOrderRelease(), MemoryScope()); - return reinterpret_cast(return_val); -} - -template -__device__ std::enable_if_t -device_atomic_compare_exchange( - T* const dest, T compare, T value, MemoryOrderSeqCst, MemoryScope) { - device_atomic_thread_fence(MemoryOrderAcquire(), MemoryScope()); - T return_val = device_atomic_compare_exchange( - dest, compare, value, MemoryOrderRelaxed(), MemoryScope()); - device_atomic_thread_fence(MemoryOrderRelease(), MemoryScope()); +template +__device__ std::enable_if_t::value, T> +device_atomic_exchange(T* const dest, T value, MemoryOrder, MemoryScope) { + T return_val = __hip_atomic_exchange(dest, + value, + HIPMemoryOrder::value, + HIPMemoryScope::value); return return_val; } template -__device__ std::enable_if_t<(sizeof(T) != 8) && (sizeof(T) != 4), T> +__device__ std::enable_if_t::value, T> device_atomic_compare_exchange( T* const dest, T compare, T value, MemoryOrder, MemoryScope scope) { // This is a way to avoid deadlock in a warp or wave front @@ -169,7 +80,7 @@ device_atomic_compare_exchange( } template -__device__ std::enable_if_t<(sizeof(T) != 8) && (sizeof(T) != 4), T> +__device__ std::enable_if_t::value, T> device_atomic_exchange(T* const dest, T value, MemoryOrder, MemoryScope scope) { // This is a way to avoid deadlock in a warp or wave front T return_val; diff --git a/lib/kokkos/tpls/desul/include/desul/atomics/Fetch_Op_CUDA.hpp b/lib/kokkos/tpls/desul/include/desul/atomics/Fetch_Op_CUDA.hpp index 69ed8bcb9f..68622758d8 100644 --- a/lib/kokkos/tpls/desul/include/desul/atomics/Fetch_Op_CUDA.hpp +++ b/lib/kokkos/tpls/desul/include/desul/atomics/Fetch_Op_CUDA.hpp @@ -69,56 +69,56 @@ inline __device__ unsigned int device_atomic_fetch_inc_mod( unsigned int* inline __device__ unsigned int device_atomic_fetch_dec_mod( unsigned int* ptr, unsigned int val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicDec(ptr, val); } // clang-format on -#define DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP(OP, TYPE) \ +#define DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP(FETCH_OP, TYPE) \ template \ - __device__ TYPE device_atomic_fetch_##OP( \ + __device__ TYPE device_atomic_##FETCH_OP( \ TYPE* ptr, TYPE val, MemoryOrder, MemoryScopeDevice) { \ __threadfence(); \ TYPE return_val = \ - device_atomic_fetch_##OP(ptr, val, MemoryOrderRelaxed(), MemoryScopeDevice()); \ + device_atomic_##FETCH_OP(ptr, val, MemoryOrderRelaxed(), MemoryScopeDevice()); \ __threadfence(); \ return return_val; \ } \ template \ - __device__ TYPE device_atomic_fetch_##OP( \ + __device__ TYPE device_atomic_##FETCH_OP( \ TYPE* ptr, TYPE val, MemoryOrder, MemoryScopeCore) { \ - return device_atomic_fetch_##OP(ptr, val, MemoryOrder(), MemoryScopeDevice()); \ + return device_atomic_##FETCH_OP(ptr, val, MemoryOrder(), MemoryScopeDevice()); \ } -#define DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(OP) \ - DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP(OP, int) \ - DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP(OP, unsigned int) \ - DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP(OP, unsigned long long) +#define DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(FETCH_OP) \ + DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP(FETCH_OP, int) \ + DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP(FETCH_OP, unsigned int) \ + DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP(FETCH_OP, unsigned long long) #ifdef DESUL_CUDA_ARCH_IS_PRE_PASCAL -#define DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_FLOATING_POINT(OP) \ - DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP(OP, float) +#define DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_FLOATING_POINT(FETCH_OP) \ + DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP(FETCH_OP, float) #else -#define DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_FLOATING_POINT(OP) \ - DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP(OP, float) \ - DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP(OP, double) +#define DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_FLOATING_POINT(FETCH_OP) \ + DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP(FETCH_OP, float) \ + DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP(FETCH_OP, double) #endif -DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(min) -DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(max) -DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(and) -DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(or) -DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(xor) +DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(fetch_min) +DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(fetch_max) +DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(fetch_and) +DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(fetch_or) +DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(fetch_xor) -DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_FLOATING_POINT(add) -DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(add) -DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_FLOATING_POINT(sub) -DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(sub) +DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_FLOATING_POINT(fetch_add) +DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(fetch_add) +DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_FLOATING_POINT(fetch_sub) +DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(fetch_sub) -DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(inc) -DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(dec) +DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(fetch_inc) +DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(fetch_dec) -DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP(inc_mod, unsigned int) -DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP(dec_mod, unsigned int) +DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP(fetch_inc_mod, unsigned int) +DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP(fetch_dec_mod, unsigned int) #undef DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_FLOATING_POINT #undef DESUL_IMPL_CUDA_DEVICE_ATOMIC_FETCH_OP_INTEGRAL diff --git a/lib/kokkos/tpls/desul/include/desul/atomics/Fetch_Op_Generic.hpp b/lib/kokkos/tpls/desul/include/desul/atomics/Fetch_Op_Generic.hpp index a94ff8ef18..530195a832 100644 --- a/lib/kokkos/tpls/desul/include/desul/atomics/Fetch_Op_Generic.hpp +++ b/lib/kokkos/tpls/desul/include/desul/atomics/Fetch_Op_Generic.hpp @@ -18,38 +18,38 @@ SPDX-License-Identifier: (BSD-3-Clause) namespace desul { namespace Impl { -#define DESUL_IMPL_ATOMIC_FETCH_OP(ANNOTATION, HOST_OR_DEVICE, OP) \ - template \ - ANNOTATION T HOST_OR_DEVICE##_atomic_fetch_##OP( \ - T* const dest, const T val, MemoryOrder order, MemoryScope scope) { \ - return HOST_OR_DEVICE##_atomic_fetch_oper( \ - OP##_operator(), dest, val, order, scope); \ - } \ - template \ - ANNOTATION T HOST_OR_DEVICE##_atomic_##OP##_fetch( \ - T* const dest, const T val, MemoryOrder order, MemoryScope scope) { \ - return HOST_OR_DEVICE##_atomic_oper_fetch( \ - OP##_operator(), dest, val, order, scope); \ +#define DESUL_IMPL_ATOMIC_FETCH_OP(ANNOTATION, HOST_OR_DEVICE, FETCH_OP, OP_FETCH) \ + template \ + ANNOTATION T HOST_OR_DEVICE##_atomic_##FETCH_OP( \ + T* const dest, const T val, MemoryOrder order, MemoryScope scope) { \ + return HOST_OR_DEVICE##_atomic_fetch_oper( \ + OP_FETCH##_operator(), dest, val, order, scope); \ + } \ + template \ + ANNOTATION T HOST_OR_DEVICE##_atomic_##OP_FETCH( \ + T* const dest, const T val, MemoryOrder order, MemoryScope scope) { \ + return HOST_OR_DEVICE##_atomic_oper_fetch( \ + OP_FETCH##_operator(), dest, val, order, scope); \ } -#define DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(OP) \ - DESUL_IMPL_ATOMIC_FETCH_OP(DESUL_IMPL_HOST_FUNCTION, host, OP) \ - DESUL_IMPL_ATOMIC_FETCH_OP(DESUL_IMPL_DEVICE_FUNCTION, device, OP) +#define DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(FETCH_OP, OP_FETCH) \ + DESUL_IMPL_ATOMIC_FETCH_OP(DESUL_IMPL_HOST_FUNCTION, host, FETCH_OP, OP_FETCH) \ + DESUL_IMPL_ATOMIC_FETCH_OP(DESUL_IMPL_DEVICE_FUNCTION, device, FETCH_OP, OP_FETCH) -DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(add) -DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(sub) -DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(max) -DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(min) -DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(mul) -DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(div) -DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(mod) -DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(and) -DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(or) -DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(xor) -DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(nand) +DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(fetch_add, add_fetch) +DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(fetch_sub, sub_fetch) +DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(fetch_max, max_fetch) +DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(fetch_min, min_fetch) +DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(fetch_mul, mul_fetch) +DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(fetch_div, div_fetch) +DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(fetch_mod, mod_fetch) +DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(fetch_and, and_fetch) +DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(fetch_or, or_fetch) +DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(fetch_xor, xor_fetch) +DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(fetch_nand, nand_fetch) -DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(inc_mod) -DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(dec_mod) +DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(fetch_inc_mod, inc_mod_fetch) +DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(fetch_dec_mod, dec_mod_fetch) #undef DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE #undef DESUL_IMPL_ATOMIC_FETCH_OP @@ -59,13 +59,13 @@ DESUL_IMPL_ATOMIC_FETCH_OP_HOST_AND_DEVICE(dec_mod) ANNOTATION T HOST_OR_DEVICE##_atomic_fetch_##OP( \ T* const dest, const unsigned int val, MemoryOrder order, MemoryScope scope) { \ return HOST_OR_DEVICE##_atomic_fetch_oper( \ - OP##_operator(), dest, val, order, scope); \ + OP##_fetch_operator(), dest, val, order, scope); \ } \ template \ ANNOTATION T HOST_OR_DEVICE##_atomic_##OP##_fetch( \ T* const dest, const unsigned int val, MemoryOrder order, MemoryScope scope) { \ return HOST_OR_DEVICE##_atomic_oper_fetch( \ - OP##_operator(), dest, val, order, scope); \ + OP##_fetch_operator(), dest, val, order, scope); \ } #define DESUL_IMPL_ATOMIC_FETCH_OP_SHIFT_HOST_AND_DEVICE(OP) \ @@ -78,19 +78,21 @@ DESUL_IMPL_ATOMIC_FETCH_OP_SHIFT_HOST_AND_DEVICE(rshift) #undef DESUL_IMPL_ATOMIC_FETCH_OP_SHIFT_HOST_AND_DEVICE #undef DESUL_IMPL_ATOMIC_FETCH_OP_SHIFT -#define DESUL_IMPL_ATOMIC_LOAD_AND_STORE(ANNOTATION, HOST_OR_DEVICE) \ - template \ - ANNOTATION T HOST_OR_DEVICE##_atomic_load( \ - const T* const dest, MemoryOrder order, MemoryScope scope) { \ - return HOST_OR_DEVICE##_atomic_fetch_oper( \ - load_operator(), const_cast(dest), T(), order, scope); \ - } \ - \ - template \ - ANNOTATION void HOST_OR_DEVICE##_atomic_store( \ - T* const dest, const T val, MemoryOrder order, MemoryScope scope) { \ - (void)HOST_OR_DEVICE##_atomic_fetch_oper( \ - store_operator(), dest, val, order, scope); \ +// NOTE: using atomic_oper_fetch in the fallback implementation of atomic_store to avoid +// reading potentially uninitialized values which would yield undefined behavior. +#define DESUL_IMPL_ATOMIC_LOAD_AND_STORE(ANNOTATION, HOST_OR_DEVICE) \ + template \ + ANNOTATION T HOST_OR_DEVICE##_atomic_load( \ + const T* const dest, MemoryOrder order, MemoryScope scope) { \ + return HOST_OR_DEVICE##_atomic_fetch_oper( \ + load_fetch_operator(), const_cast(dest), T(), order, scope); \ + } \ + \ + template \ + ANNOTATION void HOST_OR_DEVICE##_atomic_store( \ + T* const dest, const T val, MemoryOrder order, MemoryScope scope) { \ + (void)HOST_OR_DEVICE##_atomic_oper_fetch( \ + store_fetch_operator(), dest, val, order, scope); \ } DESUL_IMPL_ATOMIC_LOAD_AND_STORE(DESUL_IMPL_HOST_FUNCTION, host) diff --git a/lib/kokkos/tpls/desul/include/desul/atomics/Fetch_Op_HIP.hpp b/lib/kokkos/tpls/desul/include/desul/atomics/Fetch_Op_HIP.hpp index e9c749809d..8d9bd86825 100644 --- a/lib/kokkos/tpls/desul/include/desul/atomics/Fetch_Op_HIP.hpp +++ b/lib/kokkos/tpls/desul/include/desul/atomics/Fetch_Op_HIP.hpp @@ -9,99 +9,108 @@ SPDX-License-Identifier: (BSD-3-Clause) #ifndef DESUL_ATOMICS_FECH_OP_HIP_HPP_ #define DESUL_ATOMICS_FECH_OP_HIP_HPP_ +#include + namespace desul { namespace Impl { -// clang-format off -inline __device__ int device_atomic_fetch_add( int* ptr, int val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicAdd(ptr, val); } -inline __device__ unsigned int device_atomic_fetch_add( unsigned int* ptr, unsigned int val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicAdd(ptr, val); } -inline __device__ unsigned long long device_atomic_fetch_add(unsigned long long* ptr, unsigned long long val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicAdd(ptr, val); } -inline __device__ float device_atomic_fetch_add( float* ptr, float val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicAdd(ptr, val); } -inline __device__ double device_atomic_fetch_add( double* ptr, double val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicAdd(ptr, val); } - -inline __device__ int device_atomic_fetch_sub( int* ptr, int val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicSub(ptr, val); } -inline __device__ unsigned int device_atomic_fetch_sub( unsigned int* ptr, unsigned int val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicSub(ptr, val); } -inline __device__ unsigned long long device_atomic_fetch_sub(unsigned long long* ptr, unsigned long long val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicAdd(ptr, -val); } -inline __device__ float device_atomic_fetch_sub( float* ptr, float val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicAdd(ptr, -val); } -inline __device__ double device_atomic_fetch_sub( double* ptr, double val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicAdd(ptr, -val); } - -inline __device__ int device_atomic_fetch_min( int* ptr, int val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicMin(ptr, val); } -inline __device__ unsigned int device_atomic_fetch_min( unsigned int* ptr, unsigned int val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicMin(ptr, val); } -inline __device__ unsigned long long device_atomic_fetch_min(unsigned long long* ptr, unsigned long long val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicMin(ptr, val); } - -inline __device__ int device_atomic_fetch_max( int* ptr, int val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicMax(ptr, val); } -inline __device__ unsigned int device_atomic_fetch_max( unsigned int* ptr, unsigned int val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicMax(ptr, val); } -inline __device__ unsigned long long device_atomic_fetch_max(unsigned long long* ptr, unsigned long long val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicMax(ptr, val); } - -inline __device__ int device_atomic_fetch_and( int* ptr, int val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicAnd(ptr, val); } -inline __device__ unsigned int device_atomic_fetch_and( unsigned int* ptr, unsigned int val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicAnd(ptr, val); } -inline __device__ unsigned long long device_atomic_fetch_and(unsigned long long* ptr, unsigned long long val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicAnd(ptr, val); } - -inline __device__ int device_atomic_fetch_or ( int* ptr, int val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicOr (ptr, val); } -inline __device__ unsigned int device_atomic_fetch_or ( unsigned int* ptr, unsigned int val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicOr (ptr, val); } -inline __device__ unsigned long long device_atomic_fetch_or (unsigned long long* ptr, unsigned long long val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicOr (ptr, val); } - -inline __device__ int device_atomic_fetch_xor( int* ptr, int val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicXor(ptr, val); } -inline __device__ unsigned int device_atomic_fetch_xor( unsigned int* ptr, unsigned int val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicXor(ptr, val); } -inline __device__ unsigned long long device_atomic_fetch_xor(unsigned long long* ptr, unsigned long long val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicXor(ptr, val); } - -inline __device__ int device_atomic_fetch_inc( int* ptr, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicAdd(ptr, 1 ); } -inline __device__ unsigned int device_atomic_fetch_inc( unsigned int* ptr, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicAdd(ptr, 1u ); } -inline __device__ unsigned long long device_atomic_fetch_inc(unsigned long long* ptr, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicAdd(ptr, 1ull); } - -inline __device__ int device_atomic_fetch_dec( int* ptr, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicSub(ptr, 1 ); } -inline __device__ unsigned int device_atomic_fetch_dec( unsigned int* ptr, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicSub(ptr, 1u ); } -inline __device__ unsigned long long device_atomic_fetch_dec(unsigned long long* ptr, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicAdd(ptr, -1 ); } - -inline __device__ unsigned int device_atomic_fetch_inc_mod( unsigned int* ptr, unsigned int val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicInc(ptr, val); } -inline __device__ unsigned int device_atomic_fetch_dec_mod( unsigned int* ptr, unsigned int val, MemoryOrderRelaxed, MemoryScopeDevice) { return atomicDec(ptr, val); } -// clang-format on - -#define DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP(OP, TYPE) \ - template \ - __device__ TYPE device_atomic_fetch_##OP( \ - TYPE* ptr, TYPE val, MemoryOrder, MemoryScopeDevice) { \ - __threadfence(); \ - TYPE return_val = \ - device_atomic_fetch_##OP(ptr, val, MemoryOrderRelaxed(), MemoryScopeDevice()); \ - __threadfence(); \ - return return_val; \ - } \ - template \ - __device__ TYPE device_atomic_fetch_##OP( \ - TYPE* ptr, TYPE val, MemoryOrder, MemoryScopeCore) { \ - return device_atomic_fetch_##OP(ptr, val, MemoryOrder(), MemoryScopeDevice()); \ +#define DESUL_IMPL_HIP_ATOMIC_FETCH_OP(OP, T) \ + template \ + __device__ inline T device_atomic_fetch_##OP( \ + T* ptr, T val, MemoryOrder, MemoryScope) { \ + return __hip_atomic_fetch_##OP(ptr, \ + val, \ + HIPMemoryOrder::value, \ + HIPMemoryScope::value); \ } -#define DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(OP) \ - DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP(OP, int) \ - DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP(OP, unsigned int) \ - DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP(OP, unsigned long long) +#define DESUL_IMPL_HIP_ATOMIC_FETCH_OP_INTEGRAL(OP) \ + DESUL_IMPL_HIP_ATOMIC_FETCH_OP(OP, int) \ + DESUL_IMPL_HIP_ATOMIC_FETCH_OP(OP, long long) \ + DESUL_IMPL_HIP_ATOMIC_FETCH_OP(OP, unsigned int) \ + DESUL_IMPL_HIP_ATOMIC_FETCH_OP(OP, unsigned long long) -#define DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP_FLOATING_POINT(OP) \ - DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP(OP, float) \ - DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP(OP, double) +#define DESUL_IMPL_HIP_ATOMIC_FETCH_OP_FLOATING_POINT(OP) \ + DESUL_IMPL_HIP_ATOMIC_FETCH_OP(OP, float) \ + DESUL_IMPL_HIP_ATOMIC_FETCH_OP(OP, double) -DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(min) -DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(max) -DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(and) -DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(or) -DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(xor) +DESUL_IMPL_HIP_ATOMIC_FETCH_OP_INTEGRAL(add) +DESUL_IMPL_HIP_ATOMIC_FETCH_OP_INTEGRAL(min) +DESUL_IMPL_HIP_ATOMIC_FETCH_OP_INTEGRAL(max) +DESUL_IMPL_HIP_ATOMIC_FETCH_OP_INTEGRAL(and) +DESUL_IMPL_HIP_ATOMIC_FETCH_OP_INTEGRAL(or) +DESUL_IMPL_HIP_ATOMIC_FETCH_OP_INTEGRAL(xor) +DESUL_IMPL_HIP_ATOMIC_FETCH_OP_FLOATING_POINT(add) +// atomic min/max gives the wrong results (tested with ROCm 6.0 on Frontier) +// DESUL_IMPL_HIP_ATOMIC_FETCH_OP_FLOATING_POINT(min) +// DESUL_IMPL_HIP_ATOMIC_FETCH_OP_FLOATING_POINT(max) -DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP_FLOATING_POINT(add) -DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(add) -DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP_FLOATING_POINT(sub) -DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(sub) +#undef DESUL_IMPL_HIP_ATOMIC_FETCH_OP_FLOATING_POINT +#undef DESUL_IMPL_HIP_ATOMIC_FETCH_OP_INTEGRAL +#undef DESUL_IMPL_HIP_ATOMIC_FETCH_OP -DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(inc) -DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP_INTEGRAL(dec) +#define DESUL_IMPL_HIP_ATOMIC_FETCH_SUB(T) \ + template \ + __device__ inline T device_atomic_fetch_sub( \ + T* ptr, T val, MemoryOrder, MemoryScope) { \ + return __hip_atomic_fetch_add(ptr, \ + -val, \ + HIPMemoryOrder::value, \ + HIPMemoryScope::value); \ + } -DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP(inc_mod, unsigned int) -DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP(dec_mod, unsigned int) +DESUL_IMPL_HIP_ATOMIC_FETCH_SUB(int) +DESUL_IMPL_HIP_ATOMIC_FETCH_SUB(long long) +DESUL_IMPL_HIP_ATOMIC_FETCH_SUB(unsigned int) +DESUL_IMPL_HIP_ATOMIC_FETCH_SUB(unsigned long long) +DESUL_IMPL_HIP_ATOMIC_FETCH_SUB(float) +DESUL_IMPL_HIP_ATOMIC_FETCH_SUB(double) -#undef DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP_FLOATING_POINT -#undef DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP_INTEGRAL -#undef DESUL_IMPL_HIP_DEVICE_ATOMIC_FETCH_OP +#undef DESUL_IMPL_HIP_ATOMIC_FETCH_SUB + +#define DESUL_IMPL_HIP_ATOMIC_FETCH_INC(T) \ + template \ + __device__ inline T device_atomic_fetch_inc(T* ptr, MemoryOrder, MemoryScope) { \ + return __hip_atomic_fetch_add(ptr, \ + 1, \ + HIPMemoryOrder::value, \ + HIPMemoryScope::value); \ + } \ + template \ + __device__ inline T device_atomic_fetch_dec(T* ptr, MemoryOrder, MemoryScope) { \ + return __hip_atomic_fetch_add(ptr, \ + -1, \ + HIPMemoryOrder::value, \ + HIPMemoryScope::value); \ + } + +DESUL_IMPL_HIP_ATOMIC_FETCH_INC(int) +DESUL_IMPL_HIP_ATOMIC_FETCH_INC(long long) +DESUL_IMPL_HIP_ATOMIC_FETCH_INC(unsigned int) +DESUL_IMPL_HIP_ATOMIC_FETCH_INC(unsigned long long) + +#undef DESUL_IMPL_HIP_ATOMIC_FETCH_INC + +#define DESUL_IMPL_HIP_ATOMIC_FETCH_INC_MOD(MEMORY_SCOPE, MEMORY_SCOPE_STRING_LITERAL) \ + template \ + __device__ inline unsigned int device_atomic_fetch_inc_mod( \ + unsigned int* ptr, unsigned int val, MemoryOrder, MEMORY_SCOPE) { \ + return __builtin_amdgcn_atomic_inc32( \ + ptr, val, HIPMemoryOrder::value, MEMORY_SCOPE_STRING_LITERAL); \ + } \ + template \ + __device__ inline unsigned int device_atomic_fetch_dec_mod( \ + unsigned int* ptr, unsigned int val, MemoryOrder, MEMORY_SCOPE) { \ + return __builtin_amdgcn_atomic_dec32( \ + ptr, val, HIPMemoryOrder::value, MEMORY_SCOPE_STRING_LITERAL); \ + } + +DESUL_IMPL_HIP_ATOMIC_FETCH_INC_MOD(MemoryScopeCore, "workgroup") +DESUL_IMPL_HIP_ATOMIC_FETCH_INC_MOD(MemoryScopeDevice, "agent") +DESUL_IMPL_HIP_ATOMIC_FETCH_INC_MOD(MemoryScopeNode, "") +DESUL_IMPL_HIP_ATOMIC_FETCH_INC_MOD(MemoryScopeSystem, "") + +#undef DESUL_IMPL_HIP_ATOMIC_FETCH_INC_MOD } // namespace Impl } // namespace desul diff --git a/lib/kokkos/tpls/desul/include/desul/atomics/Operator_Function_Objects.hpp b/lib/kokkos/tpls/desul/include/desul/atomics/Operator_Function_Objects.hpp index be90cdbbd8..1f5159c4f8 100644 --- a/lib/kokkos/tpls/desul/include/desul/atomics/Operator_Function_Objects.hpp +++ b/lib/kokkos/tpls/desul/include/desul/atomics/Operator_Function_Objects.hpp @@ -18,7 +18,7 @@ namespace desul { namespace Impl { template -struct max_operator { +struct max_fetch_operator { DESUL_FORCEINLINE_FUNCTION static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) { return (val1 > val2 ? val1 : val2); @@ -30,7 +30,7 @@ struct max_operator { }; template -struct min_operator { +struct min_fetch_operator { DESUL_FORCEINLINE_FUNCTION static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) { return (val1 < val2 ? val1 : val2); @@ -70,55 +70,55 @@ constexpr DESUL_FUNCTION } template -struct add_operator { +struct add_fetch_operator { DESUL_FORCEINLINE_FUNCTION static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) { return val1 + val2; } }; template -struct sub_operator { +struct sub_fetch_operator { DESUL_FORCEINLINE_FUNCTION static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) { return val1 - val2; } }; template -struct mul_operator { +struct mul_fetch_operator { DESUL_FORCEINLINE_FUNCTION static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) { return val1 * val2; } }; template -struct div_operator { +struct div_fetch_operator { DESUL_FORCEINLINE_FUNCTION static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) { return val1 / val2; } }; template -struct mod_operator { +struct mod_fetch_operator { DESUL_FORCEINLINE_FUNCTION static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) { return val1 % val2; } }; template -struct and_operator { +struct and_fetch_operator { DESUL_FORCEINLINE_FUNCTION static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) { return val1 & val2; } }; template -struct or_operator { +struct or_fetch_operator { DESUL_FORCEINLINE_FUNCTION static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) { return val1 | val2; } }; template -struct xor_operator { +struct xor_fetch_operator { DESUL_FORCEINLINE_FUNCTION static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) { return val1 ^ val2; } }; template -struct nand_operator { +struct nand_fetch_operator { DESUL_FORCEINLINE_FUNCTION static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) { return ~(val1 & val2); @@ -126,7 +126,7 @@ struct nand_operator { }; template -struct lshift_operator { +struct lshift_fetch_operator { DESUL_FORCEINLINE_FUNCTION static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) { return val1 << val2; @@ -134,7 +134,7 @@ struct lshift_operator { }; template -struct rshift_operator { +struct rshift_fetch_operator { DESUL_FORCEINLINE_FUNCTION static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) { return val1 >> val2; @@ -142,7 +142,7 @@ struct rshift_operator { }; template -struct inc_mod_operator { +struct inc_mod_fetch_operator { DESUL_FORCEINLINE_FUNCTION static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) { return ((val1 >= val2) ? Scalar1(0) : val1 + Scalar1(1)); @@ -150,7 +150,7 @@ struct inc_mod_operator { }; template -struct dec_mod_operator { +struct dec_mod_fetch_operator { DESUL_FORCEINLINE_FUNCTION static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) { return (((val1 == Scalar1(0)) | (val1 > val2)) ? val2 : (val1 - Scalar1(1))); @@ -158,13 +158,13 @@ struct dec_mod_operator { }; template -struct store_operator { +struct store_fetch_operator { DESUL_FORCEINLINE_FUNCTION static Scalar1 apply(const Scalar1&, const Scalar2& val2) { return val2; } }; template -struct load_operator { +struct load_fetch_operator { DESUL_FORCEINLINE_FUNCTION static Scalar1 apply(const Scalar1& val1, const Scalar2&) { return val1; } }; diff --git a/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/config.hpp b/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/config.hpp index 8e42a37ba7..24166462e7 100644 --- a/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/config.hpp +++ b/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/config.hpp @@ -205,7 +205,7 @@ static_assert(_MDSPAN_CPLUSPLUS >= MDSPAN_CXX_STD_14, "mdspan requires C++14 or #endif #ifndef _MDSPAN_USE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION -# if (!defined(__NVCC__) || (__CUDACC_VER_MAJOR__ >= 11 && __CUDACC_VER_MINOR__ >= 7)) && \ +# if (!defined(__NVCC__) || (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__ * 10 >= 1170)) && \ ((defined(__cpp_deduction_guides) && __cpp_deduction_guides >= 201703) || \ (!defined(__cpp_deduction_guides) && MDSPAN_HAS_CXX_17)) # define _MDSPAN_USE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION 1 diff --git a/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/extents.hpp b/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/extents.hpp index 9a28c3ed5c..d58d37732d 100644 --- a/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/extents.hpp +++ b/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/extents.hpp @@ -16,12 +16,15 @@ #pragma once #include "dynamic_extent.hpp" +#include "utility.hpp" #ifdef __cpp_lib_span #include #endif #include +#include +#include #include namespace MDSPAN_IMPL_STANDARD_NAMESPACE { @@ -30,6 +33,7 @@ namespace detail { // Function used to check compatibility of extents in converting constructor // can't be a private member function for some reason. template +MDSPAN_INLINE_FUNCTION static constexpr std::integral_constant __check_compatible_extents( std::integral_constant, std::integer_sequence, @@ -46,6 +50,7 @@ struct __compare_extent_compatible : std::integral_constant +MDSPAN_INLINE_FUNCTION static constexpr std::integral_constant< bool, _MDSPAN_FOLD_AND(__compare_extent_compatible::value)> __check_compatible_extents( @@ -59,8 +64,8 @@ template MDSPAN_INLINE_FUNCTION static constexpr bool are_valid_indices() { return - (std::is_convertible::value && ... && true) && - (std::is_nothrow_constructible::value && ... && true); + _MDSPAN_FOLD_AND(std::is_convertible::value) && + _MDSPAN_FOLD_AND(std::is_nothrow_constructible::value); } // ------------------------------------------------------------------ @@ -538,14 +543,9 @@ public: MDSPAN_INLINE_FUNCTION friend constexpr bool operator==(const extents &lhs, const extents &rhs) noexcept { - if constexpr (rank() != extents::rank()) { - return false; - } else { - using common_t = std::common_type_t; - for (size_type r = 0; r < m_rank; r++) - if(static_cast(rhs.extent(r)) != static_cast(lhs.extent(r))) return false; - } - return true; + return + rank() == extents::rank() && + detail::rankwise_equal(detail::with_rank{}, rhs, lhs, detail::extent); } #if !(MDSPAN_HAS_CXX_20) @@ -614,5 +614,80 @@ static #endif constexpr bool __is_extents_v = __is_extents::value; +template +MDSPAN_INLINE_FUNCTION +constexpr void +check_lower_bound(InputIndexType user_index, + ExtentsIndexType /* current_extent */, + std::true_type /* is_signed */) +{ + (void) user_index; // prevent unused variable warning +#ifdef _MDSPAN_DEBUG + assert(static_cast(user_index) >= 0); +#endif +} + +template +MDSPAN_INLINE_FUNCTION +constexpr void +check_lower_bound(InputIndexType /* user_index */, + ExtentsIndexType /* current_extent */, + std::false_type /* is_signed */) +{} + +template +MDSPAN_INLINE_FUNCTION +constexpr void +check_upper_bound(InputIndexType user_index, + ExtentsIndexType current_extent) +{ + (void) user_index; // prevent unused variable warnings + (void) current_extent; +#ifdef _MDSPAN_DEBUG + assert(static_cast(user_index) < current_extent); +#endif +} + +// Returning true to use AND fold instead of comma +// CPP14 mode doesn't like the use of void expressions +// with the way the _MDSPAN_FOLD_AND is set up +template +MDSPAN_INLINE_FUNCTION +constexpr bool +check_one_index(InputIndex user_index, + ExtentsIndexType current_extent) +{ + check_lower_bound(user_index, current_extent, + std::integral_constant::value>{}); + check_upper_bound(user_index, current_extent); + return true; +} + +template +MDSPAN_INLINE_FUNCTION +constexpr void +check_all_indices_helper(std::index_sequence, + const extents& exts, + Indices... indices) +{ + // Suppress warning about statement has no effect + (void) _MDSPAN_FOLD_AND( + (check_one_index(indices, exts.extent(RankIndices))) + ); +} + +template +MDSPAN_INLINE_FUNCTION +constexpr void +check_all_indices(const extents& exts, + Indices... indices) +{ + check_all_indices_helper(std::make_index_sequence(), + exts, indices...); +} + } // namespace detail } // namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/layout_left.hpp b/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/layout_left.hpp index 83ed9ef7fe..222fba7aa0 100644 --- a/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/layout_left.hpp +++ b/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/layout_left.hpp @@ -18,8 +18,11 @@ #include "macros.hpp" #include "trait_backports.hpp" #include "extents.hpp" +#include "layout_stride.hpp" +#include "utility.hpp" +#if MDSPAN_HAS_CXX_17 #include "../__p2642_bits/layout_padded_fwd.hpp" -#include +#endif #include namespace MDSPAN_IMPL_STANDARD_NAMESPACE { @@ -133,11 +136,11 @@ class layout_left::mapping { : __extents(__other.extents()) { MDSPAN_IMPL_PROPOSED_NAMESPACE::detail:: - check_padded_layout_converting_constructor_mandates(); + check_padded_layout_converting_constructor_mandates< + extents_type, _Mapping>(detail::with_rank{}); MDSPAN_IMPL_PROPOSED_NAMESPACE::detail:: check_padded_layout_converting_constructor_preconditions< - extents_type>(__other); + extents_type>(detail::with_rank{}, __other); } #endif @@ -156,17 +159,7 @@ class layout_left::mapping { * TODO: check precondition * other.required_span_size() is a representable value of type index_type */ - #if !defined(_MDSPAN_HAS_CUDA) && !defined(_MDSPAN_HAS_HIP) && !defined(NDEBUG) - if constexpr (extents_type::rank() > 0) { - index_type stride = 1; - using common_t = std::common_type_t; - for(rank_type r=0; r<__extents.rank(); r++) { - if(static_cast(stride) != static_cast(other.stride(r))) - std::abort(); // ("Assigning layout_stride to layout_left with invalid strides."); - stride *= __extents.extent(r); - } - } - #endif + detail::validate_strides(detail::with_rank{}, layout_left{}, __extents, other); } MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED mapping& operator=(mapping const&) noexcept = default; @@ -194,6 +187,9 @@ class layout_left::mapping { ) _MDSPAN_HOST_DEVICE constexpr index_type operator()(Indices... idxs) const noexcept { +#if ! defined(NDEBUG) + detail::check_all_indices(this->extents(), idxs...); +#endif // ! NDEBUG return __compute_offset(__rank_count<0, extents_type::rank()>(), static_cast(idxs)...); } diff --git a/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/layout_right.hpp b/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/layout_right.hpp index 3d3927df7b..284569f653 100644 --- a/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/layout_right.hpp +++ b/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/layout_right.hpp @@ -18,9 +18,11 @@ #include "macros.hpp" #include "trait_backports.hpp" #include "extents.hpp" -#include #include "layout_stride.hpp" +#include "utility.hpp" +#if MDSPAN_HAS_CXX_17 #include "../__p2642_bits/layout_padded_fwd.hpp" +#endif namespace MDSPAN_IMPL_STANDARD_NAMESPACE { @@ -134,11 +136,11 @@ class layout_right::mapping { : __extents(__other.extents()) { MDSPAN_IMPL_PROPOSED_NAMESPACE::detail:: - check_padded_layout_converting_constructor_mandates(); + check_padded_layout_converting_constructor_mandates< + extents_type, _Mapping>(detail::with_rank{}); MDSPAN_IMPL_PROPOSED_NAMESPACE::detail:: check_padded_layout_converting_constructor_preconditions< - extents_type>(__other); + extents_type>(detail::with_rank{}, __other); } #endif @@ -157,17 +159,7 @@ class layout_right::mapping { * TODO: check precondition * other.required_span_size() is a representable value of type index_type */ - #if !defined(_MDSPAN_HAS_CUDA) && !defined(_MDSPAN_HAS_HIP) && !defined(NDEBUG) - if constexpr (extents_type::rank() > 0) { - index_type stride = 1; - using common_t = std::common_type_t; - for(rank_type r=__extents.rank(); r>0; r--) { - if(static_cast(stride) != static_cast(other.stride(r-1))) - std::abort(); // ("Assigning layout_stride to layout_right with invalid strides."); - stride *= __extents.extent(r-1); - } - } - #endif + detail::validate_strides(detail::with_rank{}, layout_right{}, __extents, other); } MDSPAN_INLINE_FUNCTION_DEFAULTED _MDSPAN_CONSTEXPR_14_DEFAULTED mapping& operator=(mapping const&) noexcept = default; @@ -195,6 +187,9 @@ class layout_right::mapping { ) _MDSPAN_HOST_DEVICE constexpr index_type operator()(Indices... idxs) const noexcept { +#if ! defined(NDEBUG) + detail::check_all_indices(this->extents(), idxs...); +#endif // ! NDEBUG return __compute_offset(__rank_count<0, extents_type::rank()>(), static_cast(idxs)...); } diff --git a/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/layout_stride.hpp b/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/layout_stride.hpp index 15ad577d14..d6cdad2ab2 100644 --- a/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/layout_stride.hpp +++ b/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/layout_stride.hpp @@ -19,14 +19,16 @@ #include "extents.hpp" #include "trait_backports.hpp" #include "compressed_pair.hpp" +#include "utility.hpp" #if !defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) # include "no_unique_address.hpp" #endif -#include -#include #include +#include +#include + #ifdef __cpp_lib_span #include #endif @@ -38,11 +40,11 @@ namespace MDSPAN_IMPL_STANDARD_NAMESPACE { struct layout_left { template - class mapping; + class mapping; }; struct layout_right { template - class mapping; + class mapping; }; namespace detail { @@ -79,6 +81,7 @@ namespace detail { std::bool_constant::value; }; #endif + } // namespace detail struct layout_stride { @@ -199,6 +202,20 @@ struct layout_stride { return __strides_storage_t{static_cast(s[Idxs])...}; } + MDSPAN_TEMPLATE_REQUIRES( + class IntegralType, + // The is_convertible condition is added to make sfinae valid + // the extents_type::rank() > 0 is added to avoid use of non-standard zero length c-array + (std::is_convertible::value && (extents_type::rank() > 0)) + ) + MDSPAN_INLINE_FUNCTION + // despite the requirement some compilers still complain about zero length array during parsing + // making it length 1 now, but since the thing can't be instantiated due to requirement the actual + // instantiation of strides_storage will not fail despite mismatching length + static constexpr const __strides_storage_t fill_strides(mdspan_non_standard_tag, const IntegralType (&s)[extents_type::rank()>0?extents_type::rank():1]) { + return __strides_storage_t{static_cast(s[Idxs])...}; + } + #ifdef __cpp_lib_span template MDSPAN_INLINE_FUNCTION @@ -225,7 +242,11 @@ struct layout_stride { // Can't use defaulted parameter in the __deduction_workaround template because of a bug in MSVC warning C4348. using __impl = __deduction_workaround>; - static constexpr __strides_storage_t strides_storage(std::true_type) { + static constexpr __strides_storage_t strides_storage(detail::with_rank<0>) { + return {}; + } + template + static constexpr __strides_storage_t strides_storage(detail::with_rank) { __strides_storage_t s{}; extents_type e; @@ -237,9 +258,6 @@ struct layout_stride { return s; } - static constexpr __strides_storage_t strides_storage(std::false_type) { - return {}; - } //---------------------------------------------------------------------------- @@ -262,7 +280,7 @@ struct layout_stride { : __base_t(__base_t{__member_pair_t( #endif extents_type(), - __strides_storage_t(strides_storage(std::integral_constant 0)>{})) + __strides_storage_t(strides_storage(detail::with_rank{})) #if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) } #else @@ -309,6 +327,48 @@ struct layout_stride { */ } + MDSPAN_TEMPLATE_REQUIRES( + class IntegralTypes, + /* requires */ ( + // MSVC 19.32 does not like using index_type here, requires the typename Extents::index_type + // error C2641: cannot deduce template arguments for 'MDSPAN_IMPL_STANDARD_NAMESPACE::layout_stride::mapping' + _MDSPAN_TRAIT(std::is_convertible, const std::remove_const_t&, typename Extents::index_type) && + _MDSPAN_TRAIT(std::is_nothrow_constructible, typename Extents::index_type, const std::remove_const_t&) && + (Extents::rank() > 0) + ) + ) + MDSPAN_INLINE_FUNCTION + constexpr + mapping( + mdspan_non_standard_tag, + extents_type const& e, + // despite the requirement some compilers still complain about zero length array during parsing + // making it length 1 now, but since the thing can't be instantiated due to requirement the actual + // instantiation of strides_storage will not fail despite mismatching length + IntegralTypes (&s)[extents_type::rank()>0?extents_type::rank():1] + ) noexcept +#if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) + : __members{ +#else + : __base_t(__base_t{__member_pair_t( +#endif + e, __strides_storage_t(__impl::fill_strides(mdspan_non_standard, s)) +#if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS) + } +#else + )}) +#endif + { + /* + * TODO: check preconditions + * - s[i] > 0 is true for all i in the range [0, rank_ ). + * - REQUIRED-SPAN-SIZE(e, s) is a representable value of type index_type ([basic.fundamental]). + * - If rank_ is greater than 0, then there exists a permutation P of the integers in the + * range [0, rank_), such that s[ pi ] >= s[ pi − 1 ] * e.extent( pi − 1 ) is true for + * all i in the range [1, rank_ ), where pi is the ith element of P. + */ + } + #ifdef __cpp_lib_span MDSPAN_TEMPLATE_REQUIRES( class IntegralTypes, @@ -434,6 +494,9 @@ struct layout_stride { ) MDSPAN_FORCE_INLINE_FUNCTION constexpr index_type operator()(Indices... idxs) const noexcept { +#if ! defined(NDEBUG) + detail::check_all_indices(this->extents(), idxs...); +#endif // ! NDEBUG return static_cast(__impl::_call_op_impl(*this, static_cast(idxs)...)); } @@ -444,32 +507,48 @@ struct layout_stride { MDSPAN_INLINE_FUNCTION static constexpr bool is_always_strided() noexcept { return true; } MDSPAN_INLINE_FUNCTION static constexpr bool is_unique() noexcept { return true; } - MDSPAN_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 bool is_exhaustive() const noexcept { - if constexpr (extents_type::rank() == 0) - return true; - else { - index_type span_size = required_span_size(); - if (span_size == static_cast(0)) { - if constexpr (extents_type::rank() == 1) { - return stride(0) == 1; - } else { - rank_type r_largest = 0; - for (rank_type r = 1; r < extents_type::rank(); r++) { - if (stride(r) > stride(r_largest)) { - r_largest = r; - } - } - for (rank_type r = 0; r < extents_type::rank(); r++) { - if (extents().extent(r) == 0 && r != r_largest) { - return false; - } - } - return true; - } - } else { - return required_span_size() == __get_size(extents(), std::make_index_sequence()); + + private: + constexpr bool exhaustive_for_nonzero_span_size() const + { + return required_span_size() == __get_size(extents(), std::make_index_sequence()); + } + + constexpr bool is_exhaustive_impl(detail::with_rank<0>) const + { + return true; + } + constexpr bool is_exhaustive_impl(detail::with_rank<1>) const + { + if (required_span_size() != static_cast(0)) { + return exhaustive_for_nonzero_span_size(); + } + return stride(0) == 1; + } + template + constexpr bool is_exhaustive_impl(detail::with_rank) const + { + if (required_span_size() != static_cast(0)) { + return exhaustive_for_nonzero_span_size(); + } + + rank_type r_largest = 0; + for (rank_type r = 1; r < extents_type::rank(); r++) { + if (stride(r) > stride(r_largest)) { + r_largest = r; } } + for (rank_type r = 0; r < extents_type::rank(); r++) { + if (extents().extent(r) == 0 && r != r_largest) { + return false; + } + } + return true; + } + + public: + MDSPAN_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 bool is_exhaustive() const noexcept { + return is_exhaustive_impl(detail::with_rank{}); } MDSPAN_INLINE_FUNCTION static constexpr bool is_strided() noexcept { return true; } @@ -498,15 +577,9 @@ struct layout_stride { #endif MDSPAN_INLINE_FUNCTION friend constexpr bool operator==(const mapping& x, const StridedLayoutMapping& y) noexcept { - bool strides_match = true; - if constexpr (extents_type::rank() > 0) { - using common_t = std::common_type_t; - for(rank_type r = 0; r < extents_type::rank(); r++) - strides_match = strides_match && (static_cast(x.stride(r)) == static_cast(y.stride(r))); - } return (x.extents() == y.extents()) && (__impl::__OFFSET(y) == static_cast(0)) && - strides_match; + detail::rankwise_equal(detail::with_rank{}, x, y, detail::stride); } // This one is not technically part of the proposal. Just here to make implementation a bit more optimal hopefully @@ -532,7 +605,7 @@ struct layout_stride { ) MDSPAN_INLINE_FUNCTION friend constexpr bool operator!=(const mapping& x, const StridedLayoutMapping& y) noexcept { - return not (x == y); + return !(x == y); } MDSPAN_TEMPLATE_REQUIRES( @@ -561,4 +634,34 @@ struct layout_stride { }; }; +namespace detail { + +template +constexpr void validate_strides(with_rank<0>, Layout, const Extents&, const Mapping&) +{} + +template +constexpr void validate_strides(with_rank, Layout, const Extents& ext, const Mapping& other) +{ + static_assert(std::is_same::value && + (std::is_same::value || + std::is_same::value) + , "This function is only intended to validate construction of " + "a layout_left or layout_right mapping from a layout_stride mapping."); + + constexpr auto is_left = std::is_same::value; + + typename Extents::index_type expected_stride = 1; + + for (std::size_t r = 0; r < N; r++) { + const std::size_t s = is_left ? r : N - 1 - r; + + MDSPAN_IMPL_PRECONDITION(common_integral_compare(expected_stride, other.stride(s)) + && "invalid strides for layout_{left,right}"); + + expected_stride *= ext.extent(s); + } +} + +} // namespace detail } // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/macros.hpp b/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/macros.hpp index 3eeb39755c..b60c426177 100644 --- a/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/macros.hpp +++ b/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/macros.hpp @@ -18,7 +18,12 @@ #include "config.hpp" +#include +#include #include // std::is_void +#if defined(_MDSPAN_HAS_CUDA) || defined(_MDSPAN_HAS_HIP) || defined(_MDSPAN_HAS_SYCL) +#include "assert.h" +#endif #ifndef _MDSPAN_HOST_DEVICE # if defined(_MDSPAN_HAS_CUDA) || defined(_MDSPAN_HAS_HIP) @@ -101,6 +106,69 @@ #define MDSPAN_IMPL_STANDARD_NAMESPACE_STRING MDSPAN_PP_STRINGIFY(MDSPAN_IMPL_STANDARD_NAMESPACE) #define MDSPAN_IMPL_PROPOSED_NAMESPACE_STRING MDSPAN_PP_STRINGIFY(MDSPAN_IMPL_STANDARD_NAMESPACE) "::" MDSPAN_PP_STRINGIFY(MDSPAN_IMPL_PROPOSED_NAMESPACE) +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace detail { + +#if defined(_MDSPAN_HAS_CUDA) || defined(_MDSPAN_HAS_HIP) +MDSPAN_FUNCTION inline void default_precondition_violation_handler(const char* cond, const char* file, unsigned line) +{ + printf("%s:%u: precondition failure: `%s`\n", file, line, cond); + assert(0); +} +#elif defined(_MDSPAN_HAS_SYCL) +MDSPAN_FUNCTION inline void default_precondition_violation_handler(const char* cond, const char* file, unsigned line) +{ + sycl::ext::oneapi::experimental::printf("%s:%u: precondition failure: `%s`\n", file, line, cond); + assert(0); +} +#else +MDSPAN_FUNCTION inline void default_precondition_violation_handler(const char* cond, const char* file, unsigned line) +{ + std::fprintf(stderr, "%s:%u: precondition failure: `%s`\n", file, line, cond); + std::abort(); +} +#endif + +} // namespace detail +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE + +#ifndef MDSPAN_IMPL_PRECONDITION_VIOLATION_HANDLER +#define MDSPAN_IMPL_PRECONDITION_VIOLATION_HANDLER(cond, file, line) \ + MDSPAN_IMPL_STANDARD_NAMESPACE::detail::default_precondition_violation_handler(cond, file, line) +#endif + +#ifndef MDSPAN_IMPL_CHECK_PRECONDITION + #ifndef NDEBUG + #define MDSPAN_IMPL_CHECK_PRECONDITION 0 + #else + #define MDSPAN_IMPL_CHECK_PRECONDITION 1 + #endif +#endif + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace detail { + +template +MDSPAN_FUNCTION constexpr void precondition(const char* cond, const char* file, unsigned line) +{ + if (!check) { return; } + // in case the macro doesn't use the arguments for custom macros + (void) cond; + (void) file; + (void) line; + MDSPAN_IMPL_PRECONDITION_VIOLATION_HANDLER(cond, file, line); +} + +} // namespace detail +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE + +#define MDSPAN_IMPL_PRECONDITION(...) \ + do { \ + if (!(__VA_ARGS__)) { \ + MDSPAN_IMPL_STANDARD_NAMESPACE::detail::precondition(#__VA_ARGS__, __FILE__, __LINE__); \ + } \ + } while (0) + // end Preprocessor helpers }}}1 //============================================================================== @@ -574,7 +642,7 @@ __fold_left_assign_impl(Args&&... args) { template -constexpr __mdspan_enable_fold_comma __fold_comma_impl(Args&&... args) noexcept { return { }; } +constexpr __mdspan_enable_fold_comma __fold_comma_impl(Args&&...) noexcept { return { }; } template struct __bools; diff --git a/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/mdspan.hpp b/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/mdspan.hpp index d6ec49e65b..23114aa550 100644 --- a/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/mdspan.hpp +++ b/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/mdspan.hpp @@ -34,6 +34,8 @@ class mdspan private: static_assert(detail::__is_extents_v, MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::mdspan's Extents template parameter must be a specialization of " MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::extents."); + static_assert(std::is_same::value, + MDSPAN_IMPL_STANDARD_NAMESPACE_STRING "::mdspan's ElementType template parameter must be the same as its AccessorPolicy::element_type."); // Workaround for non-deducibility of the index sequence template parameter if it's given at the top level template @@ -321,7 +323,7 @@ public: #endif // MDSPAN_USE_PAREN_OPERATOR MDSPAN_INLINE_FUNCTION constexpr size_type size() const noexcept { - return __impl::__size(*this); + return static_cast(__impl::__size(*this)); }; MDSPAN_INLINE_FUNCTION constexpr bool empty() const noexcept { diff --git a/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/utility.hpp b/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/utility.hpp new file mode 100644 index 0000000000..e690cd6939 --- /dev/null +++ b/lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/utility.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace detail { + +// type alias used for rank-based tag dispatch +// +// this is used to enable alternatives to constexpr if when building for C++14 +// +template +using with_rank = std::integral_constant; + +template +MDSPAN_INLINE_FUNCTION +constexpr bool common_integral_compare(I1 x, I2 y) +{ + static_assert(std::is_integral::value && + std::is_integral::value, ""); + + using I = std::common_type_t; + return static_cast(x) == static_cast(y); +} + +template +MDSPAN_INLINE_FUNCTION +constexpr bool rankwise_equal(with_rank<0>, const T1&, const T2&, F) +{ + return true; +} + +template +MDSPAN_INLINE_FUNCTION +constexpr bool rankwise_equal(with_rank, const T1& x, const T2& y, F func) +{ + bool match = true; + + for (std::size_t r = 0; r < N; r++) { + match = match && common_integral_compare(func(x, r), func(y, r)); + } + + return match; +} + +constexpr struct +{ + template + MDSPAN_INLINE_FUNCTION + constexpr auto operator()(const T& x, I i) const + { + return x.extent(i); + } +} extent; + +constexpr struct +{ + template + MDSPAN_INLINE_FUNCTION + constexpr auto operator()(const T& x, I i) const + { + return x.stride(i); + } +} stride; + +} // namespace detail + +constexpr struct mdspan_non_standard_tag { +} mdspan_non_standard; + +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/lib/kokkos/core/unit_test/hip/TestHIP_Graph.cpp b/lib/kokkos/tpls/mdspan/include/experimental/__p2389_bits/dims.hpp similarity index 59% rename from lib/kokkos/core/unit_test/hip/TestHIP_Graph.cpp rename to lib/kokkos/tpls/mdspan/include/experimental/__p2389_bits/dims.hpp index 405cb76c64..00045215c4 100644 --- a/lib/kokkos/core/unit_test/hip/TestHIP_Graph.cpp +++ b/lib/kokkos/tpls/mdspan/include/experimental/__p2389_bits/dims.hpp @@ -14,5 +14,15 @@ // //@HEADER -#include -#include +#pragma once + +// backward compatibility import into experimental +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace MDSPAN_IMPL_PROPOSED_NAMESPACE { + +template< ::std::size_t Rank, class IndexType = std::size_t> +using dims = + :: MDSPAN_IMPL_STANDARD_NAMESPACE :: dextents; + +} // namespace MDSPAN_IMPL_PROPOSED_NAMESPACE +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/lib/kokkos/tpls/mdspan/include/experimental/__p2630_bits/submdspan_mapping.hpp b/lib/kokkos/tpls/mdspan/include/experimental/__p2630_bits/submdspan_mapping.hpp index ca6948c9a9..e1390fdeb5 100644 --- a/lib/kokkos/tpls/mdspan/include/experimental/__p2630_bits/submdspan_mapping.hpp +++ b/lib/kokkos/tpls/mdspan/include/experimental/__p2630_bits/submdspan_mapping.hpp @@ -17,10 +17,30 @@ #pragma once #include -#include #include +#include #include // index_sequence +// Suppress spurious warning with NVCC about no return statement. +// This is a known issue in NVCC and NVC++ +// Depending on the CUDA and GCC version we need both the builtin +// and the diagnostic push. I tried really hard to find something shorter +// but no luck ... +#if defined __NVCC__ +#ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ +#pragma nv_diagnostic push +#pragma nv_diag_suppress = implicit_return_from_non_void_function +#else +#ifdef __CUDA_ARCH__ +#pragma diagnostic push +#pragma diag_suppress implicit_return_from_non_void_function +#endif +#endif +#elif defined __NVCOMPILER +#pragma diagnostic push +#pragma diag_suppress = implicit_return_from_non_void_function +#endif + namespace MDSPAN_IMPL_STANDARD_NAMESPACE { //****************************************** // Return type of submdspan_mapping overloads @@ -31,18 +51,68 @@ template struct submdspan_mapping_result { }; namespace detail { +// We use const Slice& and not Slice&& because the various +// submdspan_mapping_impl overloads use their slices arguments +// multiple times. This makes perfect forwarding not useful, but we +// still don't want to pass those (possibly of size 64 x 3 bits) +// objects by value. +template +MDSPAN_INLINE_FUNCTION constexpr bool +one_slice_out_of_bounds(const IndexType &ext, const Slice &slice) { + using common_t = + std::common_type_t; + return static_cast(detail::first_of(slice)) == + static_cast(ext); +} + +template +MDSPAN_INLINE_FUNCTION constexpr bool +any_slice_out_of_bounds_helper(std::index_sequence, + const extents &exts, + const Slices &... slices) { + return _MDSPAN_FOLD_OR( + (one_slice_out_of_bounds(exts.extent(RankIndices), slices))); +} + +template +MDSPAN_INLINE_FUNCTION constexpr bool +any_slice_out_of_bounds(const extents &exts, + const Slices &... slices) { + return any_slice_out_of_bounds_helper( + std::make_index_sequence(), exts, slices...); +} + // constructs sub strides template -MDSPAN_INLINE_FUNCTION -constexpr auto -construct_sub_strides(const SrcMapping &src_mapping, - std::index_sequence, - const std::tuple &slices_stride_factor) { +MDSPAN_INLINE_FUNCTION constexpr auto construct_sub_strides( + const SrcMapping &src_mapping, std::index_sequence, + const std::tuple &slices_stride_factor) { using index_type = typename SrcMapping::index_type; return std::array{ (static_cast(src_mapping.stride(InvMapIdxs)) * static_cast(std::get(slices_stride_factor)))...}; } + +template +struct is_range_slice { + constexpr static bool value = + std::is_same_v || + std::is_convertible_v>; +}; + +template +constexpr bool is_range_slice_v = is_range_slice::value; + +template +struct is_index_slice { + constexpr static bool value = std::is_convertible_v; +}; + +template +constexpr bool is_index_slice_v = is_index_slice::value; + } // namespace detail //********************************** @@ -51,52 +121,90 @@ construct_sub_strides(const SrcMapping &src_mapping, namespace detail { // Figure out whether to preserve layout_left -template -struct preserve_layout_left_mapping; +template +struct deduce_layout_left_submapping; -template -struct preserve_layout_left_mapping, SubRank, - SliceSpecifiers...> { - constexpr static bool value = - // Preserve layout for rank 0 - (SubRank == 0) || - ( - // Slice specifiers up to subrank need to be full_extent_t - except - // for the last one which could also be tuple but not a strided index - // range slice specifiers after subrank are integrals - ((Idx > SubRank - 1) || // these are only integral slice specifiers - (std::is_same_v) || - ((Idx == SubRank - 1) && - std::is_convertible_v>)) && - ...); +template +struct deduce_layout_left_submapping< + IndexType, SubRank, std::index_sequence, SliceSpecifiers...> { + + using count_range = index_sequence_scan_impl< + 0u, (is_index_slice_v ? 0u : 1u)...>; + + constexpr static int gap_len = + (((Idx > 0 && count_range::get(Idx) == 1 && + is_index_slice_v) + ? 1 + : 0) + + ... + 0); + + MDSPAN_INLINE_FUNCTION + constexpr static bool layout_left_value() { + // Use layout_left for rank 0 + if constexpr (SubRank == 0) { + return true; + // Use layout_left for rank 1 result if leftmost slice specifier is range like + } else if constexpr (SubRank == 1) { + return ((Idx > 0 || is_range_slice_v)&&...); + } else { + // Preserve if leftmost SubRank-1 slices are full_extent_t and + // the slice at idx Subrank - 1 is a range and + // for idx > SubRank the slice is an index + return ((((Idx < SubRank - 1) && std::is_same_v) || + ((Idx == SubRank - 1) && is_range_slice_v) || + ((Idx > SubRank - 1) && is_index_slice_v)) && ...); + } +#if defined(__NVCC__) && !defined(__CUDA_ARCH__) && defined(__GNUC__) + __builtin_unreachable(); +#endif + } + + MDSPAN_INLINE_FUNCTION + constexpr static bool layout_left_padded_value() { + // Technically could also keep layout_left_padded for SubRank==0 + // and SubRank==1 with leftmost slice specifier being a contiguous range + // but we intercept these cases separately + + // In all other cases: + // leftmost slice must be range + // then there can be a gap with index slices + // then SubRank - 2 full_extent slices + // then another range slice + // then more index slices + // e.g. R I I I F F F R I I for obtaining a rank-5 from a rank-10 + return ((((Idx == 0) && is_range_slice_v) || + ((Idx > 0 && Idx <= gap_len) && is_index_slice_v) || + ((Idx > gap_len && Idx < gap_len + SubRank - 1) && std::is_same_v) || + ((Idx == gap_len + SubRank - 1) && is_range_slice_v) || + ((Idx > gap_len + SubRank - 1) && is_index_slice_v)) && ... ); + } }; + +// We are reusing the same thing for layout_left and layout_left_padded +// For layout_left as source StaticStride is static_extent(0) +template +struct compute_s_static_layout_left { + // Neither StaticStride nor any of the provided extents can be zero. + // StaticStride can never be zero, the static_extents we are looking at are associated with + // integral slice specifiers - which wouldn't be valid for zero extent + template + MDSPAN_INLINE_FUNCTION + static constexpr size_t value(std::index_sequence) { + size_t val = ((Idx>0 && Idx<=NumGaps ? (Extents::static_extent(Idx) == dynamic_extent?0:Extents::static_extent(Idx)) : 1) * ... * (StaticStride == dynamic_extent?0:StaticStride)); + return val == 0?dynamic_extent:val; + } +}; + } // namespace detail -// Suppress spurious warning with NVCC about no return statement. -// This is a known issue in NVCC and NVC++ -// Depending on the CUDA and GCC version we need both the builtin -// and the diagnostic push. I tried really hard to find something shorter -// but no luck ... -#if defined __NVCC__ - #ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ - #pragma nv_diagnostic push - #pragma nv_diag_suppress = implicit_return_from_non_void_function - #else - #ifdef __CUDA_ARCH__ - #pragma diagnostic push - #pragma diag_suppress implicit_return_from_non_void_function - #endif - #endif -#elif defined __NVCOMPILER - #pragma diagnostic push - #pragma diag_suppress = implicit_return_from_non_void_function -#endif // Actual submdspan mapping call template template -MDSPAN_INLINE_FUNCTION -constexpr auto -layout_left::mapping::submdspan_mapping_impl(SliceSpecifiers... slices) const { +MDSPAN_INLINE_FUNCTION constexpr auto +layout_left::mapping::submdspan_mapping_impl( + SliceSpecifiers... slices) const { // compute sub extents using src_ext_t = Extents; @@ -104,51 +212,137 @@ layout_left::mapping::submdspan_mapping_impl(SliceSpecifiers... slices) using dst_ext_t = decltype(dst_ext); // figure out sub layout type - constexpr bool preserve_layout = detail::preserve_layout_left_mapping< - decltype(std::make_index_sequence()), dst_ext_t::rank(), - SliceSpecifiers...>::value; - using dst_layout_t = - std::conditional_t; - using dst_mapping_t = typename dst_layout_t::template mapping; + using deduce_layout = detail::deduce_layout_left_submapping< + typename dst_ext_t::index_type, dst_ext_t::rank(), + std::make_index_sequence, + SliceSpecifiers...>; - if constexpr (std::is_same_v) { + // Figure out if any slice's lower bound equals the corresponding extent. + // If so, bypass evaluating the layout mapping. This fixes LWG Issue 4060. + const bool out_of_bounds = + detail::any_slice_out_of_bounds(this->extents(), slices...); + auto offset = static_cast( + out_of_bounds ? this->required_span_size() + : this->operator()(detail::first_of(slices)...)); + + if constexpr (deduce_layout::layout_left_value()) { // layout_left case + using dst_mapping_t = typename layout_left::template mapping; + return submdspan_mapping_result{dst_mapping_t(dst_ext), + offset}; + } else if constexpr (deduce_layout::layout_left_padded_value()) { + constexpr size_t S_static = MDSPAN_IMPL_STANDARD_NAMESPACE::detail::compute_s_static_layout_left::value(std::make_index_sequence()); + using dst_mapping_t = typename MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_left_padded::template mapping; return submdspan_mapping_result{ - dst_mapping_t(dst_ext), - static_cast(this->operator()(detail::first_of(slices)...))}; + dst_mapping_t(dst_ext, stride(1 + deduce_layout::gap_len)), offset}; } else { // layout_stride case - auto inv_map = detail::inv_map_rank( - std::integral_constant(), - std::index_sequence<>(), - slices...); - return submdspan_mapping_result{ - dst_mapping_t(dst_ext, detail::construct_sub_strides( - *this, inv_map, - // HIP needs deduction guides to have markups so we need to be explicit - // NVCC 11.0 has a bug with deduction guide here, tested that 11.2 does not have the issue - #if defined(_MDSPAN_HAS_HIP) || (defined(__NVCC__) && (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__ * 10) < 1120) - std::tuple{detail::stride_of(slices)...})), - #else - std::tuple{detail::stride_of(slices)...})), - #endif - static_cast(this->operator()(detail::first_of(slices)...))}; + using dst_mapping_t = typename layout_stride::mapping; + auto inv_map = detail::inv_map_rank(std::integral_constant(), + std::index_sequence<>(), slices...); + return submdspan_mapping_result { + dst_mapping_t(dst_ext, + detail::construct_sub_strides( + *this, inv_map, +// HIP needs deduction guides to have markups so we need to be explicit +// NVCC 11.0 has a bug with deduction guide here, tested that 11.2 does not have +// the issue But Clang-CUDA also doesn't accept the use of deduction guide so +// disable it for CUDA altogether +#if defined(_MDSPAN_HAS_HIP) || defined(_MDSPAN_HAS_CUDA) + std::tuple{ + detail::stride_of(slices)...})), +#else + std::tuple{detail::stride_of(slices)...})), +#endif + offset + }; } #if defined(__NVCC__) && !defined(__CUDA_ARCH__) && defined(__GNUC__) __builtin_unreachable(); #endif } -#if defined __NVCC__ - #ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ - #pragma nv_diagnostic pop - #else - #ifdef __CUDA_ARCH__ - #pragma diagnostic pop - #endif - #endif -#elif defined __NVCOMPILER - #pragma diagnostic pop + +template +template +template +MDSPAN_INLINE_FUNCTION constexpr auto +MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_left_padded::mapping::submdspan_mapping_impl( + SliceSpecifiers... slices) const { + + // compute sub extents + using src_ext_t = Extents; + auto dst_ext = submdspan_extents(extents(), slices...); + using dst_ext_t = decltype(dst_ext); + + if constexpr (Extents::rank() == 0) { // rank-0 case + using dst_mapping_t = typename MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_left_padded::template mapping; + return submdspan_mapping_result{*this, 0}; + } else { + const bool out_of_bounds = + MDSPAN_IMPL_STANDARD_NAMESPACE::detail::any_slice_out_of_bounds(this->extents(), slices...); + auto offset = static_cast( + out_of_bounds ? this->required_span_size() + : this->operator()(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::first_of(slices)...)); + if constexpr (dst_ext_t::rank() == 0) { // result rank-0 + // The following for some reasons leads to compiler error later, while not using a typedef works: + // Compilers: CUDA 11.2 with GCC 9.1 + // + // using dst_mapping_t = typename layout_left::template mapping; + // return submdspan_mapping_result{dst_mapping_t{dst_ext}, offset}; + // + // Error: submdspan_mapping.hpp:299:23: error: 'dst_mapping_t' does not name a type + // 299 | using dst_mapping_t = typename layout_left::template mapping; + // The same error is given (about dst_mapping_t not naming type) when a different name is used in 299: + // using dst_mapping_t2 = typename layout_left::template mapping; + + return submdspan_mapping_result> + {typename layout_left::template mapping{dst_ext}, offset}; + } else { // general case + // Figure out if any slice's lower bound equals the corresponding extent. + // If so, bypass evaluating the layout mapping. This fixes LWG Issue 4060. + // figure out sub layout type + using deduce_layout = MDSPAN_IMPL_STANDARD_NAMESPACE::detail::deduce_layout_left_submapping< + typename dst_ext_t::index_type, dst_ext_t::rank(), + decltype(std::make_index_sequence()), + SliceSpecifiers...>; + + if constexpr (deduce_layout::layout_left_value() && dst_ext_t::rank() == 1) { // getting rank-1 from leftmost + using dst_mapping_t = typename layout_left::template mapping; + return submdspan_mapping_result{dst_mapping_t{dst_ext}, offset}; + } else if constexpr (deduce_layout::layout_left_padded_value()) { // can keep layout_left_padded + constexpr size_t S_static = MDSPAN_IMPL_STANDARD_NAMESPACE::detail::compute_s_static_layout_left::value(std::make_index_sequence()); + using dst_mapping_t = typename MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_left_padded::template mapping; + return submdspan_mapping_result{ + dst_mapping_t(dst_ext, stride(1 + deduce_layout::gap_len)), offset}; + } else { // layout_stride + auto inv_map = MDSPAN_IMPL_STANDARD_NAMESPACE::detail::inv_map_rank(std::integral_constant(), + std::index_sequence<>(), slices...); + using dst_mapping_t = typename layout_stride::template mapping; + return submdspan_mapping_result { + dst_mapping_t(dst_ext, + MDSPAN_IMPL_STANDARD_NAMESPACE::detail::construct_sub_strides( + *this, inv_map, +// HIP needs deduction guides to have markups so we need to be explicit +// NVCC 11.0 has a bug with deduction guide here, tested that 11.2 does not have +// the issue But Clang-CUDA also doesn't accept the use of deduction guide so +// disable it for CUDA alltogether +#if defined(_MDSPAN_HAS_HIP) || defined(_MDSPAN_HAS_CUDA) + std::tuple{ + MDSPAN_IMPL_STANDARD_NAMESPACE::detail::stride_of(slices)...})), +#else + std::tuple{MDSPAN_IMPL_STANDARD_NAMESPACE::detail::stride_of(slices)...})), #endif + offset + }; + } + } + } + + +#if defined(__NVCC__) && !defined(__CUDA_ARCH__) && defined(__GNUC__) + __builtin_unreachable(); +#endif +} //********************************** // layout_right submdspan_mapping @@ -156,134 +350,276 @@ layout_left::mapping::submdspan_mapping_impl(SliceSpecifiers... slices) namespace detail { // Figure out whether to preserve layout_right -template -struct preserve_layout_right_mapping; +template +struct deduce_layout_right_submapping; -template -struct preserve_layout_right_mapping, SubRank, - SliceSpecifiers...> { - constexpr static size_t SrcRank = sizeof...(SliceSpecifiers); - constexpr static bool value = - // Preserve layout for rank 0 - (SubRank == 0) || - ( - // The last subrank slice specifiers need to be full_extent_t - except - // for the srcrank-subrank one which could also be tuple but not a - // strided index range slice specifiers before srcrank-subrank are - // integrals - ((Idx < - SrcRank - SubRank) || // these are only integral slice specifiers - (std::is_same_v) || - ((Idx == SrcRank - SubRank) && - std::is_convertible_v>)) && - ...); +template +struct deduce_layout_right_submapping< + IndexType, SubRank, std::index_sequence, SliceSpecifiers...> { + + static constexpr size_t Rank = sizeof...(Idx); + using count_range = index_sequence_scan_impl< + 0u, (std::is_convertible_v ? 0u : 1u)...>; + //__static_partial_sums...>; + constexpr static int gap_len = + (((Idx < Rank - 1 && count_range::get(Idx) == SubRank - 1 && + std::is_convertible_v) + ? 1 + : 0) + + ... + 0); + + MDSPAN_INLINE_FUNCTION + constexpr static bool layout_right_value() { + // Use layout_right for rank 0 + if constexpr (SubRank == 0) { + return true; + // Use layout_right for rank 1 result if rightmost slice specifier is range like + } else if constexpr (SubRank == 1) { + return ((Idx < Rank - 1 || is_range_slice_v)&&...); + } else { + // Preserve if rightmost SubRank-1 slices are full_extent_t and + // the slice at idx Rank-Subrank is a range and + // for idx < Rank - SubRank the slice is an index + return ((((Idx >= Rank - SubRank) && std::is_same_v) || + ((Idx == Rank - SubRank) && is_range_slice_v) || + ((Idx < Rank - SubRank) && is_index_slice_v)) && ...); + } +#if defined(__NVCC__) && !defined(__CUDA_ARCH__) && defined(__GNUC__) + __builtin_unreachable(); +#endif + } + + MDSPAN_INLINE_FUNCTION + constexpr static bool layout_right_padded_value() { + // Technically could also keep layout_right_padded for SubRank==0 + // and SubRank==1 with rightmost slice specifier being a contiguous range + // but we intercept these cases separately + + // In all other cases: + // rightmost slice must be range + // then there can be a gap with index slices + // then SubRank - 2 full_extent slices + // then another range slice + // then more index slices + // e.g. I I R F F F I I I R for obtaining a rank-5 from a rank-10 + return ((((Idx == Rank - 1) && is_range_slice_v) || + ((Idx >= Rank - gap_len - 1 && Idx < Rank - 1) && is_index_slice_v) || + ((Idx > Rank - gap_len - SubRank && Idx < Rank - gap_len - 1) && std::is_same_v) || + ((Idx == Rank - gap_len - SubRank) && is_range_slice_v) || + ((Idx < Rank - gap_len - SubRank) && is_index_slice_v)) && ... ); + } }; + +// We are reusing the same thing for layout_right and layout_right_padded +// For layout_right as source StaticStride is static_extent(Rank-1) +template +struct compute_s_static_layout_right { + // Neither StaticStride nor any of the provided extents can be zero. + // StaticStride can never be zero, the static_extents we are looking at are associated with + // integral slice specifiers - which wouldn't be valid for zero extent + template + MDSPAN_INLINE_FUNCTION + static constexpr size_t value(std::index_sequence) { + size_t val = ((Idx >= Extents::rank() - 1 - NumGaps && Idx < Extents::rank() - 1 ? (Extents::static_extent(Idx) == dynamic_extent?0:Extents::static_extent(Idx)) : 1) * ... * (StaticStride == dynamic_extent?0:StaticStride)); + return val == 0?dynamic_extent:val; + } +}; + } // namespace detail -// Suppress spurious warning with NVCC about no return statement. -// This is a known issue in NVCC and NVC++ -// Depending on the CUDA and GCC version we need both the builtin -// and the diagnostic push. I tried really hard to find something shorter -// but no luck ... -#if defined __NVCC__ - #ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ - #pragma nv_diagnostic push - #pragma nv_diag_suppress = implicit_return_from_non_void_function - #else - #ifdef __CUDA_ARCH__ - #pragma diagnostic push - #pragma diag_suppress implicit_return_from_non_void_function - #endif - #endif -#elif defined __NVCOMPILER - #pragma diagnostic push - #pragma diag_suppress = implicit_return_from_non_void_function -#endif +// Actual submdspan mapping call template template -MDSPAN_INLINE_FUNCTION -constexpr auto +MDSPAN_INLINE_FUNCTION constexpr auto layout_right::mapping::submdspan_mapping_impl( - SliceSpecifiers... slices) const { - // get sub extents + SliceSpecifiers... slices) const { + + // compute sub extents using src_ext_t = Extents; auto dst_ext = submdspan_extents(extents(), slices...); using dst_ext_t = decltype(dst_ext); - // determine new layout type - constexpr bool preserve_layout = detail::preserve_layout_right_mapping< - decltype(std::make_index_sequence()), dst_ext_t::rank(), - SliceSpecifiers...>::value; - using dst_layout_t = - std::conditional_t; - using dst_mapping_t = typename dst_layout_t::template mapping; + // figure out sub layout type + using deduce_layout = detail::deduce_layout_right_submapping< + typename dst_ext_t::index_type, dst_ext_t::rank(), + std::make_index_sequence, + SliceSpecifiers...>; - if constexpr (std::is_same_v) { + // Figure out if any slice's lower bound equals the corresponding extent. + // If so, bypass evaluating the layout mapping. This fixes LWG Issue 4060. + const bool out_of_bounds = + detail::any_slice_out_of_bounds(this->extents(), slices...); + auto offset = static_cast( + out_of_bounds ? this->required_span_size() + : this->operator()(detail::first_of(slices)...)); + + if constexpr (deduce_layout::layout_right_value()) { // layout_right case + using dst_mapping_t = typename layout_right::mapping; + return submdspan_mapping_result{dst_mapping_t(dst_ext), + offset}; + } else if constexpr (deduce_layout::layout_right_padded_value()) { + constexpr size_t S_static = MDSPAN_IMPL_STANDARD_NAMESPACE::detail::compute_s_static_layout_left::value(std::make_index_sequence()); + using dst_mapping_t = typename MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_right_padded::template mapping; return submdspan_mapping_result{ - dst_mapping_t(dst_ext), - static_cast(this->operator()(detail::first_of(slices)...))}; + dst_mapping_t(dst_ext, + stride(src_ext_t::rank() - 2 - deduce_layout::gap_len)), + offset}; } else { // layout_stride case - auto inv_map = detail::inv_map_rank( - std::integral_constant(), - std::index_sequence<>(), - slices...); - return submdspan_mapping_result{ - dst_mapping_t(dst_ext, detail::construct_sub_strides( - *this, inv_map, - // HIP needs deduction guides to have markups so we need to be explicit - // NVCC 11.0 has a bug with deduction guide here, tested that 11.2 does not have the issue - #if defined(_MDSPAN_HAS_HIP) || (defined(__NVCC__) && (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__ * 10) < 1120) - std::tuple{detail::stride_of(slices)...})), - #else - std::tuple{detail::stride_of(slices)...})), - #endif - static_cast(this->operator()(detail::first_of(slices)...))}; + using dst_mapping_t = typename layout_stride::mapping; + auto inv_map = detail::inv_map_rank(std::integral_constant(), + std::index_sequence<>(), slices...); + return submdspan_mapping_result { + dst_mapping_t(dst_ext, + detail::construct_sub_strides( + *this, inv_map, +// HIP needs deduction guides to have markups so we need to be explicit +// NVCC 11.0 has a bug with deduction guide here, tested that 11.2 does not have +// the issue But Clang-CUDA also doesn't accept the use of deduction guide so +// disable it for CUDA altogether +#if defined(_MDSPAN_HAS_HIP) || defined(_MDSPAN_HAS_CUDA) + std::tuple{ + detail::stride_of(slices)...})), +#else + std::tuple{detail::stride_of(slices)...})), +#endif + offset + }; } #if defined(__NVCC__) && !defined(__CUDA_ARCH__) && defined(__GNUC__) __builtin_unreachable(); #endif } -#if defined __NVCC__ - #ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ - #pragma nv_diagnostic pop - #else - #ifdef __CUDA_ARCH__ - #pragma diagnostic pop - #endif - #endif -#elif defined __NVCOMPILER - #pragma diagnostic pop + +template +template +template +MDSPAN_INLINE_FUNCTION constexpr auto +MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_right_padded::mapping::submdspan_mapping_impl( + SliceSpecifiers... slices) const { + + // compute sub extents + using src_ext_t = Extents; + auto dst_ext = submdspan_extents(extents(), slices...); + using dst_ext_t = decltype(dst_ext); + + if constexpr (Extents::rank() == 0) { // rank-0 case + using dst_mapping_t = typename MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_right_padded::template mapping; + return submdspan_mapping_result{*this, 0}; + } else { + // Figure out if any slice's lower bound equals the corresponding extent. + // If so, bypass evaluating the layout mapping. This fixes LWG Issue 4060. + // figure out sub layout type + const bool out_of_bounds = + MDSPAN_IMPL_STANDARD_NAMESPACE::detail::any_slice_out_of_bounds(this->extents(), slices...); + auto offset = static_cast( + out_of_bounds ? this->required_span_size() + : this->operator()(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::first_of(slices)...)); + if constexpr (dst_ext_t::rank() == 0) { // result rank-0 + // Same issue as in layout_left_padded: see comment there + // using dst_mapping_t = typename layout_right::template mapping; + // return submdspan_mapping_result{dst_mapping_t{dst_ext}, offset}; + return submdspan_mapping_result> + {typename layout_right::template mapping{dst_ext}, offset}; + } else { // general case + using deduce_layout = MDSPAN_IMPL_STANDARD_NAMESPACE::detail::deduce_layout_right_submapping< + typename dst_ext_t::index_type, dst_ext_t::rank(), + decltype(std::make_index_sequence()), + SliceSpecifiers...>; + + if constexpr (deduce_layout::layout_right_value() && dst_ext_t::rank() == 1) { // getting rank-1 from rightmost + using dst_mapping_t = typename layout_right::template mapping; + return submdspan_mapping_result{dst_mapping_t{dst_ext}, offset}; + } else if constexpr (deduce_layout::layout_right_padded_value()) { // can keep layout_right_padded + constexpr size_t S_static = MDSPAN_IMPL_STANDARD_NAMESPACE::detail::compute_s_static_layout_right::value(std::make_index_sequence()); + using dst_mapping_t = typename MDSPAN_IMPL_PROPOSED_NAMESPACE::layout_right_padded::template mapping; + return submdspan_mapping_result{ + dst_mapping_t(dst_ext, stride(Extents::rank() - 2 - deduce_layout::gap_len)), offset}; + } else { // layout_stride + auto inv_map = MDSPAN_IMPL_STANDARD_NAMESPACE::detail::inv_map_rank(std::integral_constant(), + std::index_sequence<>(), slices...); + using dst_mapping_t = typename layout_stride::template mapping; + return submdspan_mapping_result { + dst_mapping_t(dst_ext, + MDSPAN_IMPL_STANDARD_NAMESPACE::detail::construct_sub_strides( + *this, inv_map, +// HIP needs deduction guides to have markups so we need to be explicit +// NVCC 11.0 has a bug with deduction guide here, tested that 11.2 does not have +// the issue But Clang-CUDA also doesn't accept the use of deduction guide so +// disable it for CUDA alltogether +#if defined(_MDSPAN_HAS_HIP) || defined(_MDSPAN_HAS_CUDA) + std::tuple{ + MDSPAN_IMPL_STANDARD_NAMESPACE::detail::stride_of(slices)...})), +#else + std::tuple{MDSPAN_IMPL_STANDARD_NAMESPACE::detail::stride_of(slices)...})), #endif + offset + }; + } + } + } + + +#if defined(__NVCC__) && !defined(__CUDA_ARCH__) && defined(__GNUC__) + __builtin_unreachable(); +#endif +} //********************************** // layout_stride submdspan_mapping //********************************* template template -MDSPAN_INLINE_FUNCTION -constexpr auto +MDSPAN_INLINE_FUNCTION constexpr auto layout_stride::mapping::submdspan_mapping_impl( - SliceSpecifiers... slices) const { + SliceSpecifiers... slices) const { auto dst_ext = submdspan_extents(extents(), slices...); using dst_ext_t = decltype(dst_ext); - auto inv_map = detail::inv_map_rank( - std::integral_constant(), - std::index_sequence<>(), - slices...); + auto inv_map = detail::inv_map_rank(std::integral_constant(), + std::index_sequence<>(), slices...); using dst_mapping_t = typename layout_stride::template mapping; - return submdspan_mapping_result{ - dst_mapping_t(dst_ext, detail::construct_sub_strides( - *this, inv_map, - // HIP needs deduction guides to have markups so we need to be explicit - // NVCC 11.0 has a bug with deduction guide here, tested that 11.2 does not have the issue - #if defined(_MDSPAN_HAS_HIP) || (defined(__NVCC__) && (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__ * 10) < 1120) - std::tuple(detail::stride_of(slices)...))), + + // Figure out if any slice's lower bound equals the corresponding extent. + // If so, bypass evaluating the layout mapping. This fixes LWG Issue 4060. + const bool out_of_bounds = + detail::any_slice_out_of_bounds(this->extents(), slices...); + auto offset = static_cast( + out_of_bounds ? this->required_span_size() + : this->operator()(detail::first_of(slices)...)); + + return submdspan_mapping_result { + dst_mapping_t(dst_ext, + detail::construct_sub_strides( + *this, inv_map, +// HIP needs deduction guides to have markups so we need to be explicit +// NVCC 11.0 has a bug with deduction guide here, tested that 11.2 does not have +// the issue +#if defined(_MDSPAN_HAS_HIP) || \ + (defined(__NVCC__) && \ + (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__ * 10) < 1120) + std::tuple( + detail::stride_of(slices)...))), #else - std::tuple(detail::stride_of(slices)...))), + std::tuple(detail::stride_of(slices)...))), #endif - static_cast(this->operator()(detail::first_of(slices)...))}; + offset + }; } } // namespace MDSPAN_IMPL_STANDARD_NAMESPACE + +#if defined __NVCC__ +#ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ +#pragma nv_diagnostic pop +#else +#ifdef __CUDA_ARCH__ +#pragma diagnostic pop +#endif +#endif +#elif defined __NVCOMPILER +#pragma diagnostic pop +#endif diff --git a/lib/kokkos/tpls/mdspan/include/experimental/__p2642_bits/layout_padded.hpp b/lib/kokkos/tpls/mdspan/include/experimental/__p2642_bits/layout_padded.hpp index a801486792..e5f7bee4ca 100644 --- a/lib/kokkos/tpls/mdspan/include/experimental/__p2642_bits/layout_padded.hpp +++ b/lib/kokkos/tpls/mdspan/include/experimental/__p2642_bits/layout_padded.hpp @@ -59,6 +59,10 @@ MDSPAN_INLINE_FUNCTION constexpr size_t get_actual_static_padding_value() { } else { return dynamic_extent; } + // Missing return statement warning from NVCC and ICC +#if defined(__NVCC__) || defined(__INTEL_COMPILER) + return 0; +#endif } template @@ -69,7 +73,7 @@ struct static_array_type_for_padded_extent using extents_type = _Extents; using type = ::MDSPAN_IMPL_STANDARD_NAMESPACE::detail::maybe_static_array< index_type, size_t, dynamic_extent, - detail::get_actual_static_padding_value()>; }; @@ -101,6 +105,10 @@ struct padded_extent { } else { return init_padding(exts, padding_value); } + // Missing return statement warning from NVCC and ICC +#if defined(__NVCC__) || defined(__INTEL_COMPILER) + return {}; +#endif } MDSPAN_INLINE_FUNCTION static constexpr static_array_type @@ -112,6 +120,10 @@ struct padded_extent { } else { return {}; } + // Missing return statement warning from NVCC and ICC +#if defined(__NVCC__) || defined(__INTEL_COMPILER) + return {}; +#endif } template @@ -123,6 +135,10 @@ struct padded_extent { } else { return {}; } + // Missing return statement warning from NVCC and ICC +#if defined(__NVCC__) || defined(__INTEL_COMPILER) + return {}; +#endif } }; } // namespace detail @@ -158,19 +174,21 @@ private: typename padded_stride_type::static_array_type padded_stride = {}; extents_type exts = {}; - constexpr index_type compute_offset(std::index_sequence<>) const { + MDSPAN_INLINE_FUNCTION constexpr index_type + compute_offset(std::index_sequence<>) const { return 0; } template - constexpr index_type compute_offset(std::index_sequence, - IndexOffset index_offset) const { + MDSPAN_INLINE_FUNCTION constexpr index_type + compute_offset(std::index_sequence, IndexOffset index_offset) const { return index_offset; } template - constexpr index_type compute_offset(std::index_sequence, - IndexOffsets... index_offsets) const { + MDSPAN_INLINE_FUNCTION constexpr index_type + compute_offset(std::index_sequence, + IndexOffsets... index_offsets) const { index_type indices[] = {static_cast(index_offsets)...}; // self-recursive fold trick from // https://github.com/llvm/llvm-project/blob/96e1914aa2e6d8966acbfbe2f4d184201f1aa318/libcxx/include/mdspan/layout_left.h#L144 @@ -203,7 +221,7 @@ public: #endif MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping(const mapping&) noexcept = default; - MDSPAN_INLINE_FUNCTION_DEFAULTED mapping& operator=(const mapping&) noexcept = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping& operator=(const mapping&) noexcept = default; /** * Initializes the mapping with the given extents. @@ -241,62 +259,71 @@ public: /** * Converting constructor from `layout_left::mapping`. * - * This overload participates in overload resolution only if `is_constructible_v` is true. - * If `OtherExtents::rank() > 1` then one of `padding_value`, `static_extent(0)`, or `OtherExtents::static_extent(0)` must be `dynamic_extent`; - * otherwise, `OtherExtents::static_extent(0)` must be equal to the least multiple of `padding_value` greater than or equal to `extents_type::static_extent(0)` + * This overload participates in overload resolution only if + * `is_constructible_v` is true. If + * `OtherExtents::rank() > 1` then one of `padding_value`, `static_extent(0)`, + * or `OtherExtents::static_extent(0)` must be `dynamic_extent`; otherwise, + * `OtherExtents::static_extent(0)` must be equal to the least multiple of + * `padding_value` greater than or equal to `extents_type::static_extent(0)` */ MDSPAN_TEMPLATE_REQUIRES( - class _OtherExtents, - /* requires */ ( - std::is_constructible_v - ) - ) - MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible_v<_OtherExtents, extents_type>)) + class _OtherExtents, + /* requires */ (std::is_constructible_v)) + MDSPAN_CONDITIONAL_EXPLICIT( + (!std::is_convertible_v<_OtherExtents, extents_type>)) + MDSPAN_INLINE_FUNCTION constexpr mapping(const layout_left::mapping<_OtherExtents> &other_mapping) - : padded_stride(padded_stride_type::init_padding(other_mapping, std::integral_constant{})), - exts(other_mapping.extents()) - { - static_assert((_OtherExtents::rank() > 1) || (static_padding_stride != dynamic_extent) || (_OtherExtents::static_extent(extent_to_pad_idx) != dynamic_extent) - || (static_padding_stride == _OtherExtents::static_extent(extent_to_pad_idx))); + : padded_stride(padded_stride_type::init_padding( + other_mapping, + std::integral_constant{})), + exts(other_mapping.extents()) { + static_assert( + (_OtherExtents::rank() > 1) || + (static_padding_stride != dynamic_extent) || + (_OtherExtents::static_extent(extent_to_pad_idx) != dynamic_extent) || + (static_padding_stride == + _OtherExtents::static_extent(extent_to_pad_idx))); } /** * Converting constructor from `layout_stride::mapping`. * - * This overload participates in overload resolution only if `is_constructible_v` is true + * This overload participates in overload resolution only if + * `is_constructible_v` is true */ MDSPAN_TEMPLATE_REQUIRES( - class _OtherExtents, - /* requires */ ( - std::is_constructible_v - ) - ) + class _OtherExtents, + /* requires */ (std::is_constructible_v)) MDSPAN_CONDITIONAL_EXPLICIT((extents_type::rank() > 0)) + MDSPAN_INLINE_FUNCTION constexpr mapping(const layout_stride::mapping<_OtherExtents> &other_mapping) - : padded_stride(padded_stride_type::init_padding(other_mapping, std::integral_constant{})), - exts(other_mapping.extents()) - { - } + : padded_stride(padded_stride_type::init_padding( + other_mapping, + std::integral_constant{})), + exts(other_mapping.extents()) {} /** * Converting constructor from `layout_left_padded::mapping`. * - * This overload participates in overload resolution only if `is_constructible_v` is true. - * Either `padding_value` or `OtherPaddingStride` must be `std::dynamic_extent`, or `padding_value == OtherPaddingStride`. + * This overload participates in overload resolution only if + * `is_constructible_v` is true. Either + * `padding_value` or `OtherPaddingStride` must be `std::dynamic_extent`, or + * `padding_value == OtherPaddingStride`. */ MDSPAN_TEMPLATE_REQUIRES( - class _Mapping, - /* requires */ ( - detail::is_layout_left_padded_mapping<_Mapping>::value - && std::is_constructible_v - ) - ) - MDSPAN_CONDITIONAL_EXPLICIT((extents_type::rank() > 1 && (padding_value == dynamic_extent || _Mapping::padding_value == dynamic_extent))) - constexpr - mapping(const _Mapping &other_mapping) - : padded_stride(padded_stride_type::init_padding(other_mapping, std::integral_constant{})), - exts(other_mapping.extents()) - { + class _Mapping, + /* requires */ (detail::is_layout_left_padded_mapping<_Mapping>::value + &&std::is_constructible_v< + extents_type, typename _Mapping::extents_type>)) + MDSPAN_CONDITIONAL_EXPLICIT((extents_type::rank() > 1 && + (padding_value == dynamic_extent || + _Mapping::padding_value == dynamic_extent))) + MDSPAN_INLINE_FUNCTION + constexpr mapping(const _Mapping &other_mapping) + : padded_stride(padded_stride_type::init_padding( + other_mapping, + std::integral_constant{})), + exts(other_mapping.extents()) { static_assert(padding_value == dynamic_extent || _Mapping::padding_value == dynamic_extent || padding_value == _Mapping::padding_value); @@ -305,42 +332,43 @@ public: /** * Converting constructor from `layout_right_padded::mapping`. * - * This overload participates in overload resolution only if `extents_type::rank()` is 0 or 1 and `is_constructible_v` is `true`. + * This overload participates in overload resolution only if + * `extents_type::rank()` is 0 or 1 and `is_constructible_v` is `true`. */ MDSPAN_TEMPLATE_REQUIRES( - class _Mapping, - /* requires */ ( - detail::is_layout_right_padded_mapping<_Mapping>::value - && extents_type::rank() <= 1 - && std::is_constructible_v - ) - ) - MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible_v)) - constexpr - mapping(const _Mapping &other_mapping) noexcept - : padded_stride(padded_stride_type::init_padding(other_mapping.extents(), other_mapping.extents().extent(extent_to_pad_idx))), - exts(other_mapping.extents()) - {} + class _Mapping, + /* requires */ (detail::is_layout_right_padded_mapping<_Mapping>::value + &&extents_type::rank() <= 1 && + std::is_constructible_v)) + MDSPAN_CONDITIONAL_EXPLICIT( + (!std::is_convertible_v)) + MDSPAN_INLINE_FUNCTION + constexpr mapping(const _Mapping &other_mapping) noexcept + : padded_stride(padded_stride_type::init_padding( + other_mapping.extents(), + other_mapping.extents().extent(extent_to_pad_idx))), + exts(other_mapping.extents()) {} - constexpr const extents_type &extents() const noexcept - { + MDSPAN_INLINE_FUNCTION constexpr const extents_type & + extents() const noexcept { return exts; } - constexpr std::array - strides() const noexcept - { - if constexpr ( extents_type::rank() == 0 ) { + MDSPAN_INLINE_FUNCTION constexpr std::array + strides() const noexcept { + if constexpr (extents_type::rank() == 0) { return {}; - } else if constexpr ( extents_type::rank() == 1 ) { + } else if constexpr (extents_type::rank() == 1) { return {1}; } else { index_type value = 1; std::array s{}; s[extent_to_pad_idx] = value; value *= padded_stride.value(0); - for (rank_type r = extent_to_pad_idx + 1; r < extents_type::rank() - 1; ++r) - { + for (rank_type r = extent_to_pad_idx + 1; r < extents_type::rank() - 1; + ++r) { s[r] = value; value *= exts.extent(r); } @@ -349,12 +377,11 @@ public: } } - constexpr index_type - required_span_size() const noexcept - { - if constexpr ( extents_type::rank() == 0 ) { + MDSPAN_INLINE_FUNCTION constexpr index_type + required_span_size() const noexcept { + if constexpr (extents_type::rank() == 0) { return 1; - } else if constexpr ( extents_type::rank() == 1 ) { + } else if constexpr (extents_type::rank() == 1) { return exts.extent(0); } else { index_type value = padded_stride.value(0); @@ -375,40 +402,51 @@ public: */ MDSPAN_TEMPLATE_REQUIRES( class... _Indices, - /* requires */ ( - sizeof...(_Indices) == extents_type::rank() && - (::MDSPAN_IMPL_STANDARD_NAMESPACE::detail::are_valid_indices()) - ) - ) - constexpr size_t operator()(_Indices... idxs) const noexcept - { + /* requires */ (sizeof...(_Indices) == extents_type::rank() && + (::MDSPAN_IMPL_STANDARD_NAMESPACE::detail:: + are_valid_indices()))) + MDSPAN_INLINE_FUNCTION constexpr size_t + operator()(_Indices... idxs) const noexcept { +#if !defined(NDEBUG) + ::MDSPAN_IMPL_STANDARD_NAMESPACE::detail::check_all_indices(this->extents(), + idxs...); +#endif // ! NDEBUG return compute_offset(std::index_sequence_for<_Indices...>{}, idxs...); } - static constexpr bool is_always_unique() noexcept { return true; } - static constexpr bool is_always_exhaustive() noexcept - { - return (extents_type::rank() <= rank_type(1)) - || (extents_type::static_extent(extent_to_pad_idx) != dynamic_extent - && extents_type::static_extent(extent_to_pad_idx) == padded_stride_type::static_value()); + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_unique() noexcept { + return true; } - static constexpr bool is_always_strided() noexcept { return true; } - - static constexpr bool is_unique() noexcept { return true; } - constexpr bool is_exhaustive() const noexcept - { - return (extents_type::rank() < 2) - || (exts.extent(extent_to_pad_idx) == padded_stride.value(0)); + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_exhaustive() noexcept { + return (extents_type::rank() <= rank_type(1)) || + (extents_type::static_extent(extent_to_pad_idx) != dynamic_extent && + extents_type::static_extent(extent_to_pad_idx) == + padded_stride_type::static_value()); + } + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_strided() noexcept { + return true; } - static constexpr bool is_strided() noexcept { return true; } - constexpr index_type stride(rank_type r) const noexcept - { + MDSPAN_INLINE_FUNCTION static constexpr bool is_unique() noexcept { + return true; + } + MDSPAN_INLINE_FUNCTION constexpr bool is_exhaustive() const noexcept { + return (extents_type::rank() < 2) || + (exts.extent(extent_to_pad_idx) == padded_stride.value(0)); + } + MDSPAN_INLINE_FUNCTION static constexpr bool is_strided() noexcept { + return true; + } + + MDSPAN_INLINE_FUNCTION + constexpr index_type stride(rank_type r) const noexcept { assert(r < extents_type::rank()); - if(r == 0) return index_type(1); + if (r == 0) + return index_type(1); index_type value = padded_stride.value(0); - for (rank_type k = 1; k < r; k++) value *= exts.extent(k); + for (rank_type k = 1; k < r; k++) + value *= exts.extent(k); return value; } @@ -416,26 +454,26 @@ public: /** * Equality operator between `layout_left_padded`s * - * This overload only participates in overload resolution if `OtherExtents::rank() == extents_type::rank()`. + * This overload only participates in overload resolution if + * `OtherExtents::rank() == extents_type::rank()`. * - * \note There is currently a difference from p2642r2, where this function is specified as taking - * `layout_left_padded< padding_value >::mapping< Extents>`. However, this makes `padding_value` non-deducible. + * \note There is currently a difference from p2642r2, where this function is + * specified as taking `layout_left_padded< padding_value >::mapping< + * Extents>`. However, this makes `padding_value` non-deducible. */ MDSPAN_TEMPLATE_REQUIRES( - class _Mapping, - /* requires */ ( - detail::is_layout_left_padded_mapping<_Mapping>::value - && (_Mapping::extents_type::rank() == extents_type::rank()) - ) - ) - friend constexpr bool operator==(const mapping &left, const _Mapping &right) noexcept - { - // Workaround for some compilers not short-circuiting properly with compile-time checks - // i.e. we can't access stride(_padding_stride_idx) of a rank 0 mapping + class _Mapping, + /* requires */ (detail::is_layout_left_padded_mapping<_Mapping>::value && + (_Mapping::extents_type::rank() == extents_type::rank()))) + MDSPAN_INLINE_FUNCTION friend constexpr bool + operator==(const mapping &left, const _Mapping &right) noexcept { + // Workaround for some compilers not short-circuiting properly with + // compile-time checks i.e. we can't access stride(_padding_stride_idx) of a + // rank 0 mapping bool strides_equal = true; - if constexpr (extents_type::rank() > rank_type(1)) - { - strides_equal = left.stride(padded_stride_idx) == right.stride(padded_stride_idx); + if constexpr (extents_type::rank() > rank_type(1)) { + strides_equal = + left.stride(padded_stride_idx) == right.stride(padded_stride_idx); } return (left.extents() == right.extents()) && strides_equal; } @@ -444,20 +482,31 @@ public: /** * Inequality operator between `layout_left_padded`s * - * This overload only participates in overload resolution if `OtherExtents::rank() == extents_type::rank()`. + * This overload only participates in overload resolution if + * `OtherExtents::rank() == extents_type::rank()`. */ MDSPAN_TEMPLATE_REQUIRES( - class _Mapping, - /* requires */ ( - detail::is_layout_left_padded_mapping<_Mapping>::value - && (_Mapping::extents_type::rank() == extents_type::rank()) - ) - ) - friend constexpr bool operator!=(const mapping &left, const _Mapping &right) noexcept - { + class _Mapping, + /* requires */ (detail::is_layout_left_padded_mapping<_Mapping>::value && + (_Mapping::extents_type::rank() == extents_type::rank()))) + MDSPAN_INLINE_FUNCTION friend constexpr bool + operator!=(const mapping &left, const _Mapping &right) noexcept { return !(left == right); } #endif + + // [mdspan.submdspan.mapping], submdspan mapping specialization + template + MDSPAN_INLINE_FUNCTION + constexpr auto submdspan_mapping_impl( + SliceSpecifiers... slices) const; + + template + MDSPAN_INLINE_FUNCTION + friend constexpr auto submdspan_mapping( + const mapping& src, SliceSpecifiers... slices) { + return src.submdspan_mapping_impl(slices...); + } }; template @@ -490,25 +539,27 @@ public: typename padded_stride_type::static_array_type padded_stride = {}; extents_type exts = {}; - constexpr index_type compute_offset(std::index_sequence<>) const { + MDSPAN_INLINE_FUNCTION constexpr index_type + compute_offset(std::index_sequence<>) const { return 0; } template - constexpr index_type compute_offset(std::index_sequence, - IndexOffset index_offset) const { + MDSPAN_INLINE_FUNCTION constexpr index_type + compute_offset(std::index_sequence, IndexOffset index_offset) const { return index_offset; } template - constexpr index_type compute_offset(std::index_sequence, - IndexOffsets... index_offsets) const { + MDSPAN_INLINE_FUNCTION constexpr index_type + compute_offset(std::index_sequence, + IndexOffsets... index_offsets) const { // self-recursive fold trick from // https://github.com/llvm/llvm-project/blob/4d9771741d40cc9cfcccb6b033f43689d36b705a/libcxx/include/mdspan/layout_right.h#L141 index_type res = 0; ((res = static_cast(index_offsets) + (Ranks == extent_to_pad_idx ? padded_stride.value(0) - : exts.extent(Ranks)) * + : exts.extent(Ranks)) * res), ...); return res; @@ -533,7 +584,7 @@ public: #endif MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping(const mapping&) noexcept = default; - MDSPAN_INLINE_FUNCTION_DEFAULTED mapping& operator=(const mapping&) noexcept = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping& operator=(const mapping&) noexcept = default; /** * Initializes the mapping with the given extents. @@ -577,56 +628,62 @@ public: */ MDSPAN_TEMPLATE_REQUIRES( class _OtherExtents, - /* requires */ ( - std::is_constructible_v - ) - ) - MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible_v<_OtherExtents, extents_type>)) + /* requires */ (std::is_constructible_v)) + MDSPAN_CONDITIONAL_EXPLICIT( + (!std::is_convertible_v<_OtherExtents, extents_type>)) + MDSPAN_INLINE_FUNCTION constexpr mapping(const layout_right::mapping<_OtherExtents> &other_mapping) - : padded_stride(padded_stride_type::init_padding(other_mapping, std::integral_constant{})), - exts(other_mapping.extents()) - { - static_assert((_OtherExtents::rank() > 1) || (padded_stride_type::static_value() != dynamic_extent) || (_OtherExtents::static_extent(extent_to_pad_idx) != dynamic_extent) - || (padded_stride_type::static_value() == _OtherExtents::static_extent(extent_to_pad_idx))); + : padded_stride(padded_stride_type::init_padding( + other_mapping, + std::integral_constant{})), + exts(other_mapping.extents()) { + static_assert( + (_OtherExtents::rank() > 1) || + (padded_stride_type::static_value() != dynamic_extent) || + (_OtherExtents::static_extent(extent_to_pad_idx) != dynamic_extent) || + (padded_stride_type::static_value() == + _OtherExtents::static_extent(extent_to_pad_idx))); } /** * Converting constructor from `layout_stride::mapping`. * - * This overload participates in overload resolution only if `is_constructible_v` is true + * This overload participates in overload resolution only if + * `is_constructible_v` is true */ MDSPAN_TEMPLATE_REQUIRES( class _OtherExtents, - /* requires */ ( - std::is_constructible_v - ) - ) + /* requires */ (std::is_constructible_v)) MDSPAN_CONDITIONAL_EXPLICIT((extents_type::rank() > 0)) + MDSPAN_INLINE_FUNCTION constexpr mapping(const layout_stride::mapping<_OtherExtents> &other_mapping) - : padded_stride(padded_stride_type::init_padding(other_mapping, std::integral_constant{})), - exts(other_mapping.extents()) - {} + : padded_stride(padded_stride_type::init_padding( + other_mapping, + std::integral_constant{})), + exts(other_mapping.extents()) {} /** * Converting constructor from `layout_right_padded::mapping`. * - * This overload participates in overload resolution only if `is_constructible_v` is true. - * Either `padding_value` or `OtherPaddingStride` must be `std::dynamic_extent`, or `padding_value == OtherPaddingStride`. + * This overload participates in overload resolution only if + * `is_constructible_v` is true. Either + * `padding_value` or `OtherPaddingStride` must be `std::dynamic_extent`, or + * `padding_value == OtherPaddingStride`. */ MDSPAN_TEMPLATE_REQUIRES( class _Mapping, - /* requires */ ( - detail::is_layout_right_padded_mapping<_Mapping>::value - && std::is_constructible_v - ) - ) + /* requires */ (detail::is_layout_right_padded_mapping<_Mapping>::value + &&std::is_constructible_v< + extents_type, typename _Mapping::extents_type>)) MDSPAN_CONDITIONAL_EXPLICIT((extents_type::rank() > 1 && (padding_value == dynamic_extent || _Mapping::padding_value == dynamic_extent))) + MDSPAN_INLINE_FUNCTION constexpr mapping(const _Mapping &other_mapping) - : padded_stride(padded_stride_type::init_padding(other_mapping, std::integral_constant{})), - exts(other_mapping.extents()) - { + : padded_stride(padded_stride_type::init_padding( + other_mapping, + std::integral_constant{})), + exts(other_mapping.extents()) { static_assert(padding_value == dynamic_extent || _Mapping::padding_value == dynamic_extent || padding_value == _Mapping::padding_value); @@ -635,41 +692,42 @@ public: /** * Converting constructor from `layout_left_padded::mapping`. * - * This overload participates in overload resolution only if `extents_type::rank()` is 0 or 1 and `is_constructible_v` is `true`. + * This overload participates in overload resolution only if + * `extents_type::rank()` is 0 or 1 and `is_constructible_v` is `true`. */ MDSPAN_TEMPLATE_REQUIRES( class _Mapping, - /* requires */ ( - detail::is_layout_left_padded_mapping<_Mapping>::value - && extents_type::rank() <= 1 - && std::is_constructible_v - ) - ) - MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible_v)) + /* requires */ (detail::is_layout_left_padded_mapping<_Mapping>::value + &&extents_type::rank() <= 1 && + std::is_constructible_v)) + MDSPAN_CONDITIONAL_EXPLICIT( + (!std::is_convertible_v)) + MDSPAN_INLINE_FUNCTION constexpr mapping(const _Mapping &other_mapping) noexcept - : padded_stride(padded_stride_type::init_padding(other_mapping.extents(), other_mapping.extents().extent(extent_to_pad_idx))), - exts(other_mapping.extents()) - {} + : padded_stride(padded_stride_type::init_padding( + other_mapping.extents(), + other_mapping.extents().extent(extent_to_pad_idx))), + exts(other_mapping.extents()) {} - constexpr const extents_type &extents() const noexcept - { + MDSPAN_INLINE_FUNCTION constexpr const extents_type & + extents() const noexcept { return exts; } - constexpr std::array - strides() const noexcept - { - if constexpr ( extents_type::rank() == 0 ) { + MDSPAN_INLINE_FUNCTION constexpr std::array + strides() const noexcept { + if constexpr (extents_type::rank() == 0) { return {}; - } else if constexpr ( extents_type::rank() == 1 ) { + } else if constexpr (extents_type::rank() == 1) { return {1}; } else { index_type value = 1; std::array s{}; s[extent_to_pad_idx] = value; value *= padded_stride.value(0); - for (rank_type r = extent_to_pad_idx - 1; r > 0; --r) - { + for (rank_type r = extent_to_pad_idx - 1; r > 0; --r) { s[r] = value; value *= exts.extent(r); } @@ -678,17 +736,15 @@ public: } } - constexpr index_type - required_span_size() const noexcept - { - if constexpr ( extents_type::rank() == 0 ) { + MDSPAN_INLINE_FUNCTION constexpr index_type + required_span_size() const noexcept { + if constexpr (extents_type::rank() == 0) { return 1; - } else if constexpr ( extents_type::rank() == 1 ) { + } else if constexpr (extents_type::rank() == 1) { return exts.extent(0); } else { index_type value = 1; - for (rank_type r = 0; r < extent_to_pad_idx; ++r) - { + for (rank_type r = 0; r < extent_to_pad_idx; ++r) { value *= exts.extent(r); } return value * padded_stride.value(0); @@ -705,40 +761,47 @@ public: */ MDSPAN_TEMPLATE_REQUIRES( class... _Indices, - /* requires */ ( - sizeof...(_Indices) == extents_type::rank() && - (::MDSPAN_IMPL_STANDARD_NAMESPACE::detail::are_valid_indices()) - ) - ) - constexpr size_t operator()(_Indices... idxs) const noexcept - { + /* requires */ (sizeof...(_Indices) == extents_type::rank() && + (::MDSPAN_IMPL_STANDARD_NAMESPACE::detail:: + are_valid_indices()))) + MDSPAN_INLINE_FUNCTION constexpr size_t + operator()(_Indices... idxs) const noexcept { return compute_offset(std::index_sequence_for<_Indices...>{}, idxs...); } - static constexpr bool is_always_unique() noexcept { return true; } - static constexpr bool is_always_exhaustive() noexcept - { - return (extents_type::rank() <= rank_type(1)) - || (extents_type::static_extent(extent_to_pad_idx) != dynamic_extent - && extents_type::static_extent(extent_to_pad_idx) == padded_stride_type::static_value()); + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_unique() noexcept { + return true; } - static constexpr bool is_always_strided() noexcept { return true; } - - static constexpr bool is_unique() noexcept { return true; } - constexpr bool is_exhaustive() const noexcept - { - return (extents_type::rank() < 2) - || (exts.extent(extent_to_pad_idx) == padded_stride.value(0)); + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_exhaustive() noexcept { + return (extents_type::rank() <= rank_type(1)) || + (extents_type::static_extent(extent_to_pad_idx) != dynamic_extent && + extents_type::static_extent(extent_to_pad_idx) == + padded_stride_type::static_value()); + } + MDSPAN_INLINE_FUNCTION static constexpr bool is_always_strided() noexcept { + return true; } - static constexpr bool is_strided() noexcept { return true; } - constexpr index_type stride(rank_type r) const noexcept - { + MDSPAN_INLINE_FUNCTION static constexpr bool is_unique() noexcept { + return true; + } + MDSPAN_INLINE_FUNCTION constexpr bool is_exhaustive() const noexcept { + return (extents_type::rank() < 2) || + (exts.extent(extent_to_pad_idx) == padded_stride.value(0)); + } + MDSPAN_INLINE_FUNCTION static constexpr bool is_strided() noexcept { + return true; + } + + MDSPAN_INLINE_FUNCTION constexpr index_type + stride(rank_type r) const noexcept { assert(r < extents_type::rank()); - if(r == extents_type::rank() - 1) return index_type(1); + if (r == extents_type::rank() - 1) + return index_type(1); index_type value = padded_stride.value(0); - for (rank_type k = extents_type::rank() - 2; k > r; k--) value *= exts.extent(k); + for (rank_type k = extents_type::rank() - 2; k > r; k--) + value *= exts.extent(k); return value; } @@ -746,26 +809,26 @@ public: /** * Equality operator between `layout_right_padded`s * - * This overload only participates in overload resolution if `OtherExtents::rank() == extents_type::rank()`. + * This overload only participates in overload resolution if + * `OtherExtents::rank() == extents_type::rank()`. * - * \note There is currently a difference from p2642r2, where this function is specified as taking - * `layout_right_padded< padding_value >::mapping< Extents>`. However, this makes `padding_value` non-deducible. + * \note There is currently a difference from p2642r2, where this function is + * specified as taking `layout_right_padded< padding_value >::mapping< + * Extents>`. However, this makes `padding_value` non-deducible. */ MDSPAN_TEMPLATE_REQUIRES( class _Mapping, - /* requires */ ( - detail::is_layout_right_padded_mapping<_Mapping>::value - && (_Mapping::extents_type::rank() == extents_type::rank()) - ) - ) - friend constexpr bool operator==(const mapping &left, const _Mapping &right) noexcept - { - // Workaround for some compilers not short-circuiting properly with compile-time checks - // i.e. we can't access stride(_padding_stride_idx) of a rank 0 mapping + /* requires */ (detail::is_layout_right_padded_mapping<_Mapping>::value && + (_Mapping::extents_type::rank() == extents_type::rank()))) + MDSPAN_INLINE_FUNCTION friend constexpr bool + operator==(const mapping &left, const _Mapping &right) noexcept { + // Workaround for some compilers not short-circuiting properly with + // compile-time checks i.e. we can't access stride(_padding_stride_idx) of a + // rank 0 mapping bool strides_equal = true; - if constexpr (extents_type::rank() > rank_type(1)) - { - strides_equal = left.stride(padded_stride_idx) == right.stride(padded_stride_idx); + if constexpr (extents_type::rank() > rank_type(1)) { + strides_equal = + left.stride(padded_stride_idx) == right.stride(padded_stride_idx); } return (left.extents() == right.extents()) && strides_equal; } @@ -774,20 +837,31 @@ public: /** * Inequality operator between `layout_right_padded`s * - * This overload only participates in overload resolution if `OtherExtents::rank() == extents_type::rank()`. + * This overload only participates in overload resolution if + * `OtherExtents::rank() == extents_type::rank()`. */ MDSPAN_TEMPLATE_REQUIRES( class _Mapping, - /* requires */ ( - detail::is_layout_right_padded_mapping<_Mapping>::value - && (_Mapping::extents_type::rank() == extents_type::rank()) - ) - ) - friend constexpr bool operator!=(const mapping &left, const _Mapping &right) noexcept - { + /* requires */ (detail::is_layout_right_padded_mapping<_Mapping>::value && + (_Mapping::extents_type::rank() == extents_type::rank()))) + MDSPAN_INLINE_FUNCTION friend constexpr bool + operator!=(const mapping &left, const _Mapping &right) noexcept { return !(left == right); } #endif + + // [mdspan.submdspan.mapping], submdspan mapping specialization + template + MDSPAN_INLINE_FUNCTION + constexpr auto submdspan_mapping_impl( + SliceSpecifiers... slices) const; + + template + MDSPAN_INLINE_FUNCTION + friend constexpr auto submdspan_mapping( + const mapping& src, SliceSpecifiers... slices) { + return src.submdspan_mapping_impl(slices...); + } }; } } diff --git a/lib/kokkos/tpls/mdspan/include/experimental/__p2642_bits/layout_padded_fwd.hpp b/lib/kokkos/tpls/mdspan/include/experimental/__p2642_bits/layout_padded_fwd.hpp index 945f091a2d..18daa28cc6 100644 --- a/lib/kokkos/tpls/mdspan/include/experimental/__p2642_bits/layout_padded_fwd.hpp +++ b/lib/kokkos/tpls/mdspan/include/experimental/__p2642_bits/layout_padded_fwd.hpp @@ -17,6 +17,7 @@ #include #include "../__p0009_bits/dynamic_extent.hpp" +#include "../__p0009_bits/utility.hpp" namespace MDSPAN_IMPL_STANDARD_NAMESPACE { namespace MDSPAN_IMPL_PROPOSED_NAMESPACE { @@ -82,36 +83,49 @@ struct is_layout_right_padded_mapping<_Mapping, std::enable_if_t::template mapping>::value>> : std::true_type {}; + template -constexpr void check_padded_layout_converting_constructor_mandates() +constexpr void check_padded_layout_converting_constructor_mandates(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank<0>) {} + +template +constexpr void check_padded_layout_converting_constructor_mandates(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank<1>) {} + +template +constexpr void check_padded_layout_converting_constructor_mandates(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank) { - if constexpr (_LayoutExtentsType::rank() > 1) { - using extents_type = typename _PaddedLayoutMappingType::extents_type; - constexpr auto padding_value = _PaddedLayoutMappingType::padding_value; - constexpr auto idx = layout_padded_constants::extent_to_pad_idx; - if constexpr ((_LayoutExtentsType::static_extent(idx) != dynamic_extent) && - (extents_type::static_extent(idx) != dynamic_extent) && - (padding_value != dynamic_extent)) { - if constexpr (padding_value == 0) { - static_assert(_LayoutExtentsType::static_extent(idx) == 0); - } else { - static_assert( - _LayoutExtentsType::static_extent(idx) % padding_value == 0); - } - } - } + using extents_type = typename _PaddedLayoutMappingType::extents_type; + constexpr auto padding_value = _PaddedLayoutMappingType::padding_value; + constexpr auto idx = layout_padded_constants::extent_to_pad_idx; + + constexpr auto statically_determinable = + (_LayoutExtentsType::static_extent(idx) != dynamic_extent) && + (extents_type::static_extent(idx) != dynamic_extent) && + (padding_value != dynamic_extent); + + static_assert(!statically_determinable || + (padding_value == 0 + ? _LayoutExtentsType::static_extent(idx) == 0 + : _LayoutExtentsType::static_extent(idx) % padding_value == 0), + ""); } template -constexpr void check_padded_layout_converting_constructor_preconditions([[maybe_unused]] const _OtherMapping &other_mapping) { - if constexpr (_ExtentsType::rank() > 1) { - constexpr auto padded_stride_idx = - layout_padded_constants::padded_stride_idx; - constexpr auto extent_to_pad_idx = layout_padded_constants::extent_to_pad_idx; - assert(other_mapping.stride(padded_stride_idx) == other_mapping.extents().extent(extent_to_pad_idx)); - } -} +constexpr void check_padded_layout_converting_constructor_preconditions(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank<0>, + const _OtherMapping&) {} +template +constexpr void check_padded_layout_converting_constructor_preconditions(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank<1>, + const _OtherMapping&) {} +template +constexpr void check_padded_layout_converting_constructor_preconditions(MDSPAN_IMPL_STANDARD_NAMESPACE::detail::with_rank, + const _OtherMapping &other_mapping) { + constexpr auto padded_stride_idx = + layout_padded_constants::padded_stride_idx; + constexpr auto extent_to_pad_idx = layout_padded_constants::extent_to_pad_idx; + MDSPAN_IMPL_PRECONDITION(other_mapping.stride(padded_stride_idx) == other_mapping.extents().extent(extent_to_pad_idx)); +} + + } } } diff --git a/lib/kokkos/tpls/mdspan/include/mdspan/mdspan.hpp b/lib/kokkos/tpls/mdspan/include/mdspan/mdspan.hpp index ac72a1a4e6..4a0e354ffd 100644 --- a/lib/kokkos/tpls/mdspan/include/mdspan/mdspan.hpp +++ b/lib/kokkos/tpls/mdspan/include/mdspan/mdspan.hpp @@ -38,5 +38,6 @@ #include "../experimental/__p2642_bits/layout_padded.hpp" #include "../experimental/__p2630_bits/submdspan.hpp" #endif +#include "../experimental/__p2389_bits/dims.hpp" #endif // MDSPAN_HPP_ From cfcd7ddfbcc308e75b8608cf6669f2751ed06776 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Wed, 11 Sep 2024 09:31:54 -0600 Subject: [PATCH 275/355] Add KOKKOS_ENABLE_ATOMICS_BYPASS --- src/MAKE/OPTIONS/Makefile.kokkos_mpi_only | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MAKE/OPTIONS/Makefile.kokkos_mpi_only b/src/MAKE/OPTIONS/Makefile.kokkos_mpi_only index e1f7005617..5c39ac8f3e 100644 --- a/src/MAKE/OPTIONS/Makefile.kokkos_mpi_only +++ b/src/MAKE/OPTIONS/Makefile.kokkos_mpi_only @@ -7,7 +7,7 @@ SHELL = /bin/sh # specify flags and libraries needed for your compiler CC = mpicxx -CCFLAGS = -g -O3 -DNDEBUG +CCFLAGS = -g -O3 -DNDEBUG -DKOKKOS_ENABLE_ATOMICS_BYPASS SHFLAGS = -fPIC # uncomment when compiling with Intel 21.5 or older FMTFLAGS = # -std=c++11 From 64db592a847a28443f6ce2e2e57cb844d0a744b9 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Wed, 11 Sep 2024 09:45:21 -0600 Subject: [PATCH 276/355] Update Kokkos version in CMake --- cmake/Modules/Packages/KOKKOS.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/Modules/Packages/KOKKOS.cmake b/cmake/Modules/Packages/KOKKOS.cmake index bf0a18d324..cbda60fc53 100644 --- a/cmake/Modules/Packages/KOKKOS.cmake +++ b/cmake/Modules/Packages/KOKKOS.cmake @@ -45,8 +45,8 @@ if(DOWNLOAD_KOKKOS) list(APPEND KOKKOS_LIB_BUILD_ARGS "-DCMAKE_CXX_EXTENSIONS=${CMAKE_CXX_EXTENSIONS}") list(APPEND KOKKOS_LIB_BUILD_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}") include(ExternalProject) - set(KOKKOS_URL "https://github.com/kokkos/kokkos/archive/4.3.01.tar.gz" CACHE STRING "URL for KOKKOS tarball") - set(KOKKOS_MD5 "243de871b3dc2cf3990c1c404032df83" CACHE STRING "MD5 checksum of KOKKOS tarball") + set(KOKKOS_URL "https://github.com/kokkos/kokkos/archive/4.4.00.tar.gz" CACHE STRING "URL for KOKKOS tarball") + set(KOKKOS_MD5 "95af2e2d4b10a67a63cce09715fba127" CACHE STRING "MD5 checksum of KOKKOS tarball") mark_as_advanced(KOKKOS_URL) mark_as_advanced(KOKKOS_MD5) GetFallbackURL(KOKKOS_URL KOKKOS_FALLBACK) @@ -71,7 +71,7 @@ if(DOWNLOAD_KOKKOS) add_dependencies(LAMMPS::KOKKOSCORE kokkos_build) add_dependencies(LAMMPS::KOKKOSCONTAINERS kokkos_build) elseif(EXTERNAL_KOKKOS) - find_package(Kokkos 4.3.01 REQUIRED CONFIG) + find_package(Kokkos 4.4.00 REQUIRED CONFIG) target_link_libraries(lammps PRIVATE Kokkos::kokkos) else() set(LAMMPS_LIB_KOKKOS_SRC_DIR ${LAMMPS_LIB_SOURCE_DIR}/kokkos) From 0ebd4f5ddb5f2efa909237df9e3b508d3ada1bc3 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Wed, 11 Sep 2024 21:33:07 -0600 Subject: [PATCH 277/355] Make CMake Doc build docenv optional --- cmake/Modules/Documentation.cmake | 48 +++++++++++++++++++------------ cmake/Modules/FindSphinx.cmake | 29 +++++++++++++++++++ 2 files changed, 59 insertions(+), 18 deletions(-) create mode 100644 cmake/Modules/FindSphinx.cmake diff --git a/cmake/Modules/Documentation.cmake b/cmake/Modules/Documentation.cmake index 400109067f..7b8f4a5ba0 100644 --- a/cmake/Modules/Documentation.cmake +++ b/cmake/Modules/Documentation.cmake @@ -4,6 +4,8 @@ option(BUILD_DOC "Build LAMMPS HTML documentation" OFF) if(BUILD_DOC) + option(BUILD_DOC_VENV "Build LAMMPS documentation virtual environment" ON) + mark_as_advanced(BUILD_DOC_VENV) # Current Sphinx versions require at least Python 3.8 # use default (or custom) Python executable, if version is sufficient if(Python_VERSION VERSION_GREATER_EQUAL 3.8) @@ -18,14 +20,6 @@ if(BUILD_DOC) find_package(Doxygen 1.8.10 REQUIRED) file(GLOB DOC_SOURCES CONFIGURE_DEPENDS ${LAMMPS_DOC_DIR}/src/[^.]*.rst) - add_custom_command( - OUTPUT docenv - COMMAND ${VIRTUALENV} docenv - ) - - set(DOCENV_BINARY_DIR ${CMAKE_BINARY_DIR}/docenv/bin) - set(DOCENV_REQUIREMENTS_FILE ${LAMMPS_DOC_DIR}/utils/requirements.txt) - set(SPHINX_CONFIG_DIR ${LAMMPS_DOC_DIR}/utils/sphinx-config) set(SPHINX_CONFIG_FILE_TEMPLATE ${SPHINX_CONFIG_DIR}/conf.py.in) set(SPHINX_STATIC_DIR ${SPHINX_CONFIG_DIR}/_static) @@ -44,14 +38,32 @@ if(BUILD_DOC) # configure paths in conf.py, since relative paths change when file is copied configure_file(${SPHINX_CONFIG_FILE_TEMPLATE} ${DOC_BUILD_CONFIG_FILE}) - add_custom_command( - OUTPUT ${DOC_BUILD_DIR}/requirements.txt - DEPENDS docenv ${DOCENV_REQUIREMENTS_FILE} - COMMAND ${CMAKE_COMMAND} -E copy ${DOCENV_REQUIREMENTS_FILE} ${DOC_BUILD_DIR}/requirements.txt - COMMAND ${DOCENV_BINARY_DIR}/pip $ENV{PIP_OPTIONS} install --upgrade pip - COMMAND ${DOCENV_BINARY_DIR}/pip $ENV{PIP_OPTIONS} install --upgrade ${LAMMPS_DOC_DIR}/utils/converters - COMMAND ${DOCENV_BINARY_DIR}/pip $ENV{PIP_OPTIONS} install -r ${DOC_BUILD_DIR}/requirements.txt --upgrade - ) + if(BUILD_DOC_VENV) + add_custom_command( + OUTPUT docenv + COMMAND ${VIRTUALENV} docenv + ) + + set(DOCENV_BINARY_DIR ${CMAKE_BINARY_DIR}/docenv/bin) + set(DOCENV_REQUIREMENTS_FILE ${LAMMPS_DOC_DIR}/utils/requirements.txt) + + add_custom_command( + OUTPUT ${DOC_BUILD_DIR}/requirements.txt + DEPENDS docenv ${DOCENV_REQUIREMENTS_FILE} + COMMAND ${CMAKE_COMMAND} -E copy ${DOCENV_REQUIREMENTS_FILE} ${DOC_BUILD_DIR}/requirements.txt + COMMAND ${DOCENV_BINARY_DIR}/pip $ENV{PIP_OPTIONS} install --upgrade pip + COMMAND ${DOCENV_BINARY_DIR}/pip $ENV{PIP_OPTIONS} install --upgrade ${LAMMPS_DOC_DIR}/utils/converters + COMMAND ${DOCENV_BINARY_DIR}/pip $ENV{PIP_OPTIONS} install -r ${DOC_BUILD_DIR}/requirements.txt --upgrade + ) + + set(DOCENV_DEPS docenv ${DOC_BUILD_DIR}/requirements.txt) + if(NOT TARGET Sphinx::sphinx-build) + add_executable(Sphinx::sphinx-build IMPORTED GLOBAL) + set_target_properties(Sphinx::sphinx-build PROPERTIES IMPORTED_LOCATION "${DOCENV_BINARY_DIR}/sphinx-build") + endif() + else() + find_package(Sphinx) + endif() set(MATHJAX_URL "https://github.com/mathjax/MathJax/archive/3.1.3.tar.gz" CACHE STRING "URL for MathJax tarball") set(MATHJAX_MD5 "b81661c6e6ba06278e6ae37b30b0c492" CACHE STRING "MD5 checksum of MathJax tarball") @@ -97,8 +109,8 @@ if(BUILD_DOC) endif() add_custom_command( OUTPUT html - DEPENDS ${DOC_SOURCES} docenv ${DOC_BUILD_DIR}/requirements.txt ${DOXYGEN_XML_DIR}/index.xml ${BUILD_DOC_CONFIG_FILE} - COMMAND ${DOCENV_BINARY_DIR}/sphinx-build ${SPHINX_EXTRA_OPTS} -b html -c ${DOC_BUILD_DIR} -d ${DOC_BUILD_DIR}/doctrees ${LAMMPS_DOC_DIR}/src ${DOC_BUILD_DIR}/html + DEPENDS ${DOC_SOURCES} ${DOCENV_DEPS} ${DOXYGEN_XML_DIR}/index.xml ${BUILD_DOC_CONFIG_FILE} + COMMAND Sphinx::sphinx-build ${SPHINX_EXTRA_OPTS} -b html -c ${DOC_BUILD_DIR} -d ${DOC_BUILD_DIR}/doctrees ${LAMMPS_DOC_DIR}/src ${DOC_BUILD_DIR}/html COMMAND ${CMAKE_COMMAND} -E create_symlink Manual.html ${DOC_BUILD_DIR}/html/index.html COMMAND ${CMAKE_COMMAND} -E copy_directory ${LAMMPS_DOC_DIR}/src/PDF ${DOC_BUILD_DIR}/html/PDF COMMAND ${CMAKE_COMMAND} -E remove -f ${DOXYGEN_XML_DIR}/run.stamp diff --git a/cmake/Modules/FindSphinx.cmake b/cmake/Modules/FindSphinx.cmake new file mode 100644 index 0000000000..3718ecc543 --- /dev/null +++ b/cmake/Modules/FindSphinx.cmake @@ -0,0 +1,29 @@ +# Find sphinx-build +find_program(Sphinx_EXECUTABLE NAMES sphinx-build + PATH_SUFFIXES bin + DOC "Sphinx documenation build executable") +mark_as_advanced(Sphinx_EXECUTABLE) + +if(Sphinx_EXECUTABLE) + execute_process(COMMAND ${Sphinx_EXECUTABLE} --version + OUTPUT_VARIABLE sphinx_version + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _sphinx_version_result) + + if(_sphinx_version_result) + message(WARNING "Unable to determine sphinx-build verison: ${_sphinx_version_result}") + else() + string(REGEX REPLACE "sphinx-build ([0-9.]+).*" + "\\1" + Sphinx_VERSION + "${sphinx_version}") + endif() + + if(NOT TARGET Sphinx::sphinx-build) + add_executable(Sphinx::sphinx-build IMPORTED GLOBAL) + set_target_properties(Sphinx::sphinx-build PROPERTIES IMPORTED_LOCATION "${Sphinx_EXECUTABLE}") + endif() +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Sphinx REQUIRED_VARS Sphinx_EXECUTABLE VERSION_VAR Sphinx_VERSION) From 18f1d32f8e5f97c6f6e6694fcde021795a311d5f Mon Sep 17 00:00:00 2001 From: jmgoff <34103038+jmgoff@users.noreply.github.com> Date: Thu, 12 Sep 2024 09:00:55 -0600 Subject: [PATCH 278/355] Apply suggestions from code review Co-authored-by: Axel Kohlmeyer --- doc/src/region.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/src/region.rst b/doc/src/region.rst index 7ceef94b04..3a27c4b5ff 100644 --- a/doc/src/region.rst +++ b/doc/src/region.rst @@ -18,7 +18,7 @@ Syntax *delete* = no args *block* args = xlo xhi ylo yhi zlo zhi xlo,xhi,ylo,yhi,zlo,zhi = bounds of block in all dimensions (distance units) - xlo,xhi,ylo,yhi,zlo,zhi can be a variable + xlo,xhi,ylo,yhi,zlo,zhi can be a variable (see below) *cone* args = dim c1 c2 radlo radhi lo hi dim = *x* or *y* or *z* = axis of cone c1,c2 = coords of cone axis in other 2 dimensions (distance units) @@ -38,7 +38,7 @@ Syntax *plane* args = px py pz nx ny nz px,py,pz = point on the plane (distance units) nx,ny,nz = direction normal to plane (distance units) - px,py,pz can be a variable + px,py,pz can be a variable (see below) *prism* args = xlo xhi ylo yhi zlo zhi xy xz yz xlo,xhi,ylo,yhi,zlo,zhi = bounds of untilted prism (distance units) xy = distance to tilt y in x direction (distance units) From dc15e4ae81e34d2eda076afe5a5a42fad9f7c99f Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Thu, 12 Sep 2024 10:50:19 -0600 Subject: [PATCH 279/355] Enforce options in CMake --- cmake/Modules/Packages/KOKKOS.cmake | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cmake/Modules/Packages/KOKKOS.cmake b/cmake/Modules/Packages/KOKKOS.cmake index cbda60fc53..8c5b1229b5 100644 --- a/cmake/Modules/Packages/KOKKOS.cmake +++ b/cmake/Modules/Packages/KOKKOS.cmake @@ -10,6 +10,8 @@ endif() if(Kokkos_ENABLE_CUDA) message(STATUS "KOKKOS: Enabling CUDA LAMBDA function support") set(Kokkos_ENABLE_CUDA_LAMBDA ON CACHE BOOL "" FORCE) + message(STATUS "KOKKOS: Disabling CUDA malloc async support") + set(Kokkos_ENABLE_IMPL_CUDA_MALLOC_ASYNC OFF CACHE BOOL "" FORCE) endif() # Adding OpenMP compiler flags without the checks done for # BUILD_OMP can result in compile failures. Enforce consistency. @@ -18,6 +20,15 @@ if(Kokkos_ENABLE_OPENMP) message(FATAL_ERROR "Must enable BUILD_OMP with Kokkos_ENABLE_OPENMP") endif() endif() + +if(Kokkos_ENABLE_SERIAL) + if(NOT (Kokkos_ENABLE_OPENMP OR Kokkos_ENABLE_THREADS OR + Kokkos_ENABLE_CUDA OR Kokkos_ENABLE_HIP OR Kokkos_ENABLE_SYCL + OR Kokkos_ENABLE_OPENMPTARGET)) + message(STATUS "KOKKOS: Disabling atomics for Serial Backend") + set(Kokkos_ENABLE_ATOMICS_BYPASS ON CACHE BOOL "" FORCE) + endif() +endif() ######################################################################## option(EXTERNAL_KOKKOS "Build against external kokkos library" OFF) From 7b192282994cfca22fae9f06313f6cc996c33ec8 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Thu, 12 Sep 2024 11:30:11 -0600 Subject: [PATCH 280/355] Make realloc_kokkos function safer, suggested in discussion with @weinbe2 --- src/KOKKOS/memory_kokkos.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/KOKKOS/memory_kokkos.h b/src/KOKKOS/memory_kokkos.h index 0c7555875e..2b136375b2 100644 --- a/src/KOKKOS/memory_kokkos.h +++ b/src/KOKKOS/memory_kokkos.h @@ -327,13 +327,23 @@ void destroy_kokkos(TYPE data, typename TYPE::value_type*** &array) /* ---------------------------------------------------------------------- reallocate Kokkos views without initialization deallocate first to reduce memory use + for the first case, enforce values are given for all dimensions + for the second case, allow zero values given for dimensions ------------------------------------------------------------------------- */ template -static void realloc_kokkos(TYPE &data, const char *name, Indices... ns) +static std::enable_if_t realloc_kokkos(TYPE &data, const char *name, Indices... ns) { data = TYPE(); - data = TYPE(Kokkos::NoInit(std::string(name)), ns...); + data = TYPE(std::string(name), ns...); +} + +template +static std::enable_if_t realloc_kokkos_allow_zero(TYPE &data, const char *name, Indices... ns) +{ + data = TYPE(); + if constexpr (sizeof...(Indices) != 0) + data = TYPE(std::string(name), ns...); } /* ---------------------------------------------------------------------- From 72024e90c987cb98d642a5993368d880c7928806 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Thu, 12 Sep 2024 14:18:31 -0600 Subject: [PATCH 281/355] Only check dynamic rank --- src/KOKKOS/memory_kokkos.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/KOKKOS/memory_kokkos.h b/src/KOKKOS/memory_kokkos.h index 2b136375b2..026c8afcb4 100644 --- a/src/KOKKOS/memory_kokkos.h +++ b/src/KOKKOS/memory_kokkos.h @@ -332,14 +332,14 @@ void destroy_kokkos(TYPE data, typename TYPE::value_type*** &array) ------------------------------------------------------------------------- */ template -static std::enable_if_t realloc_kokkos(TYPE &data, const char *name, Indices... ns) +static std::enable_if_t realloc_kokkos(TYPE &data, const char *name, Indices... ns) { data = TYPE(); data = TYPE(std::string(name), ns...); } template -static std::enable_if_t realloc_kokkos_allow_zero(TYPE &data, const char *name, Indices... ns) +static std::enable_if_t realloc_kokkos_allow_zero(TYPE &data, const char *name, Indices... ns) { data = TYPE(); if constexpr (sizeof...(Indices) != 0) From 5699e3c8cf43e6839c636eabfbb60ba148b341ac Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 12 Sep 2024 17:34:12 -0400 Subject: [PATCH 282/355] add overridable settings --- cmake/Modules/Packages/KOKKOS.cmake | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/cmake/Modules/Packages/KOKKOS.cmake b/cmake/Modules/Packages/KOKKOS.cmake index 8c5b1229b5..08b109eff4 100644 --- a/cmake/Modules/Packages/KOKKOS.cmake +++ b/cmake/Modules/Packages/KOKKOS.cmake @@ -10,8 +10,21 @@ endif() if(Kokkos_ENABLE_CUDA) message(STATUS "KOKKOS: Enabling CUDA LAMBDA function support") set(Kokkos_ENABLE_CUDA_LAMBDA ON CACHE BOOL "" FORCE) - message(STATUS "KOKKOS: Disabling CUDA malloc async support") - set(Kokkos_ENABLE_IMPL_CUDA_MALLOC_ASYNC OFF CACHE BOOL "" FORCE) + option(Kokkos_ENABLE_IMPL_CUDA_MALLOC_ASYNC "CUDA asynchronous malloc support" OFF) + mark_as_advanced(Kokkos_ENABLE_IMPL_CUDA_MALLOC_ASYNC) + if(Kokkos_ENABLE_IMPL_CUDA_MALLOC_ASYNC) + message(STATUS "KOKKOS: CUDA malloc async support enabled") + else() + message(STATUS "KOKKOS: CUDA malloc async support disabled") + endif() +endif() +if(Kokkos_ENABLE_HIP) + option(KOKKOS_ENABLE_IMPL_HIP_UNIFIED_MEMORY "Enable unified memory with HIP" ON) + mark_as_advanced(KOKKOS_ENABLE_IMPL_HIP_UNIFIED_MEMORY) + option(KOKKOS_ENABLE_HIP_MULTIPLE_KERNEL_INSTANTIATIONS "Enable multiple kernel instantiations with HIP" ON) + mark_as_advanced(KOKKOS_ENABLE_HIP_MULTIPLE_KERNEL_INSTANTIATIONS) + option(KOKKOS_ENABLE_ROCTHRUST "Use RoCThrust library" ON) + mark_as_advanced(KOKKOS_ENABLE_ROCTHRUST) endif() # Adding OpenMP compiler flags without the checks done for # BUILD_OMP can result in compile failures. Enforce consistency. From 1adaab80aae703bdee10704510caab5d00becee2 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 13 Sep 2024 00:11:13 -0400 Subject: [PATCH 283/355] make certain values for LAMMPS variables are initialized --- src/region_plane.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/region_plane.cpp b/src/region_plane.cpp index 5917efc11b..6dc162eead 100644 --- a/src/region_plane.cpp +++ b/src/region_plane.cpp @@ -27,6 +27,8 @@ using namespace LAMMPS_NS; RegPlane::RegPlane(LAMMPS *lmp, int narg, char **arg) : Region(lmp, narg, arg), xstr(nullptr), ystr(nullptr), zstr(nullptr) { + xvar = yvar = zvar = 0.0; + options(narg - 8, &arg[8]); if (utils::strmatch(arg[2], "^v_")) { From 92bd9fc16105d272c874edf69b9e4990b48cf0c4 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 13 Sep 2024 02:18:48 -0400 Subject: [PATCH 284/355] fix linking a URL and fix spelling --- doc/src/Build_settings.rst | 3 +-- doc/utils/sphinx-config/false_positives.txt | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/src/Build_settings.rst b/doc/src/Build_settings.rst index 2787560be5..e4a53ddee7 100644 --- a/doc/src/Build_settings.rst +++ b/doc/src/Build_settings.rst @@ -229,8 +229,7 @@ can be used with the Intel or GNU compiler (see the ``FFT_LIB`` setting above). The NVIDIA Performance Libraries (NVPL) FFT library is optimized for NVIDIA -Grace Armv9.0 architecture. You can download it from -`https://docs.nvidia.com/nvpl/`_. +Grace Armv9.0 architecture. You can download it from https://docs.nvidia.com/nvpl/ The cuFFT and hipFFT FFT libraries are packaged with NVIDIA's CUDA and AMD's HIP installations, respectively. These FFT libraries require the diff --git a/doc/utils/sphinx-config/false_positives.txt b/doc/utils/sphinx-config/false_positives.txt index 3a714ffdd6..70d6b4e323 100644 --- a/doc/utils/sphinx-config/false_positives.txt +++ b/doc/utils/sphinx-config/false_positives.txt @@ -141,6 +141,7 @@ arg arge args argv +Armv arrhenius Arun arXiv From 97627bd77ab4adaec43e714c2a341e90c9b25204 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 13 Sep 2024 05:34:15 -0400 Subject: [PATCH 285/355] fix indexing error --- src/KOKKOS/pair_reaxff_kokkos.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/KOKKOS/pair_reaxff_kokkos.cpp b/src/KOKKOS/pair_reaxff_kokkos.cpp index 7af5889e62..741d7f846e 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.cpp +++ b/src/KOKKOS/pair_reaxff_kokkos.cpp @@ -578,7 +578,7 @@ void PairReaxFFKokkos::Deallocate_Lookup_Tables() for (i = 0; i <= ntypes; ++i) { if (map[i] == -1) continue; for (j = i; j <= ntypes; ++j) { - if (map[i] == -1) continue; + if (map[j] == -1) continue; if (LR[i][j].n) { sfree(LR[i][j].y); sfree(LR[i][j].H); From 3079d51eaf06fd979801c2470a9ed0498eb62aec Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 13 Sep 2024 05:34:40 -0400 Subject: [PATCH 286/355] enforce that Pair::map is always initialized --- src/REAXFF/pair_reaxff.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/REAXFF/pair_reaxff.cpp b/src/REAXFF/pair_reaxff.cpp index b9f4f6c838..08e90933b2 100644 --- a/src/REAXFF/pair_reaxff.cpp +++ b/src/REAXFF/pair_reaxff.cpp @@ -174,6 +174,7 @@ void PairReaxFF::allocate() memory->create(cutsq,n+1,n+1,"pair:cutsq"); memory->create(cutghost,n+1,n+1,"pair:cutghost"); map = new int[n+1]; + for (int i = 0; i <= n; ++i) map[i] = -1; chi = new double[n+1]; eta = new double[n+1]; From 487f7ade68fa09a0ffe288e3bd6f0668e765f061 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Fri, 13 Sep 2024 12:14:49 -0600 Subject: [PATCH 287/355] Update Kokkos library in LAMMPS to v4.4.1 --- lib/kokkos/CHANGELOG.md | 15 ++ lib/kokkos/CMakeLists.txt | 2 +- lib/kokkos/Makefile.kokkos | 2 +- lib/kokkos/cmake/KokkosCore_config.h.in | 1 + lib/kokkos/cmake/kokkos_enable_options.cmake | 4 +- .../unit_tests/TestWithoutInitializing.hpp | 12 ++ lib/kokkos/core/src/Cuda/Kokkos_CudaSpace.cpp | 39 ++++- lib/kokkos/core/src/Cuda/Kokkos_CudaSpace.hpp | 23 ++- .../core/src/Cuda/Kokkos_Cuda_Instance.cpp | 20 +++ lib/kokkos/core/src/Kokkos_View.hpp | 2 + lib/kokkos/core/src/OpenMP/Kokkos_OpenMP.cpp | 2 +- .../src/OpenMP/Kokkos_OpenMP_Instance.cpp | 39 +++-- .../src/OpenMP/Kokkos_OpenMP_Instance.hpp | 4 +- .../src/OpenMP/Kokkos_OpenMP_UniqueToken.hpp | 3 +- lib/kokkos/core/src/View/Kokkos_ViewAlloc.hpp | 45 ++++++ lib/kokkos/core/src/impl/Kokkos_ViewCtor.hpp | 25 ++- .../core/src/impl/Kokkos_ViewMapping.hpp | 10 +- lib/kokkos/core/unit_test/TestViewOfViews.hpp | 122 ++++++++++---- .../core/unit_test/cuda/TestCuda_Spaces.cpp | 16 ++ lib/kokkos/master_history.txt | 1 + lib/kokkos/simd/src/Kokkos_SIMD_AVX2.hpp | 12 +- lib/kokkos/simd/unit_tests/TestSIMD.cpp | 1 + .../include/TestSIMD_Construction.hpp | 150 ++++++++++++++++++ 23 files changed, 470 insertions(+), 80 deletions(-) create mode 100644 lib/kokkos/simd/unit_tests/include/TestSIMD_Construction.hpp diff --git a/lib/kokkos/CHANGELOG.md b/lib/kokkos/CHANGELOG.md index 78225f9e6c..7b1d69e566 100644 --- a/lib/kokkos/CHANGELOG.md +++ b/lib/kokkos/CHANGELOG.md @@ -1,5 +1,20 @@ # CHANGELOG +## [4.4.01](https://github.com/kokkos/kokkos/tree/4.4.01) +[Full Changelog](https://github.com/kokkos/kokkos/compare/4.0.00...4.4.01) + +### Features: +* Introduce new SequentialHostInit view allocation property [\#7229](https://github.com/kokkos/kokkos/pull/7229) + +### Backend and Architecture Enhancements: + +#### CUDA: +* Experimental support for unified memory mode (intended for Grace-Hopper etc.) [\#6823](https://github.com/kokkos/kokkos/pull/6823) + +### Bug Fixes +* OpenMP: Fix issue related to the visibility of an internal symbol with shared libraries that affected `ScatterView` in particular [\#7284](https://github.com/kokkos/kokkos/pull/7284) +* Fix implicit copy assignment operators in few AVX2 masks being deleted [#7296](https://github.com/kokkos/kokkos/pull/7296) + ## [4.4.00](https://github.com/kokkos/kokkos/tree/4.4.00) [Full Changelog](https://github.com/kokkos/kokkos/compare/4.3.01...4.4.00) diff --git a/lib/kokkos/CMakeLists.txt b/lib/kokkos/CMakeLists.txt index 054de2c1da..736cbac218 100644 --- a/lib/kokkos/CMakeLists.txt +++ b/lib/kokkos/CMakeLists.txt @@ -151,7 +151,7 @@ ENDIF() set(Kokkos_VERSION_MAJOR 4) set(Kokkos_VERSION_MINOR 4) -set(Kokkos_VERSION_PATCH 0) +set(Kokkos_VERSION_PATCH 1) set(Kokkos_VERSION "${Kokkos_VERSION_MAJOR}.${Kokkos_VERSION_MINOR}.${Kokkos_VERSION_PATCH}") message(STATUS "Kokkos version: ${Kokkos_VERSION}") math(EXPR KOKKOS_VERSION "${Kokkos_VERSION_MAJOR} * 10000 + ${Kokkos_VERSION_MINOR} * 100 + ${Kokkos_VERSION_PATCH}") diff --git a/lib/kokkos/Makefile.kokkos b/lib/kokkos/Makefile.kokkos index a8e1e803f4..eb059d9b81 100644 --- a/lib/kokkos/Makefile.kokkos +++ b/lib/kokkos/Makefile.kokkos @@ -12,7 +12,7 @@ endif KOKKOS_VERSION_MAJOR = 4 KOKKOS_VERSION_MINOR = 4 -KOKKOS_VERSION_PATCH = 0 +KOKKOS_VERSION_PATCH = 1 KOKKOS_VERSION = $(shell echo $(KOKKOS_VERSION_MAJOR)*10000+$(KOKKOS_VERSION_MINOR)*100+$(KOKKOS_VERSION_PATCH) | bc) # Options: Cuda,HIP,SYCL,OpenMPTarget,OpenMP,Threads,Serial diff --git a/lib/kokkos/cmake/KokkosCore_config.h.in b/lib/kokkos/cmake/KokkosCore_config.h.in index 7997aa3707..a93007ff83 100644 --- a/lib/kokkos/cmake/KokkosCore_config.h.in +++ b/lib/kokkos/cmake/KokkosCore_config.h.in @@ -37,6 +37,7 @@ #cmakedefine KOKKOS_ENABLE_CUDA_LAMBDA // deprecated #cmakedefine KOKKOS_ENABLE_CUDA_CONSTEXPR #cmakedefine KOKKOS_ENABLE_IMPL_CUDA_MALLOC_ASYNC +#cmakedefine KOKKOS_ENABLE_IMPL_CUDA_UNIFIED_MEMORY #cmakedefine KOKKOS_ENABLE_HIP_RELOCATABLE_DEVICE_CODE #cmakedefine KOKKOS_ENABLE_HIP_MULTIPLE_KERNEL_INSTANTIATIONS #cmakedefine KOKKOS_ENABLE_IMPL_HIP_UNIFIED_MEMORY diff --git a/lib/kokkos/cmake/kokkos_enable_options.cmake b/lib/kokkos/cmake/kokkos_enable_options.cmake index b900c4a232..53764b0c68 100644 --- a/lib/kokkos/cmake/kokkos_enable_options.cmake +++ b/lib/kokkos/cmake/kokkos_enable_options.cmake @@ -48,6 +48,8 @@ KOKKOS_ENABLE_OPTION(CUDA_LAMBDA ${CUDA_LAMBDA_DEFAULT} "Whether to allow lambda # resolved but we keep the option around a bit longer to be safe. KOKKOS_ENABLE_OPTION(IMPL_CUDA_MALLOC_ASYNC ON "Whether to enable CudaMallocAsync (requires CUDA Toolkit 11.2)") KOKKOS_ENABLE_OPTION(IMPL_NVHPC_AS_DEVICE_COMPILER OFF "Whether to allow nvc++ as Cuda device compiler") +KOKKOS_ENABLE_OPTION(IMPL_CUDA_UNIFIED_MEMORY OFF "Whether to leverage unified memory architectures for CUDA") + KOKKOS_ENABLE_OPTION(DEPRECATED_CODE_4 ON "Whether code deprecated in major release 4 is available" ) KOKKOS_ENABLE_OPTION(DEPRECATION_WARNINGS ON "Whether to emit deprecation warnings" ) KOKKOS_ENABLE_OPTION(HIP_RELOCATABLE_DEVICE_CODE OFF "Whether to enable relocatable device code (RDC) for HIP") @@ -135,7 +137,7 @@ FUNCTION(check_device_specific_options) ENDIF() ENDFUNCTION() -CHECK_DEVICE_SPECIFIC_OPTIONS(DEVICE CUDA OPTIONS CUDA_UVM CUDA_RELOCATABLE_DEVICE_CODE CUDA_LAMBDA CUDA_CONSTEXPR CUDA_LDG_INTRINSIC) +CHECK_DEVICE_SPECIFIC_OPTIONS(DEVICE CUDA OPTIONS CUDA_UVM CUDA_RELOCATABLE_DEVICE_CODE CUDA_LAMBDA CUDA_CONSTEXPR CUDA_LDG_INTRINSIC IMPL_CUDA_UNIFIED_MEMORY) CHECK_DEVICE_SPECIFIC_OPTIONS(DEVICE HIP OPTIONS HIP_RELOCATABLE_DEVICE_CODE) CHECK_DEVICE_SPECIFIC_OPTIONS(DEVICE HPX OPTIONS IMPL_HPX_ASYNC_DISPATCH) diff --git a/lib/kokkos/containers/unit_tests/TestWithoutInitializing.hpp b/lib/kokkos/containers/unit_tests/TestWithoutInitializing.hpp index 7201cd402a..e8558628dc 100644 --- a/lib/kokkos/containers/unit_tests/TestWithoutInitializing.hpp +++ b/lib/kokkos/containers/unit_tests/TestWithoutInitializing.hpp @@ -37,6 +37,17 @@ #endif ///@} +/// Some tests are skipped for unified memory space +#if defined(KOKKOS_ENABLE_IMPL_CUDA_UNIFIED_MEMORY) +#define GTEST_SKIP_IF_UNIFIED_MEMORY_SPACE \ + if constexpr (std::is_same_v) \ + GTEST_SKIP() << "skipping since unified memory requires additional " \ + "fences"; +#else +#define GTEST_SKIP_IF_UNIFIED_MEMORY_SPACE +#endif + TEST(TEST_CATEGORY, resize_realloc_no_init_dualview) { using namespace Kokkos::Test::Tools; listen_tool_events(Config::DisableAll(), Config::EnableKernels()); @@ -657,6 +668,7 @@ TEST(TEST_CATEGORY, create_mirror_no_init_dynamicview) { TEST(TEST_CATEGORY, create_mirror_view_and_copy_dynamicview) { GTEST_SKIP_IF_CUDAUVM_MEMORY_SPACE + GTEST_SKIP_IF_UNIFIED_MEMORY_SPACE using namespace Kokkos::Test::Tools; listen_tool_events(Config::DisableAll(), Config::EnableKernels(), diff --git a/lib/kokkos/core/src/Cuda/Kokkos_CudaSpace.cpp b/lib/kokkos/core/src/Cuda/Kokkos_CudaSpace.cpp index 75318aff77..6ae24022c8 100644 --- a/lib/kokkos/core/src/Cuda/Kokkos_CudaSpace.cpp +++ b/lib/kokkos/core/src/Cuda/Kokkos_CudaSpace.cpp @@ -31,7 +31,6 @@ #include #include -//#include #include #include @@ -178,6 +177,29 @@ void *impl_allocate_common(const int device_id, cudaError_t error_code = cudaSuccess; #ifndef CUDART_VERSION #error CUDART_VERSION undefined! +#elif defined(KOKKOS_ENABLE_IMPL_CUDA_UNIFIED_MEMORY) + // This is intended for Grace-Hopper (and future unified memory architectures) + // The idea is to use host allocator and then advise to keep it in HBM on the + // device, but that requires CUDA 12.2 + static_assert(CUDART_VERSION >= 12020, + "CUDA runtime version >=12.2 required when " + "Kokkos_ENABLE_IMPL_CUDA_UNIFIED_MEMORY is set. " + "Please update your CUDA runtime version or " + "reconfigure with " + "-D Kokkos_ENABLE_IMPL_CUDA_UNIFIED_MEMORY=OFF"); + if (arg_alloc_size) { // cudaMemAdvise_v2 does not work with nullptr + error_code = cudaMallocManaged(&ptr, arg_alloc_size, cudaMemAttachGlobal); + if (error_code == cudaSuccess) { + // One would think cudaMemLocation{device_id, + // cudaMemLocationTypeDevice} would work but it doesn't. I.e. the order of + // members doesn't seem to be defined. + cudaMemLocation loc; + loc.id = device_id; + loc.type = cudaMemLocationTypeDevice; + KOKKOS_IMPL_CUDA_SAFE_CALL(cudaMemAdvise_v2( + ptr, arg_alloc_size, cudaMemAdviseSetPreferredLocation, loc)); + } + } #elif (defined(KOKKOS_ENABLE_IMPL_CUDA_MALLOC_ASYNC) && CUDART_VERSION >= 11020) if (arg_alloc_size >= memory_threshold_g) { error_code = cudaMallocAsync(&ptr, arg_alloc_size, stream); @@ -190,9 +212,13 @@ void *impl_allocate_common(const int device_id, "Kokkos::Cuda: backend fence after async malloc"); } } - } else + } else { + error_code = cudaMalloc(&ptr, arg_alloc_size); + } +#else + error_code = cudaMalloc(&ptr, arg_alloc_size); #endif - { error_code = cudaMalloc(&ptr, arg_alloc_size); } + if (error_code != cudaSuccess) { // TODO tag as unlikely branch // This is the only way to clear the last error, which // we should do here since we're turning it into an @@ -326,6 +352,9 @@ void CudaSpace::impl_deallocate( } #ifndef CUDART_VERSION #error CUDART_VERSION undefined! +#elif defined(KOKKOS_ENABLE_IMPL_CUDA_UNIFIED_MEMORY) + KOKKOS_IMPL_CUDA_SAFE_CALL(cudaSetDevice(m_device)); + KOKKOS_IMPL_CUDA_SAFE_CALL(cudaFree(arg_alloc_ptr)); #elif (defined(KOKKOS_ENABLE_IMPL_CUDA_MALLOC_ASYNC) && CUDART_VERSION >= 11020) if (arg_alloc_size >= memory_threshold_g) { Impl::cuda_device_synchronize( @@ -436,8 +465,12 @@ void cuda_prefetch_pointer(const Cuda &space, const void *ptr, size_t bytes, #include +#if !defined(KOKKOS_ENABLE_IMPL_CUDA_UNIFIED_MEMORY) KOKKOS_IMPL_HOST_INACCESSIBLE_SHARED_ALLOCATION_RECORD_EXPLICIT_INSTANTIATION( Kokkos::CudaSpace); +#else +KOKKOS_IMPL_SHARED_ALLOCATION_RECORD_EXPLICIT_INSTANTIATION(Kokkos::CudaSpace); +#endif KOKKOS_IMPL_SHARED_ALLOCATION_RECORD_EXPLICIT_INSTANTIATION( Kokkos::CudaUVMSpace); KOKKOS_IMPL_SHARED_ALLOCATION_RECORD_EXPLICIT_INSTANTIATION( diff --git a/lib/kokkos/core/src/Cuda/Kokkos_CudaSpace.hpp b/lib/kokkos/core/src/Cuda/Kokkos_CudaSpace.hpp index 0e20193e8b..e1d062d72d 100644 --- a/lib/kokkos/core/src/Cuda/Kokkos_CudaSpace.hpp +++ b/lib/kokkos/core/src/Cuda/Kokkos_CudaSpace.hpp @@ -88,6 +88,19 @@ class CudaSpace { void* allocate(const char* arg_label, const size_t arg_alloc_size, const size_t arg_logical_size = 0) const; +#if defined(KOKKOS_ENABLE_IMPL_CUDA_UNIFIED_MEMORY) + template + void* allocate(const ExecutionSpace&, const size_t arg_alloc_size) const { + return allocate(arg_alloc_size); + } + template + void* allocate(const ExecutionSpace&, const char* arg_label, + const size_t arg_alloc_size, + const size_t arg_logical_size = 0) const { + return allocate(arg_label, arg_alloc_size, arg_logical_size); + } +#endif + /**\brief Deallocate untracked memory in the cuda space */ void deallocate(void* const arg_alloc_ptr, const size_t arg_alloc_size) const; void deallocate(const char* arg_label, void* const arg_alloc_ptr, @@ -337,7 +350,11 @@ static_assert( template <> struct MemorySpaceAccess { enum : bool { assignable = false }; - enum : bool { accessible = false }; +#if !defined(KOKKOS_ENABLE_IMPL_CUDA_UNIFIED_MEMORY) + enum : bool{accessible = false}; +#else + enum : bool { accessible = true }; +#endif enum : bool { deepcopy = true }; }; @@ -558,8 +575,12 @@ struct DeepCopy #include +namespace { +int g_openmp_hardware_max_threads = 1; +} + namespace Kokkos { namespace Impl { std::vector OpenMPInternal::all_instances; std::mutex OpenMPInternal::all_instances_mutex; +int OpenMPInternal::max_hardware_threads() noexcept { + return g_openmp_hardware_max_threads; +} + void OpenMPInternal::clear_thread_data() { const size_t member_bytes = sizeof(int64_t) * @@ -188,9 +196,9 @@ void OpenMPInternal::initialize(int thread_count) { // Before any other call to OMP query the maximum number of threads // and save the value for re-initialization unit testing. - Impl::g_openmp_hardware_max_threads = get_current_max_threads(); + g_openmp_hardware_max_threads = get_current_max_threads(); - int process_num_threads = Impl::g_openmp_hardware_max_threads; + int process_num_threads = g_openmp_hardware_max_threads; if (Kokkos::hwloc::available()) { process_num_threads = Kokkos::hwloc::get_available_numa_count() * @@ -203,11 +211,11 @@ void OpenMPInternal::initialize(int thread_count) { // process_num_threads if thread_count > 0, set // g_openmp_hardware_max_threads to thread_count if (thread_count < 0) { - thread_count = Impl::g_openmp_hardware_max_threads; + thread_count = g_openmp_hardware_max_threads; } else if (thread_count == 0) { - if (Impl::g_openmp_hardware_max_threads != process_num_threads) { - Impl::g_openmp_hardware_max_threads = process_num_threads; - omp_set_num_threads(Impl::g_openmp_hardware_max_threads); + if (g_openmp_hardware_max_threads != process_num_threads) { + g_openmp_hardware_max_threads = process_num_threads; + omp_set_num_threads(g_openmp_hardware_max_threads); } } else { if (Kokkos::show_warnings() && thread_count > process_num_threads) { @@ -218,16 +226,16 @@ void OpenMPInternal::initialize(int thread_count) { << ", requested thread : " << std::setw(3) << thread_count << std::endl; } - Impl::g_openmp_hardware_max_threads = thread_count; - omp_set_num_threads(Impl::g_openmp_hardware_max_threads); + g_openmp_hardware_max_threads = thread_count; + omp_set_num_threads(g_openmp_hardware_max_threads); } // setup thread local -#pragma omp parallel num_threads(Impl::g_openmp_hardware_max_threads) +#pragma omp parallel num_threads(g_openmp_hardware_max_threads) { Impl::SharedAllocationRecord::tracking_enable(); } auto &instance = OpenMPInternal::singleton(); - instance.m_pool_size = Impl::g_openmp_hardware_max_threads; + instance.m_pool_size = g_openmp_hardware_max_threads; // New, unified host thread team data: { @@ -272,10 +280,9 @@ void OpenMPInternal::finalize() { if (this == &singleton()) { auto const &instance = singleton(); // Silence Cuda Warning - const int nthreads = - instance.m_pool_size <= Impl::g_openmp_hardware_max_threads - ? Impl::g_openmp_hardware_max_threads - : instance.m_pool_size; + const int nthreads = instance.m_pool_size <= g_openmp_hardware_max_threads + ? g_openmp_hardware_max_threads + : instance.m_pool_size; (void)nthreads; #pragma omp parallel num_threads(nthreads) @@ -284,7 +291,7 @@ void OpenMPInternal::finalize() { // allow main thread to track Impl::SharedAllocationRecord::tracking_enable(); - Impl::g_openmp_hardware_max_threads = 1; + g_openmp_hardware_max_threads = 1; } m_initialized = false; @@ -307,7 +314,7 @@ void OpenMPInternal::print_configuration(std::ostream &s) const { if (m_initialized) { const int numa_count = 1; - const int core_per_numa = Impl::g_openmp_hardware_max_threads; + const int core_per_numa = g_openmp_hardware_max_threads; const int thread_per_core = 1; s << " thread_pool_topology[ " << numa_count << " x " << core_per_numa diff --git a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Instance.hpp b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Instance.hpp index f4a0d3e201..2aed723b18 100644 --- a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Instance.hpp +++ b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Instance.hpp @@ -47,8 +47,6 @@ namespace Impl { class OpenMPInternal; -inline int g_openmp_hardware_max_threads = 1; - struct OpenMPTraits { static constexpr int MAX_THREAD_COUNT = 512; }; @@ -86,6 +84,8 @@ class OpenMPInternal { void clear_thread_data(); + static int max_hardware_threads() noexcept; + int thread_pool_size() const { return m_pool_size; } void resize_thread_data(size_t pool_reduce_bytes, size_t team_reduce_bytes, diff --git a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_UniqueToken.hpp b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_UniqueToken.hpp index a37e1758a2..5937c093ba 100644 --- a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_UniqueToken.hpp +++ b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_UniqueToken.hpp @@ -105,7 +105,8 @@ class UniqueToken { /// \brief upper bound for acquired values, i.e. 0 <= value < size() KOKKOS_INLINE_FUNCTION int size() const noexcept { - KOKKOS_IF_ON_HOST((return Kokkos::Impl::g_openmp_hardware_max_threads;)) + KOKKOS_IF_ON_HOST( + (return Kokkos::Impl::OpenMPInternal::max_hardware_threads();)) KOKKOS_IF_ON_DEVICE((return 0;)) } diff --git a/lib/kokkos/core/src/View/Kokkos_ViewAlloc.hpp b/lib/kokkos/core/src/View/Kokkos_ViewAlloc.hpp index 95cb6f619c..1ade75692f 100644 --- a/lib/kokkos/core/src/View/Kokkos_ViewAlloc.hpp +++ b/lib/kokkos/core/src/View/Kokkos_ViewAlloc.hpp @@ -313,6 +313,51 @@ struct ViewValueFunctor { void destroy_shared_allocation() {} }; + +template +struct ViewValueFunctorSequentialHostInit { + using ExecSpace = typename DeviceType::execution_space; + using MemSpace = typename DeviceType::memory_space; + static_assert(SpaceAccessibility::accessible); + + ValueType* ptr; + size_t n; + + ViewValueFunctorSequentialHostInit() = default; + + ViewValueFunctorSequentialHostInit(ExecSpace const& /*arg_space*/, + ValueType* const arg_ptr, + size_t const arg_n, + std::string /*arg_name*/) + : ptr(arg_ptr), n(arg_n) {} + + ViewValueFunctorSequentialHostInit(ValueType* const arg_ptr, + size_t const arg_n, + std::string /*arg_name*/) + : ptr(arg_ptr), n(arg_n) {} + + void construct_shared_allocation() { + if constexpr (std::is_trivial_v) { + // value-initialization is equivalent to filling with zeros + std::memset(static_cast(ptr), 0, n * sizeof(ValueType)); + } else { + for (size_t i = 0; i < n; ++i) { + new (ptr + i) ValueType(); + } + } + } + + void destroy_shared_allocation() { + if constexpr (std::is_trivially_destructible_v) { + // do nothing, don't bother calling the destructor + } else { + for (size_t i = 0; i < n; ++i) { + (ptr + i)->~ValueType(); + } + } + } +}; + } // namespace Kokkos::Impl #endif // KOKKOS_VIEW_ALLOC_HPP diff --git a/lib/kokkos/core/src/impl/Kokkos_ViewCtor.hpp b/lib/kokkos/core/src/impl/Kokkos_ViewCtor.hpp index e1b8ba86a5..379180ae64 100644 --- a/lib/kokkos/core/src/impl/Kokkos_ViewCtor.hpp +++ b/lib/kokkos/core/src/impl/Kokkos_ViewCtor.hpp @@ -23,12 +23,16 @@ namespace Kokkos { namespace Impl { +struct SequentialHostInit_t {}; struct WithoutInitializing_t {}; struct AllowPadding_t {}; template struct is_view_ctor_property : public std::false_type {}; +template <> +struct is_view_ctor_property : public std::true_type {}; + template <> struct is_view_ctor_property : public std::true_type {}; @@ -84,10 +88,10 @@ struct ViewCtorProp> { /* Property flags have constexpr value */ template -struct ViewCtorProp< - std::enable_if_t::value || - std::is_same::value>, - P> { +struct ViewCtorProp || + std::is_same_v || + std::is_same_v>, + P> { ViewCtorProp() = default; ViewCtorProp(const ViewCtorProp &) = default; ViewCtorProp &operator=(const ViewCtorProp &) = default; @@ -199,6 +203,11 @@ struct ViewCtorProp : public ViewCtorProp... { Kokkos::Impl::has_type::value; static constexpr bool initialize = !Kokkos::Impl::has_type::value; + static constexpr bool sequential_host_init = + Kokkos::Impl::has_type::value; + static_assert(initialize || !sequential_host_init, + "Incompatible WithoutInitializing and SequentialHostInit view " + "alloc properties"); using memory_space = typename var_memory_space::type; using execution_space = typename var_execution_space::type; @@ -251,7 +260,9 @@ auto with_properties_if_unset(const ViewCtorProp &view_ctor_prop, (is_view_label::value && !ViewCtorProp::has_label) || (std::is_same_v && - ViewCtorProp::initialize)) { + ViewCtorProp::initialize) || + (std::is_same_v && + !ViewCtorProp::sequential_host_init)) { using NewViewCtorProp = ViewCtorProp; NewViewCtorProp new_view_ctor_prop(view_ctor_prop); static_cast &>(new_view_ctor_prop).value = @@ -299,7 +310,9 @@ struct WithPropertiesIfUnset, Property, Properties...> { (is_view_label::value && !ViewCtorProp::has_label) || (std::is_same_v && - ViewCtorProp::initialize)) { + ViewCtorProp::initialize) || + (std::is_same_v && + !ViewCtorProp::sequential_host_init)) { using NewViewCtorProp = ViewCtorProp; NewViewCtorProp new_view_ctor_prop(view_ctor_prop); static_cast &>(new_view_ctor_prop).value = diff --git a/lib/kokkos/core/src/impl/Kokkos_ViewMapping.hpp b/lib/kokkos/core/src/impl/Kokkos_ViewMapping.hpp index 8919dccdb7..10aaa63b7c 100644 --- a/lib/kokkos/core/src/impl/Kokkos_ViewMapping.hpp +++ b/lib/kokkos/core/src/impl/Kokkos_ViewMapping.hpp @@ -2825,10 +2825,12 @@ class ViewMapping< using memory_space = typename Traits::memory_space; static_assert( SpaceAccessibility::accessible); - using value_type = typename Traits::value_type; - using functor_type = - ViewValueFunctor, - value_type>; + using device_type = Kokkos::Device; + using value_type = typename Traits::value_type; + using functor_type = std::conditional_t< + alloc_prop::sequential_host_init, + ViewValueFunctorSequentialHostInit, + ViewValueFunctor>; using record_type = Kokkos::Impl::SharedAllocationRecord; diff --git a/lib/kokkos/core/unit_test/TestViewOfViews.hpp b/lib/kokkos/core/unit_test/TestViewOfViews.hpp index a87c829bb7..1d53bca336 100644 --- a/lib/kokkos/core/unit_test/TestViewOfViews.hpp +++ b/lib/kokkos/core/unit_test/TestViewOfViews.hpp @@ -20,7 +20,7 @@ namespace { -// User-defined type with a View data member +// User-defined types with a View data member template class S { V v_; @@ -28,48 +28,102 @@ class S { public: template S(std::string label, Extents... extents) : v_(std::move(label), extents...) {} - S() = default; + KOKKOS_DEFAULTED_FUNCTION S() = default; }; template -void test_view_of_views() { +class N { // not default constructible + V v_; + + public: + template + N(std::string label, Extents... extents) : v_(std::move(label), extents...) {} +}; + +template +class H { // constructible and destructible only from on the host side + V v_; + + public: + template + H(std::string label, Extents... extents) : v_(std::move(label), extents...) {} + H() {} + ~H() {} +}; + +template +void test_view_of_views_default() { + // assigning a default-constructed view to destruct the inner objects using VoV = Kokkos::View; - { // assigning a default-constructed view to destruct the inner objects - VoV vov("vov", 2, 3); - V a("a"); - V b("b"); - vov(0, 0) = a; - vov(1, 0) = a; - vov(0, 1) = b; + VoV vov("vov", 2, 3); + V a("a"); + V b("b"); + vov(0, 0) = a; + vov(1, 0) = a; + vov(0, 1) = b; #ifndef KOKKOS_ENABLE_IMPL_VIEW_OF_VIEWS_DESTRUCTOR_PRECONDITION_VIOLATION_WORKAROUND - vov(0, 0) = V(); - vov(1, 0) = V(); - vov(0, 1) = V(); + vov(0, 0) = V(); + vov(1, 0) = V(); + vov(0, 1) = V(); #endif - } - { // using placement new to construct the inner objects and explicitly - // calling the destructor - VoV vov(Kokkos::view_alloc("vov", Kokkos::WithoutInitializing), 2, 3); - V a("a"); - V b("b"); - new (&vov(0, 0)) V(a); - new (&vov(1, 0)) V(a); - new (&vov(0, 1)) V(b); -#ifndef KOKKOS_ENABLE_IMPL_VIEW_OF_VIEWS_DESTRUCTOR_PRECONDITION_VIOLATION_WORKAROUND - vov(0, 0).~V(); - vov(1, 0).~V(); - vov(0, 1).~V(); -#else - // leaks memory -#endif - } } -TEST(TEST_CATEGORY, view_of_views) { - test_view_of_views>(); - test_view_of_views>(); +template +void test_view_of_views_without_initializing() { + // using placement new to construct the inner objects and explicitly + // calling the destructor + using VoV = Kokkos::View; + VoV vov(Kokkos::view_alloc("vov", Kokkos::WithoutInitializing), 2, 3); + V a("a"); + V b("b"); + new (&vov(0, 0)) V(a); + new (&vov(1, 0)) V(a); + new (&vov(0, 1)) V(b); +#ifndef KOKKOS_ENABLE_IMPL_VIEW_OF_VIEWS_DESTRUCTOR_PRECONDITION_VIOLATION_WORKAROUND + vov(0, 0).~V(); + vov(1, 0).~V(); + vov(0, 1).~V(); +#else + // leaks memory +#endif +} + +template +void test_view_of_views_sequential_host_init() { + // inner views value-initialized sequentially on the host, and also + // sequentially destructed on the host, without the need to cleanup + using VoV = Kokkos::View; + VoV vov(Kokkos::view_alloc("vov", Kokkos::SequentialHostInit), 2, 3); + V a("a"); + V b("b"); + vov(0, 0) = a; + vov(1, 0) = a; + vov(0, 1) = b; +} + +TEST(TEST_CATEGORY, view_of_views_default) { + test_view_of_views_default>(); + test_view_of_views_default>(); // User-defined type with View data member - test_view_of_views>>(); + test_view_of_views_default>>(); +} + +TEST(TEST_CATEGORY, view_of_views_without_initializing) { + test_view_of_views_without_initializing>(); + test_view_of_views_without_initializing< + S>>(); + test_view_of_views_without_initializing< + N>>(); + test_view_of_views_without_initializing< + H>>(); +} + +TEST(TEST_CATEGORY, test_view_of_views_sequential_host_init) { + test_view_of_views_sequential_host_init>(); + test_view_of_views_sequential_host_init< + S>>(); + test_view_of_views_sequential_host_init< + H>>(); } } // namespace diff --git a/lib/kokkos/core/unit_test/cuda/TestCuda_Spaces.cpp b/lib/kokkos/core/unit_test/cuda/TestCuda_Spaces.cpp index 11fe6b8555..f40af99e7c 100644 --- a/lib/kokkos/core/unit_test/cuda/TestCuda_Spaces.cpp +++ b/lib/kokkos/core/unit_test/cuda/TestCuda_Spaces.cpp @@ -39,9 +39,14 @@ TEST(cuda, space_access) { !Kokkos::Impl::MemorySpaceAccess::assignable); +#ifndef KOKKOS_ENABLE_IMPL_CUDA_UNIFIED_MEMORY static_assert( !Kokkos::Impl::MemorySpaceAccess::accessible); +#else + static_assert(Kokkos::Impl::MemorySpaceAccess::accessible); +#endif static_assert( !Kokkos::Impl::MemorySpaceAccess::accessible); +#ifndef KOKKOS_ENABLE_IMPL_CUDA_UNIFIED_MEMORY static_assert(!Kokkos::SpaceAccessibility::accessible); +#else + static_assert(Kokkos::SpaceAccessibility::accessible); +#endif static_assert(Kokkos::SpaceAccessibility::accessible); @@ -157,8 +167,14 @@ TEST(cuda, space_access) { Kokkos::SpaceAccessibility::accessible); +#ifndef KOKKOS_ENABLE_IMPL_CUDA_UNIFIED_MEMORY static_assert(std::is_same::Space, Kokkos::HostSpace>::value); +#else + static_assert(std::is_same::Space, + Kokkos::Device>::value); +#endif static_assert( std::is_same::Space, diff --git a/lib/kokkos/master_history.txt b/lib/kokkos/master_history.txt index a0e83bef23..f2a4163610 100644 --- a/lib/kokkos/master_history.txt +++ b/lib/kokkos/master_history.txt @@ -38,3 +38,4 @@ tag: 4.2.01 date: 01:30:2024 master: 71a9bcae release: 221e5f7a tag: 4.3.00 date: 04:03:2024 master: e0dc0128 release: f08217a4 tag: 4.3.01 date: 05:07:2024 master: 486cc745 release: 262d2d6e tag: 4.4.00 date: 08:08:2024 master: 6ecdf605 release: 6068673c +tag: 4.4.01 date: 09:12:2024 master: 08ceff92 release: 2d60c039 diff --git a/lib/kokkos/simd/src/Kokkos_SIMD_AVX2.hpp b/lib/kokkos/simd/src/Kokkos_SIMD_AVX2.hpp index 27c8af79ab..0525dc8887 100644 --- a/lib/kokkos/simd/src/Kokkos_SIMD_AVX2.hpp +++ b/lib/kokkos/simd/src/Kokkos_SIMD_AVX2.hpp @@ -361,9 +361,7 @@ class simd_mask> { }; using value_type = bool; using abi_type = simd_abi::avx2_fixed_size<4>; - KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask() = default; - KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask(simd_mask const&) = default; - KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask(simd_mask&&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask() = default; KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION explicit simd_mask(value_type value) : m_value(_mm_set1_epi32(-std::int32_t(value))) {} KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION static constexpr std::size_t size() { @@ -460,9 +458,7 @@ class simd_mask> { }; using value_type = bool; using abi_type = simd_abi::avx2_fixed_size<8>; - KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask() = default; - KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask(simd_mask const&) = default; - KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask(simd_mask&&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask() = default; KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION explicit simd_mask(value_type value) : m_value(_mm256_set1_epi32(-std::int32_t(value))) {} KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION static constexpr std::size_t size() { @@ -561,9 +557,7 @@ class simd_mask> { }; using value_type = bool; using abi_type = simd_abi::avx2_fixed_size<4>; - KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask() = default; - KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask(simd_mask const&) = default; - KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask(simd_mask&&) = default; + KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION simd_mask() = default; KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION explicit simd_mask(value_type value) : m_value(_mm256_set1_epi64x(-std::int64_t(value))) {} KOKKOS_IMPL_HOST_FORCEINLINE_FUNCTION static constexpr std::size_t size() { diff --git a/lib/kokkos/simd/unit_tests/TestSIMD.cpp b/lib/kokkos/simd/unit_tests/TestSIMD.cpp index 7a1f9be2a0..df18b43c4e 100644 --- a/lib/kokkos/simd/unit_tests/TestSIMD.cpp +++ b/lib/kokkos/simd/unit_tests/TestSIMD.cpp @@ -22,3 +22,4 @@ #include #include #include +#include diff --git a/lib/kokkos/simd/unit_tests/include/TestSIMD_Construction.hpp b/lib/kokkos/simd/unit_tests/include/TestSIMD_Construction.hpp new file mode 100644 index 0000000000..0ceb1496c4 --- /dev/null +++ b/lib/kokkos/simd/unit_tests/include/TestSIMD_Construction.hpp @@ -0,0 +1,150 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#ifndef KOKKOS_TEST_SIMD_CONSTRUCTION_HPP +#define KOKKOS_TEST_SIMD_CONSTRUCTION_HPP + +#include +#include + +template +inline void host_test_simd_traits() { + using simd_type = Kokkos::Experimental::simd; + + static_assert(std::is_nothrow_default_constructible_v); + static_assert(std::is_nothrow_copy_assignable_v); + static_assert(std::is_nothrow_copy_constructible_v); + static_assert(std::is_nothrow_move_assignable_v); + static_assert(std::is_nothrow_move_constructible_v); + + simd_type default_simd, result; + simd_type test_simd(KOKKOS_LAMBDA(std::size_t i) { return (i % 2 == 0); }); + simd_type copy_simd(test_simd); + simd_type move_simd(std::move(copy_simd)); + default_simd = std::move(move_simd); + result = default_simd; + EXPECT_TRUE(all_of(test_simd == result)); +} + +template +inline void host_test_mask_traits() { + using mask_type = Kokkos::Experimental::simd_mask; + + static_assert(std::is_nothrow_default_constructible_v); + static_assert(std::is_nothrow_copy_assignable_v); + static_assert(std::is_nothrow_copy_constructible_v); + static_assert(std::is_nothrow_move_assignable_v); + static_assert(std::is_nothrow_move_constructible_v); + + mask_type default_mask, result; + mask_type test_mask(KOKKOS_LAMBDA(std::size_t i) { return (i % 2 == 0); }); + mask_type copy_mask(test_mask); + mask_type move_mask(std::move(copy_mask)); + default_mask = std::move(move_mask); + result = default_mask; + EXPECT_EQ(test_mask, result); +} + +template +inline void host_check_construction() { + if constexpr (is_type_v>) { + host_test_simd_traits(); + host_test_mask_traits(); + } +} + +template +inline void host_check_construction_all_types( + Kokkos::Experimental::Impl::data_types) { + (host_check_construction(), ...); +} + +template +inline void host_check_construction_all_abis( + Kokkos::Experimental::Impl::abi_set) { + using DataTypes = Kokkos::Experimental::Impl::data_type_set; + (host_check_construction_all_types(DataTypes()), ...); +} + +template +KOKKOS_INLINE_FUNCTION void device_test_simd_traits() { + using simd_type = Kokkos::Experimental::simd; + + simd_type default_simd, result; + simd_type test_simd(KOKKOS_LAMBDA(std::size_t i) { return (i % 2 == 0); }); + simd_type copy_simd(test_simd); + simd_type move_simd(std::move(copy_simd)); + default_simd = std::move(move_simd); + result = default_simd; + + kokkos_checker checker; + checker.truth(all_of(test_simd == result)); +} + +template +KOKKOS_INLINE_FUNCTION void device_test_mask_traits() { + using mask_type = Kokkos::Experimental::simd_mask; + + mask_type default_mask, result; + mask_type test_mask(KOKKOS_LAMBDA(std::size_t i) { return (i % 2 == 0); }); + mask_type copy_mask(test_mask); + mask_type move_mask(std::move(copy_mask)); + default_mask = std::move(move_mask); + result = default_mask; + + kokkos_checker checker; + checker.truth(test_mask == result); +} + +template +KOKKOS_INLINE_FUNCTION void device_check_construction() { + if constexpr (is_type_v>) { + device_test_simd_traits(); + device_test_mask_traits(); + } +} + +template +KOKKOS_INLINE_FUNCTION void device_check_construction_all_types( + Kokkos::Experimental::Impl::data_types) { + (device_check_construction(), ...); +} + +template +KOKKOS_INLINE_FUNCTION void device_check_construction_all_abis( + Kokkos::Experimental::Impl::abi_set) { + using DataTypes = Kokkos::Experimental::Impl::data_type_set; + (device_check_construction_all_types(DataTypes()), ...); +} + +class simd_device_construction_functor { + public: + KOKKOS_INLINE_FUNCTION void operator()(int) const { + device_check_construction_all_abis( + Kokkos::Experimental::Impl::device_abi_set()); + } +}; + +TEST(simd, host_construction) { + host_check_construction_all_abis(Kokkos::Experimental::Impl::host_abi_set()); +} + +TEST(simd, device_construction) { + Kokkos::parallel_for(Kokkos::RangePolicy>(0, 1), + simd_device_construction_functor()); +} + +#endif From 5075c7cfa135eeb401e2b35ab35385b160e476ff Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Fri, 13 Sep 2024 12:16:58 -0600 Subject: [PATCH 288/355] Update CMake --- cmake/Modules/Packages/KOKKOS.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/Modules/Packages/KOKKOS.cmake b/cmake/Modules/Packages/KOKKOS.cmake index 08b109eff4..691be4cd67 100644 --- a/cmake/Modules/Packages/KOKKOS.cmake +++ b/cmake/Modules/Packages/KOKKOS.cmake @@ -69,8 +69,8 @@ if(DOWNLOAD_KOKKOS) list(APPEND KOKKOS_LIB_BUILD_ARGS "-DCMAKE_CXX_EXTENSIONS=${CMAKE_CXX_EXTENSIONS}") list(APPEND KOKKOS_LIB_BUILD_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}") include(ExternalProject) - set(KOKKOS_URL "https://github.com/kokkos/kokkos/archive/4.4.00.tar.gz" CACHE STRING "URL for KOKKOS tarball") - set(KOKKOS_MD5 "95af2e2d4b10a67a63cce09715fba127" CACHE STRING "MD5 checksum of KOKKOS tarball") + set(KOKKOS_URL "https://github.com/kokkos/kokkos/archive/4.4.01.tar.gz" CACHE STRING "URL for KOKKOS tarball") + set(KOKKOS_MD5 "de6ee80d00b6212b02bfb7f1e71a8392" CACHE STRING "MD5 checksum of KOKKOS tarball") mark_as_advanced(KOKKOS_URL) mark_as_advanced(KOKKOS_MD5) GetFallbackURL(KOKKOS_URL KOKKOS_FALLBACK) @@ -95,7 +95,7 @@ if(DOWNLOAD_KOKKOS) add_dependencies(LAMMPS::KOKKOSCORE kokkos_build) add_dependencies(LAMMPS::KOKKOSCONTAINERS kokkos_build) elseif(EXTERNAL_KOKKOS) - find_package(Kokkos 4.4.00 REQUIRED CONFIG) + find_package(Kokkos 4.4.01 REQUIRED CONFIG) target_link_libraries(lammps PRIVATE Kokkos::kokkos) else() set(LAMMPS_LIB_KOKKOS_SRC_DIR ${LAMMPS_LIB_SOURCE_DIR}/kokkos) From fe8611bfc85010e57b9466d8c39273be8f3f1831 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 13 Sep 2024 22:55:05 -0400 Subject: [PATCH 289/355] sync author info with LAMMPS home page --- doc/src/Intro_authors.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/src/Intro_authors.rst b/doc/src/Intro_authors.rst index 78c8506421..84470ba3a0 100644 --- a/doc/src/Intro_authors.rst +++ b/doc/src/Intro_authors.rst @@ -56,7 +56,7 @@ lammps.org". General questions about LAMMPS should be posted in the - SNL - jmgoff at sandia.gov - machine learned potentials, QEq solvers, Python - * - Megan McCarthy + * - Meg McCarthy - SNL - megmcca at sandia.gov - alloys, micro-structure, machine learned potentials @@ -67,7 +67,7 @@ lammps.org". General questions about LAMMPS should be posted in the * - `Trung Nguyen `_ - U Chicago - ndactrung at gmail.com - - soft matter, GPU package + - soft matter, GPU package, DIELECTRIC package, regression testing .. _rb: https://rbberger.github.io/ .. _gc: https://enthalpiste.fr/ From bdca275f1032aa56ce8d2b63dc76dc943f59b0fc Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 14 Sep 2024 02:25:03 -0400 Subject: [PATCH 290/355] correct indexing when filling lmp_firstneigh array --- src/ML-IAP/mliap_data.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ML-IAP/mliap_data.cpp b/src/ML-IAP/mliap_data.cpp index 5d847ee25e..1ca57e2745 100644 --- a/src/ML-IAP/mliap_data.cpp +++ b/src/ML-IAP/mliap_data.cpp @@ -185,7 +185,6 @@ void MLIAPData::generate_neighdata(NeighList *list_in, int eflag_in, int vflag_i int jtype = type[j]; const int jelem = map[jtype]; - lmp_firstneigh[ii][jj] = firstneigh[i][jj]; if (rsq < descriptor->cutsq[ielem][jelem]) { pair_i[ij] = i; jatoms[ij] = j; @@ -193,6 +192,7 @@ void MLIAPData::generate_neighdata(NeighList *list_in, int eflag_in, int vflag_i rij[ij][0] = delx; rij[ij][1] = dely; rij[ij][2] = delz; + lmp_firstneigh[ii][ninside] = firstneigh[i][jj]; ij++; ninside++; } @@ -228,6 +228,7 @@ void MLIAPData::grow_neigharrays() memory->grow(ielems, natomneigh, "MLIAPData:ielems"); memory->grow(itypes, natomneigh, "MLIAPData:itypes"); memory->grow(numneighs, natomneigh, "MLIAPData:numneighs"); + memory->grow(lmp_firstneigh, natomneigh, nneigh_max, "MLIAPData:lmp_firstneigh"); natomneigh_max = natomneigh; } From 1587473ab58559d895d2811d6dd8fe845e8d0015 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 14 Sep 2024 05:02:18 -0400 Subject: [PATCH 291/355] fix comparison for missing arguments when using wildcards --- src/EXTRA-FIX/fix_ave_correlate_long.cpp | 4 ++-- src/fix_ave_chunk.cpp | 22 +++++++++++----------- src/fix_ave_grid.cpp | 14 +++++++------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/EXTRA-FIX/fix_ave_correlate_long.cpp b/src/EXTRA-FIX/fix_ave_correlate_long.cpp index 738ae3ae4c..abb1ad87de 100644 --- a/src/EXTRA-FIX/fix_ave_correlate_long.cpp +++ b/src/EXTRA-FIX/fix_ave_correlate_long.cpp @@ -166,12 +166,12 @@ FixAveCorrelateLong::FixAveCorrelateLong(LAMMPS *lmp, int narg, char **arg) : overwrite = 1; iarg += 1; } else if (strcmp(arg[iarg], "title1") == 0) { - if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "fix ave/correlate/long title1", error); + if (iarg + 2 > nargnew) utils::missing_cmd_args(FLERR, "fix ave/correlate/long title1", error); delete[] title1; title1 = utils::strdup(arg[iarg + 1]); iarg += 2; } else if (strcmp(arg[iarg], "title2") == 0) { - if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "fix ave/correlate/long title2", error); + if (iarg + 2 > nargnew) utils::missing_cmd_args(FLERR, "fix ave/correlate/long title2", error); delete[] title2; title2 = utils::strdup(arg[iarg + 1]); iarg += 2; diff --git a/src/fix_ave_chunk.cpp b/src/fix_ave_chunk.cpp index d9723cec9f..6a3c2e2032 100644 --- a/src/fix_ave_chunk.cpp +++ b/src/fix_ave_chunk.cpp @@ -153,7 +153,7 @@ FixAveChunk::FixAveChunk(LAMMPS *lmp, int narg, char **arg) : while (iarg < nargnew) { if (strcmp(arg[iarg],"norm") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "fix ave/chunk norm", error); + if (iarg+2 > nargnew) utils::missing_cmd_args(FLERR, "fix ave/chunk norm", error); if (strcmp(arg[iarg+1],"all") == 0) { normflag = ALL; scaleflag = ATOM; @@ -166,13 +166,13 @@ FixAveChunk::FixAveChunk(LAMMPS *lmp, int narg, char **arg) : } else error->all(FLERR,"Unknown fix ave/chunk norm mode: {}", arg[iarg+1]); iarg += 2; } else if (strcmp(arg[iarg],"ave") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "fix ave/chunk ave", error); + if (iarg+2 > nargnew) utils::missing_cmd_args(FLERR, "fix ave/chunk ave", error); if (strcmp(arg[iarg+1],"one") == 0) ave = ONE; else if (strcmp(arg[iarg+1],"running") == 0) ave = RUNNING; else if (strcmp(arg[iarg+1],"window") == 0) ave = WINDOW; else error->all(FLERR,"Unknown fix ave/chunk ave mode: {}", arg[iarg+1]); if (ave == WINDOW) { - if (iarg+3 > narg) utils::missing_cmd_args(FLERR, "fix ave/chunk ave window", error); + if (iarg+3 > nargnew) utils::missing_cmd_args(FLERR, "fix ave/chunk ave window", error); nwindow = utils::inumeric(FLERR,arg[iarg+2],false,lmp); if (nwindow <= 0) error->all(FLERR,"Illegal fix ave/chunk number of windows: {}", nwindow); } @@ -180,21 +180,21 @@ FixAveChunk::FixAveChunk(LAMMPS *lmp, int narg, char **arg) : if (ave == WINDOW) iarg++; } else if (strcmp(arg[iarg],"bias") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "fix ave/chunk bias", error); + if (iarg+2 > nargnew) utils::missing_cmd_args(FLERR, "fix ave/chunk bias", error); biasflag = 1; id_bias = utils::strdup(arg[iarg+1]); iarg += 2; } else if (strcmp(arg[iarg],"adof") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "fix ave/chunk adof", error); + if (iarg+2 > nargnew) utils::missing_cmd_args(FLERR, "fix ave/chunk adof", error); adof = utils::numeric(FLERR,arg[iarg+1],false,lmp); iarg += 2; } else if (strcmp(arg[iarg],"cdof") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "fix ave/chunk cdof", error); + if (iarg+2 > nargnew) utils::missing_cmd_args(FLERR, "fix ave/chunk cdof", error); cdof = utils::numeric(FLERR,arg[iarg+1],false,lmp); iarg += 2; } else if ((strcmp(arg[iarg],"file") == 0) || (strcmp(arg[iarg],"append") == 0)) { - if (iarg+2 > narg) + if (iarg+2 > nargnew) utils::missing_cmd_args(FLERR, std::string("fix ave/chunk ")+arg[iarg], error); if (comm->me == 0) { if (strcmp(arg[iarg],"file") == 0) fp = fopen(arg[iarg+1],"w"); @@ -208,23 +208,23 @@ FixAveChunk::FixAveChunk(LAMMPS *lmp, int narg, char **arg) : overwrite = 1; iarg += 1; } else if (strcmp(arg[iarg],"format") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "fix ave/chunk format", error); + if (iarg+2 > nargnew) utils::missing_cmd_args(FLERR, "fix ave/chunk format", error); delete[] format_user; format_user = utils::strdup(arg[iarg+1]); format = format_user; iarg += 2; } else if (strcmp(arg[iarg],"title1") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "fix ave/chunk title1", error); + if (iarg+2 > nargnew) utils::missing_cmd_args(FLERR, "fix ave/chunk title1", error); delete[] title1; title1 = utils::strdup(arg[iarg+1]); iarg += 2; } else if (strcmp(arg[iarg],"title2") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "fix ave/chunk title2", error); + if (iarg+2 > nargnew) utils::missing_cmd_args(FLERR, "fix ave/chunk title2", error); delete[] title2; title2 = utils::strdup(arg[iarg+1]); iarg += 2; } else if (strcmp(arg[iarg],"title3") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "fix ave/chunk title3", error); + if (iarg+2 > nargnew) utils::missing_cmd_args(FLERR, "fix ave/chunk title3", error); delete[] title3; title3 = utils::strdup(arg[iarg+1]); iarg += 2; diff --git a/src/fix_ave_grid.cpp b/src/fix_ave_grid.cpp index ca89c918ba..1b69c5644c 100644 --- a/src/fix_ave_grid.cpp +++ b/src/fix_ave_grid.cpp @@ -199,14 +199,14 @@ FixAveGrid::FixAveGrid(LAMMPS *lmp, int narg, char **arg) : while (iarg < nargnew) { if (strcmp(arg[iarg],"discard") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal fix ave/grid command"); + if (iarg+2 > nargnew) error->all(FLERR,"Illegal fix ave/grid command"); if (strcmp(arg[iarg+1],"yes") == 0) discardflag = DISCARD; else if (strcmp(arg[iarg+1],"no") == 0) discardflag = KEEP; else error->all(FLERR,"Illegal fix ave/grid command"); iarg += 2; } else if (strcmp(arg[iarg],"norm") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal fix ave/grid command"); + if (iarg+2 > nargnew) error->all(FLERR,"Illegal fix ave/grid command"); if (strcmp(arg[iarg+1],"all") == 0) normflag = ALL; else if (strcmp(arg[iarg+1],"sample") == 0) normflag = SAMPLE; else if (strcmp(arg[iarg+1],"none") == 0) normflag = NONORM; @@ -214,13 +214,13 @@ FixAveGrid::FixAveGrid(LAMMPS *lmp, int narg, char **arg) : iarg += 2; } else if (strcmp(arg[iarg],"ave") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal fix ave/grid command"); + if (iarg+2 > nargnew) error->all(FLERR,"Illegal fix ave/grid command"); if (strcmp(arg[iarg+1],"one") == 0) aveflag = ONE; else if (strcmp(arg[iarg+1],"running") == 0) aveflag = RUNNING; else if (strcmp(arg[iarg+1],"window") == 0) aveflag = WINDOW; else error->all(FLERR,"Illegal fix ave/grid command"); if (aveflag == WINDOW) { - if (iarg+3 > narg) error->all(FLERR,"Illegal fix ave/grid command"); + if (iarg+3 > nargnew) error->all(FLERR,"Illegal fix ave/grid command"); nwindow = utils::inumeric(FLERR,arg[iarg+2],false,lmp); if (nwindow <= 0) error->all(FLERR,"Illegal fix ave/grid command"); iarg++; @@ -228,19 +228,19 @@ FixAveGrid::FixAveGrid(LAMMPS *lmp, int narg, char **arg) : iarg += 2; } else if (strcmp(arg[iarg],"bias") == 0) { - if (iarg+2 > narg) + if (iarg+2 > nargnew) error->all(FLERR,"Illegal fix ave/grid command"); biasflag = 1; id_bias = utils::strdup(arg[iarg+1]); iarg += 2; } else if (strcmp(arg[iarg],"adof") == 0) { - if (iarg+2 > narg) + if (iarg+2 > nargnew) error->all(FLERR,"Illegal fix ave/grid command"); adof = utils::numeric(FLERR,arg[iarg+1],false,lmp); iarg += 2; } else if (strcmp(arg[iarg],"cdof") == 0) { - if (iarg+2 > narg) + if (iarg+2 > nargnew) error->all(FLERR,"Illegal fix ave/grid command"); cdof = utils::numeric(FLERR,arg[iarg+1],false,lmp); iarg += 2; From 79fbd46bd9900d1e13fb6ab02a1f18dda7d91a01 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 14 Sep 2024 06:04:35 -0400 Subject: [PATCH 292/355] try out concurrency group feature on quick regression as suggested by @junghans --- .github/workflows/quick-regression.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index 985177b2c1..6174d57ec2 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -8,6 +8,10 @@ on: workflow_dispatch: +concurrency: + group: ${{ github.event_name }}-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{github.event_name == 'pull_request'}} + jobs: build: name: Build LAMMPS From 8c4e67c10baab6f15af120befd6a5a412c6f444f Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 14 Sep 2024 06:07:07 -0400 Subject: [PATCH 293/355] add concurrency group feature to unit test workflows, too. --- .github/workflows/compile-msvc.yml | 4 ++++ .github/workflows/unittest-linux.yml | 4 ++++ .github/workflows/unittest-macos.yml | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/.github/workflows/compile-msvc.yml b/.github/workflows/compile-msvc.yml index 03af27788b..7560bc0549 100644 --- a/.github/workflows/compile-msvc.yml +++ b/.github/workflows/compile-msvc.yml @@ -11,6 +11,10 @@ on: workflow_dispatch: +concurrency: + group: ${{ github.event_name }}-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{github.event_name == 'pull_request'}} + jobs: build: name: Windows Compilation Test diff --git a/.github/workflows/unittest-linux.yml b/.github/workflows/unittest-linux.yml index 366db25a99..dcf495ccc0 100644 --- a/.github/workflows/unittest-linux.yml +++ b/.github/workflows/unittest-linux.yml @@ -11,6 +11,10 @@ on: workflow_dispatch: +concurrency: + group: ${{ github.event_name }}-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{github.event_name == 'pull_request'}} + jobs: build: name: Linux Unit Test diff --git a/.github/workflows/unittest-macos.yml b/.github/workflows/unittest-macos.yml index b0bc4b2727..0d478a9d6b 100644 --- a/.github/workflows/unittest-macos.yml +++ b/.github/workflows/unittest-macos.yml @@ -11,6 +11,10 @@ on: workflow_dispatch: +concurrency: + group: ${{ github.event_name }}-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{github.event_name == 'pull_request'}} + jobs: build: name: MacOS Unit Test From 44d86c378b8778ccef40ff30bb063ef29042034e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 16 Sep 2024 00:34:28 -0400 Subject: [PATCH 294/355] relax epsilon or mark unstable based on test results on non-x86 architectures --- unittest/force-styles/tests/atomic-pair-meam_ms.yaml | 2 +- unittest/force-styles/tests/atomic-pair-pedone.yaml | 2 +- .../tests/dihedral-cosine_squared_restricted.yaml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/unittest/force-styles/tests/atomic-pair-meam_ms.yaml b/unittest/force-styles/tests/atomic-pair-meam_ms.yaml index fff938d940..d8dcd7b1eb 100644 --- a/unittest/force-styles/tests/atomic-pair-meam_ms.yaml +++ b/unittest/force-styles/tests/atomic-pair-meam_ms.yaml @@ -2,7 +2,7 @@ lammps_version: 7 Feb 2024 tags: slow date_generated: Wed Feb 28 17:07:42 2024 -epsilon: 2.5e-12 +epsilon: 2.5e-11 skip_tests: prerequisites: ! | pair meam/ms diff --git a/unittest/force-styles/tests/atomic-pair-pedone.yaml b/unittest/force-styles/tests/atomic-pair-pedone.yaml index ea97d9ee8c..82c6405e65 100644 --- a/unittest/force-styles/tests/atomic-pair-pedone.yaml +++ b/unittest/force-styles/tests/atomic-pair-pedone.yaml @@ -1,6 +1,6 @@ --- lammps_version: 7 Feb 2024 -tags: +tags: unstable date_generated: Tue Apr 9 07:44:34 2024 epsilon: 7.5e-13 skip_tests: diff --git a/unittest/force-styles/tests/dihedral-cosine_squared_restricted.yaml b/unittest/force-styles/tests/dihedral-cosine_squared_restricted.yaml index f67a093017..3f4d217b9a 100644 --- a/unittest/force-styles/tests/dihedral-cosine_squared_restricted.yaml +++ b/unittest/force-styles/tests/dihedral-cosine_squared_restricted.yaml @@ -1,8 +1,8 @@ --- lammps_version: 7 Feb 2024 -tags: +tags: date_generated: Sat Apr 13 11:41:16 2024 -epsilon: 2.5e-13 +epsilon: 1.0e-11 skip_tests: prerequisites: ! | atom full From f8eac8772458943b31618e36200b59b0af86306e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 16 Sep 2024 00:56:09 -0400 Subject: [PATCH 295/355] make programming style check and doc info check a github action --- .github/workflows/style-check.yml | 38 +++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/workflows/style-check.yml diff --git a/.github/workflows/style-check.yml b/.github/workflows/style-check.yml new file mode 100644 index 0000000000..ce07215b11 --- /dev/null +++ b/.github/workflows/style-check.yml @@ -0,0 +1,38 @@ +# GitHub action to run checks from tools/coding_standard +name: "Check for Programming Style Conformance" + +on: + push: + branches: + - develop + pull_request: + branches: + - develop + + workflow_dispatch: + +concurrency: + group: ${{ github.event_name }}-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{github.event_name == 'pull_request'}} + +jobs: + build: + name: Programming Style Conformance + if: ${{ github.repository == 'lammps/lammps' }} + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Run Tests + working-directory: src + shell: bash + run: | + make check-whitespace + make check-permissions + make check-homepage + make check-error-docs + make check-docs From 4a9b7b1ba71a5f90e07aaa813c333626d0ca110e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 16 Sep 2024 00:59:43 -0400 Subject: [PATCH 296/355] fix typo --- .github/workflows/style-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/style-check.yml b/.github/workflows/style-check.yml index ce07215b11..7be468d230 100644 --- a/.github/workflows/style-check.yml +++ b/.github/workflows/style-check.yml @@ -34,5 +34,5 @@ jobs: make check-whitespace make check-permissions make check-homepage - make check-error-docs + make check-errordocs make check-docs From 80cd9ace577a906b6ce8b19943c91576648b819f Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 16 Sep 2024 01:07:38 -0400 Subject: [PATCH 297/355] remove documentation check (for now) --- .github/workflows/style-check.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/style-check.yml b/.github/workflows/style-check.yml index 7be468d230..7be2c4fc46 100644 --- a/.github/workflows/style-check.yml +++ b/.github/workflows/style-check.yml @@ -35,4 +35,3 @@ jobs: make check-permissions make check-homepage make check-errordocs - make check-docs From 804aa4ee85495aec62d39de755626703d6882d24 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 16 Sep 2024 20:38:50 -0400 Subject: [PATCH 298/355] improve error message --- src/variable.cpp | 2 +- unittest/commands/test_variables.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/variable.cpp b/src/variable.cpp index b2f6c2882c..279c14d999 100644 --- a/src/variable.cpp +++ b/src/variable.cpp @@ -613,7 +613,7 @@ void Variable::set(int narg, char **arg) // unrecognized variable style - } else error->all(FLERR,"Unknown variable keyword: {}", arg[1]); + } else error->all(FLERR,"Unknown variable style: {}", arg[1]); // set name of variable, if not replacing one flagged with replaceflag // name must be all alphanumeric chars or underscores diff --git a/unittest/commands/test_variables.cpp b/unittest/commands/test_variables.cpp index 2390b1b675..c7686cbf12 100644 --- a/unittest/commands/test_variables.cpp +++ b/unittest/commands/test_variables.cpp @@ -206,7 +206,7 @@ TEST_F(VariableTest, CreateDelete) TEST_FAILURE(".*ERROR: Invalid variable loop argument: -1.*", command("variable dummy loop -1");); TEST_FAILURE(".*ERROR: Illegal variable loop command.*", command("variable dummy loop 10 1");); - TEST_FAILURE(".*ERROR: Unknown variable keyword: xxx.*", command("variable dummy xxxx");); + TEST_FAILURE(".*ERROR: Unknown variable style: xxx.*", command("variable dummy xxxx");); TEST_FAILURE(".*ERROR: Cannot redefine variable as a different style.*", command("variable two string xxx");); TEST_FAILURE(".*ERROR: Cannot redefine variable as a different style.*", From 11365e7b2e0c6e3920275a8b338b1fcd9444752c Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 16 Sep 2024 20:48:41 -0400 Subject: [PATCH 299/355] add extract method to bond style harmonic/shift --- src/EXTRA-MOLECULE/bond_harmonic_shift.cpp | 13 +++++++++++++ src/EXTRA-MOLECULE/bond_harmonic_shift.h | 1 + 2 files changed, 14 insertions(+) diff --git a/src/EXTRA-MOLECULE/bond_harmonic_shift.cpp b/src/EXTRA-MOLECULE/bond_harmonic_shift.cpp index bd106c8567..6c87d47f5e 100644 --- a/src/EXTRA-MOLECULE/bond_harmonic_shift.cpp +++ b/src/EXTRA-MOLECULE/bond_harmonic_shift.cpp @@ -228,3 +228,16 @@ void BondHarmonicShift::born_matrix(int type, double rsq, int /*i*/, int /*j*/, du2 = 2 * k[type]; if (r > 0.0) du = du2 * dr; } + +/* ---------------------------------------------------------------------- + return ptr to internal members upon request +------------------------------------------------------------------------ */ + +void *BondHarmonicShift::extract(const char *str, int &dim) +{ + dim = 1; + if (strcmp(str, "k") == 0) return (void *) k; + if (strcmp(str, "r0") == 0) return (void *) r0; + if (strcmp(str, "r1") == 0) return (void *) r1; + return nullptr; +} diff --git a/src/EXTRA-MOLECULE/bond_harmonic_shift.h b/src/EXTRA-MOLECULE/bond_harmonic_shift.h index 922f1ba00d..68afc57bf7 100644 --- a/src/EXTRA-MOLECULE/bond_harmonic_shift.h +++ b/src/EXTRA-MOLECULE/bond_harmonic_shift.h @@ -36,6 +36,7 @@ class BondHarmonicShift : public Bond { void write_data(FILE *) override; double single(int, double, int, int, double &) override; void born_matrix(int, double, int, int, double &, double &) override; + void *extract(const char *, int &); protected: double *k, *r0, *r1; From 5d40a9640d47ed7ffb75a59988552d1eab3ef7df Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 16 Sep 2024 20:49:44 -0400 Subject: [PATCH 300/355] add support for bond style hybrid to fix adapt --- src/bond_hybrid.cpp | 8 ++++++++ src/bond_hybrid.h | 2 ++ src/fix_adapt.cpp | 27 ++++++++++++++++++++------- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/bond_hybrid.cpp b/src/bond_hybrid.cpp index 307cbd72fd..bd5badb54c 100644 --- a/src/bond_hybrid.cpp +++ b/src/bond_hybrid.cpp @@ -385,6 +385,14 @@ void BondHybrid::init_style() else map[0] = -1; } +/* ---------------------------------------------------------------------- */ + +int BondHybrid::check_itype(int itype, char *substyle) +{ + if (strcmp(keywords[map[itype]], substyle) == 0) return 1; + return 0; +} + /* ---------------------------------------------------------------------- return an equilbrium bond length ------------------------------------------------------------------------- */ diff --git a/src/bond_hybrid.h b/src/bond_hybrid.h index ba520b81b4..d93b5c7558 100644 --- a/src/bond_hybrid.h +++ b/src/bond_hybrid.h @@ -44,6 +44,8 @@ class BondHybrid : public Bond { double single(int, double, int, int, double &) override; double memory_usage() override; + int check_itype(int, char *); + protected: int *map; // which style each bond type points to int has_quartic; // which style, if any is a quartic bond style diff --git a/src/fix_adapt.cpp b/src/fix_adapt.cpp index cad157f2be..6bab704c22 100644 --- a/src/fix_adapt.cpp +++ b/src/fix_adapt.cpp @@ -17,6 +17,7 @@ #include "angle.h" #include "atom.h" #include "bond.h" +#include "bond_hybrid.h" #include "domain.h" #include "error.h" #include "fix_store_atom.h" @@ -386,11 +387,15 @@ void FixAdapt::init() if (utils::strmatch(force->pair_style,"^hybrid")) { auto pair = dynamic_cast(force->pair); - for (i = ad->ilo; i <= ad->ihi; i++) - for (j = MAX(ad->jlo,i); j <= ad->jhi; j++) - if (!pair->check_ijtype(i,j,pstyle)) - error->all(FLERR,"Fix adapt type pair range is not valid " - "for pair hybrid sub-style {}", pstyle); + if (pair) { + for (i = ad->ilo; i <= ad->ihi; i++) { + for (j = MAX(ad->jlo,i); j <= ad->jhi; j++) { + if (!pair->check_ijtype(i,j,pstyle)) + error->all(FLERR,"Fix adapt type pair range is not valid " + "for pair hybrid sub-style {}", pstyle); + } + } + } } delete[] pstyle; @@ -416,8 +421,16 @@ void FixAdapt::init() if (ad->bdim == 1) ad->vector = (double *) ptr; - if (utils::strmatch(force->bond_style,"^hybrid")) - error->all(FLERR,"Fix adapt does not support bond_style hybrid"); + if (utils::strmatch(force->bond_style,"^hybrid")) { + auto bond = dynamic_cast(force->bond); + if (bond) { + for (i = ad->ilo; i <= ad->ihi; i++) { + if (!bond->check_itype(i,bstyle)) + error->all(FLERR,"Fix adapt type bond range is not valid " + "for pair hybrid sub-style {}", bstyle); + } + } + } delete[] bstyle; From 516d988debec1aed9aceb386756bc93f00ffedfe Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 16 Sep 2024 20:59:04 -0400 Subject: [PATCH 301/355] update fix adapt docs --- doc/src/fix_adapt.rst | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/doc/src/fix_adapt.rst b/doc/src/fix_adapt.rst index 03aef12a6c..0aa0323309 100644 --- a/doc/src/fix_adapt.rst +++ b/doc/src/fix_adapt.rst @@ -319,25 +319,28 @@ all types from 1 to :math:`N`. A leading asterisk means all types from :math:`N` (inclusive). A middle asterisk means all types from m to n (inclusive). -Currently *bond* does not support bond_style hybrid nor bond_style -hybrid/overlay as bond styles. The bond styles that currently work -with fix_adapt are +If :doc:`bond_style hybrid ` is used, *bstyle* should be a +sub-style name. The bond styles that currently work with fix adapt are: -+------------------------------------+------------+------------+ -| :doc:`class2 ` | r0 | type bonds | -+------------------------------------+------------+------------+ -| :doc:`fene ` | k,r0 | type bonds | -+------------------------------------+------------+------------+ -| :doc:`fene/nm ` | k,r0 | type bonds | -+------------------------------------+------------+------------+ -| :doc:`gromos ` | k,r0 | type bonds | -+------------------------------------+------------+------------+ -| :doc:`harmonic ` | k,r0 | type bonds | -+------------------------------------+------------+------------+ -| :doc:`morse ` | r0 | type bonds | -+------------------------------------+------------+------------+ -| :doc:`nonlinear ` | epsilon,r0 | type bonds | -+------------------------------------+------------+------------+ ++---------------------------------------------------+------------+------------+ +| :doc:`class2 ` | r0 | type bonds | ++---------------------------------------------------+------------+------------+ +| :doc:`fene ` | k,r0 | type bonds | ++---------------------------------------------------+------------+------------+ +| :doc:`fene/nm ` | k,r0 | type bonds | ++---------------------------------------------------+------------+------------+ +| :doc:`gromos ` | k,r0 | type bonds | ++---------------------------------------------------+------------+------------+ +| :doc:`harmonic ` | k,r0 | type bonds | ++---------------------------------------------------+------------+------------+ +| :doc:`harmonic/shift ` | k,r0,r1 | type bonds | ++---------------------------------------------------+------------+------------+ +| :doc:`harmonic/restrain ` | k | type bonds | ++---------------------------------------------------+------------+------------+ +| :doc:`morse ` | r0 | type bonds | ++---------------------------------------------------+------------+------------+ +| :doc:`nonlinear ` | epsilon,r0 | type bonds | ++---------------------------------------------------+------------+------------+ ---------- @@ -359,7 +362,7 @@ all types from 1 to :math:`N`. A leading asterisk means all types from Currently *angle* does not support angle_style hybrid nor angle_style hybrid/overlay as angle styles. The angle styles that currently work -with fix_adapt are +with fix adapt are +------------------------------------+----------+-------------+ | :doc:`harmonic ` | k,theta0 | type angles | From 47bb1a8748ee5729f0c45a3ab7098d99d2507cf7 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 16 Sep 2024 21:04:22 -0400 Subject: [PATCH 302/355] update unit test --- unittest/force-styles/tests/bond-harmonic_shift.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/unittest/force-styles/tests/bond-harmonic_shift.yaml b/unittest/force-styles/tests/bond-harmonic_shift.yaml index 7a41c2c3cd..61212a468b 100644 --- a/unittest/force-styles/tests/bond-harmonic_shift.yaml +++ b/unittest/force-styles/tests/bond-harmonic_shift.yaml @@ -17,7 +17,10 @@ bond_coeff: ! | 4 650.0 1.2 0.2 5 450.0 1.0 0.0 equilibrium: 5 1.5 1.1 1.3 1.2 1 -extract: ! "" +extract: ! | + k 1 + r0 1 + r1 1 natoms: 29 init_energy: -9395.519982389222 init_stress: ! |- From 6503f297d47a2a6f2ecbd227364cf89456969d3b Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 16 Sep 2024 21:21:20 -0400 Subject: [PATCH 303/355] support angle style hybrid with fix adapt --- doc/src/fix_adapt.rst | 5 ++--- src/angle_hybrid.cpp | 8 ++++++++ src/angle_hybrid.h | 4 +++- src/fix_adapt.cpp | 13 +++++++++++-- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/doc/src/fix_adapt.rst b/doc/src/fix_adapt.rst index 0aa0323309..1943798160 100644 --- a/doc/src/fix_adapt.rst +++ b/doc/src/fix_adapt.rst @@ -360,9 +360,8 @@ all types from 1 to :math:`N`. A leading asterisk means all types from :math:`N` (inclusive). A middle asterisk means all types from m to n (inclusive). -Currently *angle* does not support angle_style hybrid nor angle_style -hybrid/overlay as angle styles. The angle styles that currently work -with fix adapt are +If :doc:`angle_style hybrid ` is used, *astyle* should be a +sub-style name. The angle styles that currently work with fix adapt are: +------------------------------------+----------+-------------+ | :doc:`harmonic ` | k,theta0 | type angles | diff --git a/src/angle_hybrid.cpp b/src/angle_hybrid.cpp index a015882a15..1261a78176 100644 --- a/src/angle_hybrid.cpp +++ b/src/angle_hybrid.cpp @@ -320,6 +320,14 @@ void AngleHybrid::init_style() if (styles[m]) styles[m]->init_style(); } +/* ---------------------------------------------------------------------- */ + +int AngleHybrid::check_itype(int itype, char *substyle) +{ + if (strcmp(keywords[map[itype]], substyle) == 0) return 1; + return 0; +} + /* ---------------------------------------------------------------------- return an equilbrium angle length ------------------------------------------------------------------------- */ diff --git a/src/angle_hybrid.h b/src/angle_hybrid.h index a6da29245e..a84096b297 100644 --- a/src/angle_hybrid.h +++ b/src/angle_hybrid.h @@ -42,8 +42,10 @@ class AngleHybrid : public Angle { double single(int, int, int, int) override; double memory_usage() override; + int check_itype(int, char *); + protected: - int *map; // which style each angle type points to + int *map; // which style each angle type points to int *nanglelist; // # of angles in sub-style anglelists int *maxangle; // max # of angles sub-style lists can store int ***anglelist; // anglelist for each sub-style diff --git a/src/fix_adapt.cpp b/src/fix_adapt.cpp index 6bab704c22..c725707b29 100644 --- a/src/fix_adapt.cpp +++ b/src/fix_adapt.cpp @@ -15,6 +15,7 @@ #include "fix_adapt.h" #include "angle.h" +#include "angle_hybrid.h" #include "atom.h" #include "bond.h" #include "bond_hybrid.h" @@ -455,8 +456,16 @@ void FixAdapt::init() if (ad->adim == 1) ad->vector = (double *) ptr; - if (utils::strmatch(force->angle_style,"^hybrid")) - error->all(FLERR,"Fix adapt does not support angle_style hybrid"); + if (utils::strmatch(force->angle_style,"^hybrid")) { + auto angle = dynamic_cast(force->angle); + if (angle) { + for (i = ad->ilo; i <= ad->ihi; i++) { + if (!angle->check_itype(i,astyle)) + error->all(FLERR,"Fix adapt type angle range is not valid " + "for pair hybrid sub-style {}", astyle); + } + } + } delete[] astyle; From 9f867b5f54476a984737de02e53caf100b6eabfa Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 18 Sep 2024 19:13:37 -0400 Subject: [PATCH 304/355] add LAPACK functions for matrix inversion via Cholesky decomposition --- lib/linalg/dlauu2.cpp | 77 ++++++++++++++++++++++++++++++++ lib/linalg/dlauum.cpp | 101 ++++++++++++++++++++++++++++++++++++++++++ lib/linalg/dpotri.cpp | 40 +++++++++++++++++ 3 files changed, 218 insertions(+) create mode 100644 lib/linalg/dlauu2.cpp create mode 100644 lib/linalg/dlauum.cpp create mode 100644 lib/linalg/dpotri.cpp diff --git a/lib/linalg/dlauu2.cpp b/lib/linalg/dlauu2.cpp new file mode 100644 index 0000000000..d90a84798d --- /dev/null +++ b/lib/linalg/dlauu2.cpp @@ -0,0 +1,77 @@ +#ifdef __cplusplus +extern "C" { +#endif +#include "lmp_f2c.h" +static doublereal c_b7 = 1.; +static integer c__1 = 1; +int dlauu2_(char *uplo, integer *n, doublereal *a, integer *lda, integer *info, ftnlen uplo_len) +{ + integer a_dim1, a_offset, i__1, i__2, i__3; + integer i__; + doublereal aii; + extern doublereal ddot_(integer *, doublereal *, integer *, doublereal *, integer *); + extern int dscal_(integer *, doublereal *, doublereal *, integer *); + extern logical lsame_(char *, char *, ftnlen, ftnlen); + extern int dgemv_(char *, integer *, integer *, doublereal *, doublereal *, integer *, + doublereal *, integer *, doublereal *, doublereal *, integer *, ftnlen); + logical upper; + extern int xerbla_(char *, integer *, ftnlen); + a_dim1 = *lda; + a_offset = 1 + a_dim1; + a -= a_offset; + *info = 0; + upper = lsame_(uplo, (char *)"U", (ftnlen)1, (ftnlen)1); + if (!upper && !lsame_(uplo, (char *)"L", (ftnlen)1, (ftnlen)1)) { + *info = -1; + } else if (*n < 0) { + *info = -2; + } else if (*lda < max(1, *n)) { + *info = -4; + } + if (*info != 0) { + i__1 = -(*info); + xerbla_((char *)"DLAUU2", &i__1, (ftnlen)6); + return 0; + } + if (*n == 0) { + return 0; + } + if (upper) { + i__1 = *n; + for (i__ = 1; i__ <= i__1; ++i__) { + aii = a[i__ + i__ * a_dim1]; + if (i__ < *n) { + i__2 = *n - i__ + 1; + a[i__ + i__ * a_dim1] = + ddot_(&i__2, &a[i__ + i__ * a_dim1], lda, &a[i__ + i__ * a_dim1], lda); + i__2 = i__ - 1; + i__3 = *n - i__; + dgemv_((char *)"No transpose", &i__2, &i__3, &c_b7, &a[(i__ + 1) * a_dim1 + 1], lda, + &a[i__ + (i__ + 1) * a_dim1], lda, &aii, &a[i__ * a_dim1 + 1], &c__1, + (ftnlen)12); + } else { + dscal_(&i__, &aii, &a[i__ * a_dim1 + 1], &c__1); + } + } + } else { + i__1 = *n; + for (i__ = 1; i__ <= i__1; ++i__) { + aii = a[i__ + i__ * a_dim1]; + if (i__ < *n) { + i__2 = *n - i__ + 1; + a[i__ + i__ * a_dim1] = + ddot_(&i__2, &a[i__ + i__ * a_dim1], &c__1, &a[i__ + i__ * a_dim1], &c__1); + i__2 = *n - i__; + i__3 = i__ - 1; + dgemv_((char *)"Transpose", &i__2, &i__3, &c_b7, &a[i__ + 1 + a_dim1], lda, + &a[i__ + 1 + i__ * a_dim1], &c__1, &aii, &a[i__ + a_dim1], lda, (ftnlen)9); + } else { + dscal_(&i__, &aii, &a[i__ + a_dim1], lda); + } + } + } + return 0; +} +#ifdef __cplusplus +} +#endif diff --git a/lib/linalg/dlauum.cpp b/lib/linalg/dlauum.cpp new file mode 100644 index 0000000000..632bd4ba85 --- /dev/null +++ b/lib/linalg/dlauum.cpp @@ -0,0 +1,101 @@ +#ifdef __cplusplus +extern "C" { +#endif +#include "lmp_f2c.h" +static integer c__1 = 1; +static integer c_n1 = -1; +static doublereal c_b15 = 1.; +int dlauum_(char *uplo, integer *n, doublereal *a, integer *lda, integer *info, ftnlen uplo_len) +{ + integer a_dim1, a_offset, i__1, i__2, i__3, i__4; + integer i__, ib, nb; + extern int dgemm_(char *, char *, integer *, integer *, integer *, doublereal *, doublereal *, + integer *, doublereal *, integer *, doublereal *, doublereal *, integer *, + ftnlen, ftnlen); + extern logical lsame_(char *, char *, ftnlen, ftnlen); + extern int dtrmm_(char *, char *, char *, char *, integer *, integer *, doublereal *, + doublereal *, integer *, doublereal *, integer *, ftnlen, ftnlen, ftnlen, + ftnlen); + logical upper; + extern int dsyrk_(char *, char *, integer *, integer *, doublereal *, doublereal *, integer *, + doublereal *, doublereal *, integer *, ftnlen, ftnlen), + dlauu2_(char *, integer *, doublereal *, integer *, integer *, ftnlen), + xerbla_(char *, integer *, ftnlen); + extern integer ilaenv_(integer *, char *, char *, integer *, integer *, integer *, integer *, + ftnlen, ftnlen); + a_dim1 = *lda; + a_offset = 1 + a_dim1; + a -= a_offset; + *info = 0; + upper = lsame_(uplo, (char *)"U", (ftnlen)1, (ftnlen)1); + if (!upper && !lsame_(uplo, (char *)"L", (ftnlen)1, (ftnlen)1)) { + *info = -1; + } else if (*n < 0) { + *info = -2; + } else if (*lda < max(1, *n)) { + *info = -4; + } + if (*info != 0) { + i__1 = -(*info); + xerbla_((char *)"DLAUUM", &i__1, (ftnlen)6); + return 0; + } + if (*n == 0) { + return 0; + } + nb = ilaenv_(&c__1, (char *)"DLAUUM", uplo, n, &c_n1, &c_n1, &c_n1, (ftnlen)6, (ftnlen)1); + if (nb <= 1 || nb >= *n) { + dlauu2_(uplo, n, &a[a_offset], lda, info, (ftnlen)1); + } else { + if (upper) { + i__1 = *n; + i__2 = nb; + for (i__ = 1; i__2 < 0 ? i__ >= i__1 : i__ <= i__1; i__ += i__2) { + i__3 = nb, i__4 = *n - i__ + 1; + ib = min(i__3, i__4); + i__3 = i__ - 1; + dtrmm_((char *)"Right", (char *)"Upper", (char *)"Transpose", (char *)"Non-unit", &i__3, &ib, &c_b15, + &a[i__ + i__ * a_dim1], lda, &a[i__ * a_dim1 + 1], lda, (ftnlen)5, (ftnlen)5, + (ftnlen)9, (ftnlen)8); + dlauu2_((char *)"Upper", &ib, &a[i__ + i__ * a_dim1], lda, info, (ftnlen)5); + if (i__ + ib <= *n) { + i__3 = i__ - 1; + i__4 = *n - i__ - ib + 1; + dgemm_((char *)"No transpose", (char *)"Transpose", &i__3, &ib, &i__4, &c_b15, + &a[(i__ + ib) * a_dim1 + 1], lda, &a[i__ + (i__ + ib) * a_dim1], lda, + &c_b15, &a[i__ * a_dim1 + 1], lda, (ftnlen)12, (ftnlen)9); + i__3 = *n - i__ - ib + 1; + dsyrk_((char *)"Upper", (char *)"No transpose", &ib, &i__3, &c_b15, + &a[i__ + (i__ + ib) * a_dim1], lda, &c_b15, &a[i__ + i__ * a_dim1], lda, + (ftnlen)5, (ftnlen)12); + } + } + } else { + i__2 = *n; + i__1 = nb; + for (i__ = 1; i__1 < 0 ? i__ >= i__2 : i__ <= i__2; i__ += i__1) { + i__3 = nb, i__4 = *n - i__ + 1; + ib = min(i__3, i__4); + i__3 = i__ - 1; + dtrmm_((char *)"Left", (char *)"Lower", (char *)"Transpose", (char *)"Non-unit", &ib, &i__3, &c_b15, + &a[i__ + i__ * a_dim1], lda, &a[i__ + a_dim1], lda, (ftnlen)4, (ftnlen)5, + (ftnlen)9, (ftnlen)8); + dlauu2_((char *)"Lower", &ib, &a[i__ + i__ * a_dim1], lda, info, (ftnlen)5); + if (i__ + ib <= *n) { + i__3 = i__ - 1; + i__4 = *n - i__ - ib + 1; + dgemm_((char *)"Transpose", (char *)"No transpose", &ib, &i__3, &i__4, &c_b15, + &a[i__ + ib + i__ * a_dim1], lda, &a[i__ + ib + a_dim1], lda, &c_b15, + &a[i__ + a_dim1], lda, (ftnlen)9, (ftnlen)12); + i__3 = *n - i__ - ib + 1; + dsyrk_((char *)"Lower", (char *)"Transpose", &ib, &i__3, &c_b15, &a[i__ + ib + i__ * a_dim1], + lda, &c_b15, &a[i__ + i__ * a_dim1], lda, (ftnlen)5, (ftnlen)9); + } + } + } + } + return 0; +} +#ifdef __cplusplus +} +#endif diff --git a/lib/linalg/dpotri.cpp b/lib/linalg/dpotri.cpp new file mode 100644 index 0000000000..9c0a609e1b --- /dev/null +++ b/lib/linalg/dpotri.cpp @@ -0,0 +1,40 @@ +#ifdef __cplusplus +extern "C" { +#endif +#include "lmp_f2c.h" +int dpotri_(char *uplo, integer *n, doublereal *a, integer *lda, integer *info, ftnlen uplo_len) +{ + integer a_dim1, a_offset, i__1; + extern logical lsame_(char *, char *, ftnlen, ftnlen); + extern int xerbla_(char *, integer *, ftnlen), + dlauum_(char *, integer *, doublereal *, integer *, integer *, ftnlen), + dtrtri_(char *, char *, integer *, doublereal *, integer *, integer *, ftnlen, ftnlen); + a_dim1 = *lda; + a_offset = 1 + a_dim1; + a -= a_offset; + *info = 0; + if (!lsame_(uplo, (char *)"U", (ftnlen)1, (ftnlen)1) && !lsame_(uplo, (char *)"L", (ftnlen)1, (ftnlen)1)) { + *info = -1; + } else if (*n < 0) { + *info = -2; + } else if (*lda < max(1, *n)) { + *info = -4; + } + if (*info != 0) { + i__1 = -(*info); + xerbla_((char *)"DPOTRI", &i__1, (ftnlen)6); + return 0; + } + if (*n == 0) { + return 0; + } + dtrtri_(uplo, (char *)"Non-unit", n, &a[a_offset], lda, info, (ftnlen)1, (ftnlen)8); + if (*info > 0) { + return 0; + } + dlauum_(uplo, n, &a[a_offset], lda, info, (ftnlen)1); + return 0; +} +#ifdef __cplusplus +} +#endif From b16b683cb4a0de14116cbc09525e2b701ef4ba51 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 18 Sep 2024 19:26:00 -0400 Subject: [PATCH 305/355] replace calls to GSL with calls to LAPACK --- src/RHEO/compute_rheo_kernel.cpp | 74 +++++++++++++++++++------------- src/RHEO/compute_rheo_kernel.h | 4 +- 2 files changed, 46 insertions(+), 32 deletions(-) diff --git a/src/RHEO/compute_rheo_kernel.cpp b/src/RHEO/compute_rheo_kernel.cpp index dd05901b32..329e955a32 100644 --- a/src/RHEO/compute_rheo_kernel.cpp +++ b/src/RHEO/compute_rheo_kernel.cpp @@ -37,10 +37,6 @@ #include "utils.h" #include -#include -#include -#include -#include using namespace LAMMPS_NS; using namespace RHEO_NS; @@ -50,6 +46,13 @@ using namespace MathExtra; // max value of Mdim 1 + dim + dim * (dim + 1) / 2 with dim = 3 static constexpr int MAX_MDIM = 12; +// declare LAPACK functions + +extern "C" { + void dpotrf2_(const char *uplo, const int *n, double *a, const int *lda, int *info); + void dpotri_(const char *uplo, const int *n, double *a, const int *lda, int *info); +} + /* ---------------------------------------------------------------------- */ ComputeRHEOKernel::ComputeRHEOKernel(LAMMPS *lmp, int narg, char **arg) : @@ -89,7 +92,7 @@ ComputeRHEOKernel::ComputeRHEOKernel(LAMMPS *lmp, int narg, char **arg) : comm_forward_save = comm_forward; corrections_calculated = 0; - gsl_error_flag = 0; + lapack_error_flag = 0; } /* ---------------------------------------------------------------------- */ @@ -156,9 +159,9 @@ void ComputeRHEOKernel::init_list(int /*id*/, NeighList *ptr) int ComputeRHEOKernel::check_corrections(int i) { - // Skip if there were gsl errors for this atom - if (gsl_error_flag) - if (gsl_error_tags.find(atom->tag[i]) != gsl_error_tags.end()) return 0; + // Skip if there were lapack errors for this atom + if (lapack_error_flag) + if (lapack_error_tags.find(atom->tag[i]) != lapack_error_tags.end()) return 0; // Skip if undercoordinated if (coordination[i] < zmin) return 0; @@ -558,19 +561,15 @@ void ComputeRHEOKernel::calc_dw_rk2(int i, double delx, double dely, double delz void ComputeRHEOKernel::compute_peratom() { - gsl_error_flag = 0; - gsl_error_tags.clear(); + lapack_error_flag = 0; + lapack_error_tags.clear(); if (kernel_style == QUINTIC) return; corrections_calculated = 1; - int i, j, ii, jj, inum, jnum, a, b, gsl_error; + int i, j, ii, jj, inum, jnum, a, b, lapack_error; double xtmp, ytmp, ztmp, r, rsq, w, vj, rhoj; double dx[3]; - gsl_matrix_view gM; - - // Turn off GSL error handler, revert RK to Quintic when insufficient neighbors - gsl_set_error_handler_off(); double **x = atom->x; int *type = atom->type; @@ -633,7 +632,7 @@ void ComputeRHEOKernel::compute_peratom() } } else if (correction_order > 0) { - // Moment matrix M and polynomial basis vector cut (1d for gsl compatibility) + // Moment matrix M and polynomial basis vector cut (1d for LAPACK compatibility) double H[MAX_MDIM], M[MAX_MDIM * MAX_MDIM]; for (ii = 0; ii < inum; ii++) { @@ -647,7 +646,9 @@ void ComputeRHEOKernel::compute_peratom() // Zero upper-triangle M and cut (will be symmetric): for (a = 0; a < Mdim; a++) { - for (b = a; b < Mdim; b++) { M[a * Mdim + b] = 0; } + for (b = a; b < Mdim; b++) { + M[a * Mdim + b] = 0; + } } for (jj = 0; jj < jnum; jj++) { @@ -700,37 +701,50 @@ void ComputeRHEOKernel::compute_peratom() // Populate the upper triangle for (a = 0; a < Mdim; a++) { - for (b = a; b < Mdim; b++) { M[a * Mdim + b] += H[a] * H[b] * w * vj; } + for (b = a; b < Mdim; b++) { + M[a * Mdim + b] += H[a] * H[b] * w * vj; + } } } } // Populate the lower triangle from the symmetric entries of M: for (a = 0; a < Mdim; a++) { - for (b = a; b < Mdim; b++) { M[b * Mdim + a] = M[a * Mdim + b]; } + for (b = a; b < Mdim; b++) { + M[b * Mdim + a] = M[a * Mdim + b]; + } } // Skip if undercoordinated if (coordination[i] < zmin) continue; - // Use gsl to get Minv, use Cholesky decomposition since the + // Use LAPACK to get Minv, use Cholesky decomposition since the // polynomials are independent, M is symmetrix & positive-definite - gM = gsl_matrix_view_array(M, Mdim, Mdim); - gsl_error = gsl_linalg_cholesky_decomp1(&gM.matrix); + const char uplo = 'U'; + dpotrf2_(&uplo, &Mdim, M, &Mdim, &lapack_error); - if (gsl_error) { - //Revert to uncorrected SPH for this particle - gsl_error_flag = 1; - gsl_error_tags.insert(tag[i]); + if (lapack_error) { + // Revert to uncorrected SPH for this particle + lapack_error_flag = 1; + lapack_error_tags.insert(tag[i]); - //check if not positive-definite - if (gsl_error != GSL_EDOM) - error->warning(FLERR, "Failed decomposition in rheo/kernel, gsl_error = {}", gsl_error); + // check if not positive-definite + if (lapack_error > 0) + error->warning(FLERR, "Failed DPOTRF2 decomposition in rheo/kernel, info = {}", + lapack_error); continue; } - gsl_linalg_cholesky_invert(&gM.matrix); //M is now M^-1 + // M is now M^-1 + dpotri_(&uplo, &Mdim, M, &Mdim, &lapack_error); + + // make result matrix symmetric + for (int i = 0; i < Mdim; ++i) { + for (int j = i+1; j < Mdim; ++j) { + M[i * Mdim + j] = M[j * Mdim + i]; + } + } // Correction coefficients are columns of M^-1 multiplied by an appropriate coefficient // Solve the linear system several times to get coefficientns diff --git a/src/RHEO/compute_rheo_kernel.h b/src/RHEO/compute_rheo_kernel.h index 20516255be..8b70509e6a 100644 --- a/src/RHEO/compute_rheo_kernel.h +++ b/src/RHEO/compute_rheo_kernel.h @@ -53,8 +53,8 @@ class ComputeRHEOKernel : public Compute { private: int comm_stage, comm_forward_save; int interface_flag; - int gsl_error_flag; - std::unordered_set gsl_error_tags; + int lapack_error_flag; + std::unordered_set lapack_error_tags; int corrections_calculated; int kernel_style, zmin, dim, Mdim, ncor; From 1ac9f0801cf386a9433999a8c991e818cd868451 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 18 Sep 2024 19:55:59 -0400 Subject: [PATCH 306/355] update build support for using LAPACK with RHEO instead of GSL --- cmake/CMakeLists.txt | 4 ++-- cmake/Modules/Packages/RHEO.cmake | 2 -- cmake/presets/mingw-cross.cmake | 1 + cmake/presets/most.cmake | 1 + cmake/presets/windows.cmake | 1 + lib/rheo/Makefile.lammps | 17 ++++------------- lib/rheo/Makefile.lammps.empty | 5 +++++ lib/rheo/Makefile.lammps.installed | 5 +++++ lib/rheo/Makefile.lammps.linalg | 5 +++++ lib/rheo/README | 12 +++++------- 10 files changed, 29 insertions(+), 24 deletions(-) delete mode 100644 cmake/Modules/Packages/RHEO.cmake create mode 100644 lib/rheo/Makefile.lammps.empty create mode 100644 lib/rheo/Makefile.lammps.installed create mode 100644 lib/rheo/Makefile.lammps.linalg diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index c68a925324..1bd387b5b9 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -497,7 +497,7 @@ if((CMAKE_CXX_COMPILER_ID STREQUAL "Intel") AND (CMAKE_CXX_STANDARD GREATER_EQUA PROPERTIES COMPILE_OPTIONS "-std=c++14") endif() -if(PKG_ATC OR PKG_AWPMD OR PKG_ML-QUIP OR PKG_ML-POD OR PKG_ELECTRODE OR BUILD_TOOLS) +if(PKG_ATC OR PKG_AWPMD OR PKG_ML-QUIP OR PKG_ML-POD OR PKG_ELECTRODE OR PKG_RHEO OR BUILD_TOOLS) enable_language(C) if (NOT USE_INTERNAL_LINALG) find_package(LAPACK) @@ -572,7 +572,7 @@ else() endif() foreach(PKG_WITH_INCL KSPACE PYTHON ML-IAP VORONOI COLVARS ML-HDNNP MDI MOLFILE NETCDF - PLUMED QMMM ML-QUIP SCAFACOS MACHDYN VTK KIM COMPRESS ML-PACE LEPTON RHEO EXTRA-COMMAND) + PLUMED QMMM ML-QUIP SCAFACOS MACHDYN VTK KIM COMPRESS ML-PACE LEPTON EXTRA-COMMAND) if(PKG_${PKG_WITH_INCL}) include(Packages/${PKG_WITH_INCL}) endif() diff --git a/cmake/Modules/Packages/RHEO.cmake b/cmake/Modules/Packages/RHEO.cmake deleted file mode 100644 index 7639acd8bc..0000000000 --- a/cmake/Modules/Packages/RHEO.cmake +++ /dev/null @@ -1,2 +0,0 @@ -find_package(GSL 2.6 REQUIRED) -target_link_libraries(lammps PRIVATE GSL::gsl) diff --git a/cmake/presets/mingw-cross.cmake b/cmake/presets/mingw-cross.cmake index 100ce13632..413744b078 100644 --- a/cmake/presets/mingw-cross.cmake +++ b/cmake/presets/mingw-cross.cmake @@ -67,6 +67,7 @@ set(WIN_PACKAGES REACTION REAXFF REPLICA + RHEO RIGID SHOCK SMTBQ diff --git a/cmake/presets/most.cmake b/cmake/presets/most.cmake index d01642f94d..05282eebdd 100644 --- a/cmake/presets/most.cmake +++ b/cmake/presets/most.cmake @@ -60,6 +60,7 @@ set(ALL_PACKAGES REACTION REAXFF REPLICA + RHEO RIGID SHOCK SPH diff --git a/cmake/presets/windows.cmake b/cmake/presets/windows.cmake index 403d40efa4..71241c559c 100644 --- a/cmake/presets/windows.cmake +++ b/cmake/presets/windows.cmake @@ -60,6 +60,7 @@ set(WIN_PACKAGES REACTION REAXFF REPLICA + RHEO RIGID SHOCK SMTBQ diff --git a/lib/rheo/Makefile.lammps b/lib/rheo/Makefile.lammps index ec58740370..5785f8978b 100644 --- a/lib/rheo/Makefile.lammps +++ b/lib/rheo/Makefile.lammps @@ -1,14 +1,5 @@ -# Settings that the LAMMPS build will import when this package is installed +# Settings that the LAMMPS build will import when this package library is used -ifeq ($(strip $(shell pkg-config --version)),) - # manual configuration w/o pkg-config/pkgconf - # change this to -I/path/to/your/lib/gsl/include/ - rheo_SYSINC = -I../../lib/rheo/gsl/include/ - - # change this to -L/path/to/your/lib/gsl/lib/ - rheo_SYSLIB = -L../../lib/rheo/gsl/lib/ -lgsl -lgslcblas -else - # autodetect GSL settings from pkg-config/pkgconf - rheo_SYSINC = $(shell pkg-config --cflags gsl) - rheo_SYSLIB = $(shell pkg-config --libs gsl) -endif +rheo_SYSINC = +rheo_SYSLIB = -llinalg +rheo_SYSPATH = -L../../lib/linalg$(LIBOBJDIR) diff --git a/lib/rheo/Makefile.lammps.empty b/lib/rheo/Makefile.lammps.empty new file mode 100644 index 0000000000..f71390299c --- /dev/null +++ b/lib/rheo/Makefile.lammps.empty @@ -0,0 +1,5 @@ +# Settings that the LAMMPS build will import when this package library is used + +rheo_SYSINC = +rheo_SYSLIB = +rheo_SYSPATH = diff --git a/lib/rheo/Makefile.lammps.installed b/lib/rheo/Makefile.lammps.installed new file mode 100644 index 0000000000..8900470077 --- /dev/null +++ b/lib/rheo/Makefile.lammps.installed @@ -0,0 +1,5 @@ +# Settings that the LAMMPS build will import when this package library is used + +rheo_SYSINC = +rheo_SYSLIB = -lblas -llapack +rheo_SYSPATH = diff --git a/lib/rheo/Makefile.lammps.linalg b/lib/rheo/Makefile.lammps.linalg new file mode 100644 index 0000000000..5785f8978b --- /dev/null +++ b/lib/rheo/Makefile.lammps.linalg @@ -0,0 +1,5 @@ +# Settings that the LAMMPS build will import when this package library is used + +rheo_SYSINC = +rheo_SYSLIB = -llinalg +rheo_SYSPATH = -L../../lib/linalg$(LIBOBJDIR) diff --git a/lib/rheo/README b/lib/rheo/README index ae421b6e80..fe082797f1 100644 --- a/lib/rheo/README +++ b/lib/rheo/README @@ -1,7 +1,5 @@ -This directory has a Makefile.lammps file with settings that allows LAMMPS to -dynamically link to the GSL library. This is required to use the RHEO package -in a LAMMPS input script. If you have the pkg-config command available, it -will automatically import the GSL settings. Otherwise they will have to be -added manually. - -See the header of Makefile.lammps for more info. +This directory has multiple Makefile.lammps variant files with settings that +allows LAMMPS to link with a BLAS/LAPACK or compatible library or the bundled +linalg library (which is subset of BLAS/LAPACK). Copy the suitable file +to Makefile.lammps and edit, if needed. +This is required to use the RHEO package in a LAMMPS input script. From d909b205c72115345f301fdbb22f489bd690379e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 18 Sep 2024 20:14:43 -0400 Subject: [PATCH 307/355] update docs for building the RHEO package --- doc/src/Build_extras.rst | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/doc/src/Build_extras.rst b/doc/src/Build_extras.rst index ac7edc7678..6ce917339b 100644 --- a/doc/src/Build_extras.rst +++ b/doc/src/Build_extras.rst @@ -2251,28 +2251,37 @@ verified to work in February 2020 with Quantum Espresso versions 6.3 to RHEO package ------------ -To build with this package you must have the `GNU Scientific Library -(GSL) ` installed in locations that -are accessible in your environment. The GSL library should be at least -version 2.7. +This package depends on the BPM package. .. tabs:: .. tab:: CMake build - If CMake cannot find the GSL library or include files, you can set: - .. code-block:: bash - -D GSL_ROOT_DIR=path # path to root of GSL installation + -D PKG_RHEO=yes # enable the package itself + -D PKG_BPM=yes # the RHEO package requires BPM + -D USE_INTERNAL_LINALG=value # + + Features in the RHEO package are dependent on code in the BPM + package so the latter one *must* be enabled. + + The RHEO package also requires LAPACK (and BLAS) and CMake + can identify their locations and pass that info to the RHEO + build script. But on some systems this may cause problems when + linking or the dependency is not desired. Try enabling + ``USE_INTERNAL_LINALG`` in those cases to use the bundled linear + algebra library and work around the limitations. .. tab:: Traditional make - LAMMPS will try to auto-detect the GSL compiler and linker flags - from the corresponding ``pkg-config`` file (``gsl.pc``), otherwise - you can edit the file ``lib/rheo/Makefile.lammps`` - to specify the paths and library names where indicated by comments. - This must be done **before** the package is installed. + The RHEO package also requires LAPACK (and BLAS) which can be + either a system provided library or the bundled "linalg" library + which is a subset of LAPACK. For that, one of the provided + ``Makefile.lammps.`` files needs to be copied to + ``Makefile.lammps`` and edited as needed. The default file + uses the bundled "linalg" library, which can be built with + ``make lib-linalg args='-m serial'``. ---------- From 42bcc43ece9a6bed656a3c7ce0ac0144f692fdf3 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 18 Sep 2024 20:22:17 -0400 Subject: [PATCH 308/355] add missing line in Install.sh file --- src/RHEO/Install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/src/RHEO/Install.sh b/src/RHEO/Install.sh index e34ca3a555..07a439f44b 100644 --- a/src/RHEO/Install.sh +++ b/src/RHEO/Install.sh @@ -47,6 +47,7 @@ if (test $1 = 1) then sed -i -e 's/[^ \t]*rheo[^ \t]* //' ../Makefile.package sed -i -e 's|^PKG_SYSINC =[ \t]*|&$(rheo_SYSINC) |' ../Makefile.package sed -i -e 's|^PKG_SYSLIB =[ \t]*|&$(rheo_SYSLIB) |' ../Makefile.package + sed -i -e 's|^PKG_SYSPATH =[ \t]*|&$(rheo_SYSPATH) |' ../Makefile.package fi if (test -e ../Makefile.package.settings) then From 4eb1b1f60678f5fd93a201e30967824a02b3fc86 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 18 Sep 2024 23:14:27 -0400 Subject: [PATCH 309/355] clarify docs --- doc/src/Build_extras.rst | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/doc/src/Build_extras.rst b/doc/src/Build_extras.rst index 6ce917339b..8465bea829 100644 --- a/doc/src/Build_extras.rst +++ b/doc/src/Build_extras.rst @@ -2261,27 +2261,28 @@ This package depends on the BPM package. -D PKG_RHEO=yes # enable the package itself -D PKG_BPM=yes # the RHEO package requires BPM - -D USE_INTERNAL_LINALG=value # + -D USE_INTERNAL_LINALG=value # prefer internal LAPACK if true - Features in the RHEO package are dependent on code in the BPM - package so the latter one *must* be enabled. + Some features in the RHEO package are dependent on code in the BPM + package so the latter one *must* be enabled as well. The RHEO package also requires LAPACK (and BLAS) and CMake can identify their locations and pass that info to the RHEO build script. But on some systems this may cause problems when - linking or the dependency is not desired. Try enabling - ``USE_INTERNAL_LINALG`` in those cases to use the bundled linear - algebra library and work around the limitations. + linking or the dependency is not desired. By using the setting + ``-D USE_INTERNAL_LINALG=yes`` when running the CMake + configuration, you will select compiling and linking the bundled + linear algebra library and work around the limitations. .. tab:: Traditional make - The RHEO package also requires LAPACK (and BLAS) which can be - either a system provided library or the bundled "linalg" library - which is a subset of LAPACK. For that, one of the provided - ``Makefile.lammps.`` files needs to be copied to - ``Makefile.lammps`` and edited as needed. The default file - uses the bundled "linalg" library, which can be built with - ``make lib-linalg args='-m serial'``. + The RHEO package requires LAPACK (and BLAS) which can be either + a system provided library or the bundled "linalg" library. This + is a subset of LAPACK translated to C++. For that, one of the + provided ``Makefile.lammps.`` files needs to be copied + to ``Makefile.lammps`` and edited as needed. The default file + uses the bundled "linalg" library, which can be built by + ``make lib-linalg args='-m serial'`` in the ``src`` folder. ---------- From d23b8aa17f9d4ca99432e6743ca506af25ec16a1 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 18 Sep 2024 23:29:49 -0400 Subject: [PATCH 310/355] try to work around macOS issue --- src/RHEO/compute_rheo_kernel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/RHEO/compute_rheo_kernel.cpp b/src/RHEO/compute_rheo_kernel.cpp index 329e955a32..453f2b9028 100644 --- a/src/RHEO/compute_rheo_kernel.cpp +++ b/src/RHEO/compute_rheo_kernel.cpp @@ -49,7 +49,7 @@ static constexpr int MAX_MDIM = 12; // declare LAPACK functions extern "C" { - void dpotrf2_(const char *uplo, const int *n, double *a, const int *lda, int *info); + void dpotrf_(const char *uplo, const int *n, double *a, const int *lda, int *info); void dpotri_(const char *uplo, const int *n, double *a, const int *lda, int *info); } @@ -721,7 +721,7 @@ void ComputeRHEOKernel::compute_peratom() // Use LAPACK to get Minv, use Cholesky decomposition since the // polynomials are independent, M is symmetrix & positive-definite const char uplo = 'U'; - dpotrf2_(&uplo, &Mdim, M, &Mdim, &lapack_error); + dpotrf_(&uplo, &Mdim, M, &Mdim, &lapack_error); if (lapack_error) { // Revert to uncorrected SPH for this particle From 3d2a344dd5e69cf9b376704994fadcb1c0fdc86e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 19 Sep 2024 03:36:09 -0400 Subject: [PATCH 311/355] we no longer need to install GSL for workflows with RHEO --- .github/workflows/check-vla.yml | 2 +- .github/workflows/full-regression.yml | 3 ++- .github/workflows/quick-regression.yml | 3 ++- .github/workflows/unittest-linux.yml | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/check-vla.yml b/.github/workflows/check-vla.yml index 26f23cc33f..ab89018a3d 100644 --- a/.github/workflows/check-vla.yml +++ b/.github/workflows/check-vla.yml @@ -27,9 +27,9 @@ jobs: - name: Install extra packages run: | + sudo apt-get update sudo apt-get install -y ccache \ libeigen3-dev \ - libgsl-dev \ libcurl4-openssl-dev \ mold \ mpi-default-bin \ diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index 73e1803bb6..a6b5353b9b 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -30,8 +30,9 @@ jobs: - name: Install extra packages run: | + sudo apt-get update sudo apt-get install -y ccache ninja-build libeigen3-dev \ - libgsl-dev libcurl4-openssl-dev python3-dev \ + libcurl4-openssl-dev python3-dev \ mpi-default-bin mpi-default-dev - name: Create Build Environment diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index 6174d57ec2..88794bfa0a 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -34,8 +34,9 @@ jobs: - name: Install extra packages run: | + sudo apt-get update sudo apt-get install -y ccache ninja-build libeigen3-dev \ - libgsl-dev libcurl4-openssl-dev python3-dev \ + libcurl4-openssl-dev python3-dev \ mpi-default-bin mpi-default-dev - name: Create Build Environment diff --git a/.github/workflows/unittest-linux.yml b/.github/workflows/unittest-linux.yml index dcf495ccc0..ce98fcea35 100644 --- a/.github/workflows/unittest-linux.yml +++ b/.github/workflows/unittest-linux.yml @@ -31,9 +31,9 @@ jobs: - name: Install extra packages run: | + sudo apt-get update sudo apt-get install -y ccache \ libeigen3-dev \ - libgsl-dev \ libcurl4-openssl-dev \ mold \ ninja-build \ From cc24f3d2409f69024067c8ab3fd617e4580b1e02 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:16:55 +0200 Subject: [PATCH 312/355] add extract() function to angle_charmm.h --- src/MOLECULE/angle_charmm.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/MOLECULE/angle_charmm.h b/src/MOLECULE/angle_charmm.h index 2a77ac7864..cc4095e90f 100644 --- a/src/MOLECULE/angle_charmm.h +++ b/src/MOLECULE/angle_charmm.h @@ -35,6 +35,7 @@ class AngleCharmm : public Angle { void read_restart(FILE *) override; void write_data(FILE *) override; double single(int, int, int, int) override; + void *extract(const char *, int &) override; protected: double *k, *theta0, *k_ub, *r_ub; From 438a8c9335bbe03fce572cf4a37ac4cac57432ba Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:19:47 +0200 Subject: [PATCH 313/355] add extract() function to angle_charmm.cpp --- src/MOLECULE/angle_charmm.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/MOLECULE/angle_charmm.cpp b/src/MOLECULE/angle_charmm.cpp index 1b66260c55..11b5abd699 100644 --- a/src/MOLECULE/angle_charmm.cpp +++ b/src/MOLECULE/angle_charmm.cpp @@ -309,3 +309,15 @@ double AngleCharmm::single(int type, int i1, int i2, int i3) return (tk * dtheta + rk * dr); } + +/* ---------------------------------------------------------------------- + return ptr to internal members upon request +------------------------------------------------------------------------ */ + +void *AngleCharmm::extract(const char *str, int &dim) +{ + dim = 1; + if (strcmp(str, "k") == 0) return (void *) k; + if (strcmp(str, "theta0") == 0) return (void *) theta0; + return nullptr; +} From 9d1a0240d636f5fa010eb84e6af035a16a5cee38 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:21:32 +0200 Subject: [PATCH 314/355] add extract() function to angle_fourier_simple.h --- src/EXTRA-MOLECULE/angle_fourier_simple.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EXTRA-MOLECULE/angle_fourier_simple.h b/src/EXTRA-MOLECULE/angle_fourier_simple.h index 3296ba6067..d37b3a83a8 100644 --- a/src/EXTRA-MOLECULE/angle_fourier_simple.h +++ b/src/EXTRA-MOLECULE/angle_fourier_simple.h @@ -36,6 +36,7 @@ class AngleFourierSimple : public Angle { void write_data(FILE *) override; double single(int, int, int, int) override; void born_matrix(int type, int i1, int i2, int i3, double &du, double &du2) override; + void *extract(const char *, int &) override; protected: double *k, *C, *N; From 3b73f56d79ca2d3f23bd2b2e475f116b26579f85 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:22:29 +0200 Subject: [PATCH 315/355] add extract() function to angle_fourier_simple.cpp --- src/EXTRA-MOLECULE/angle_fourier_simple.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/EXTRA-MOLECULE/angle_fourier_simple.cpp b/src/EXTRA-MOLECULE/angle_fourier_simple.cpp index 6de7956ffa..143a008039 100644 --- a/src/EXTRA-MOLECULE/angle_fourier_simple.cpp +++ b/src/EXTRA-MOLECULE/angle_fourier_simple.cpp @@ -316,3 +316,16 @@ void AngleFourierSimple::born_matrix(int type, int i1, int i2, int i3, double &d du2 = k[type] * C[type] * N[type] * (cos(theta) * sin(N[type] * theta) - N[type] * sin(theta) * cos(N[type] * theta)) / pow(sin(theta),3); } + +/* ---------------------------------------------------------------------- + return ptr to internal members upon request +------------------------------------------------------------------------ */ + +void *AngleFourierSimple::extract(const char *str, int &dim) +{ + dim = 1; + if (strcmp(str, "k") == 0) return (void *) k; + if (strcmp(str, "C") == 0) return (void *) C; + if (strcmp(str, "N") == 0) return (void *) N; + return nullptr; +} From df533d97cbe7997086ca1de24c30f77bf7499389 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:24:20 +0200 Subject: [PATCH 316/355] add extract() function to angle_quartic.h --- src/EXTRA-MOLECULE/angle_quartic.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EXTRA-MOLECULE/angle_quartic.h b/src/EXTRA-MOLECULE/angle_quartic.h index 7de51b24d1..3ff7f6f3e4 100644 --- a/src/EXTRA-MOLECULE/angle_quartic.h +++ b/src/EXTRA-MOLECULE/angle_quartic.h @@ -36,6 +36,7 @@ class AngleQuartic : public Angle { void write_data(FILE *) override; double single(int, int, int, int) override; void born_matrix(int type, int i1, int i2, int i3, double &du, double &du2) override; + void *extract(const char *, int &) override; protected: double *k2, *k3, *k4, *theta0; From 1e00249b737afbd66373233596a61c82a79e80d0 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:25:04 +0200 Subject: [PATCH 317/355] add extract() function to angle_quartic.cpp --- src/EXTRA-MOLECULE/angle_quartic.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/EXTRA-MOLECULE/angle_quartic.cpp b/src/EXTRA-MOLECULE/angle_quartic.cpp index aade6b4534..616c81c749 100644 --- a/src/EXTRA-MOLECULE/angle_quartic.cpp +++ b/src/EXTRA-MOLECULE/angle_quartic.cpp @@ -325,3 +325,17 @@ void AngleQuartic::born_matrix(int type, int i1, int i2, int i3, double &du, dou du2 = (2.0 * k2[type] + 6.0 * k3[type] * dtheta + 12.0 * k4[type] * dtheta2) / (s*s) - (2.0 * k2[type] * dtheta + 3.0 * k3[type] * dtheta2 + 4.0 * k4[type] * dtheta3) * c / (s*s*s); } + +/* ---------------------------------------------------------------------- + return ptr to internal members upon request +------------------------------------------------------------------------ */ + +void *AngleQuartic::extract(const char *str, int &dim) +{ + dim = 1; + if (strcmp(str, "k2") == 0) return (void *) k2; + if (strcmp(str, "k3") == 0) return (void *) k3; + if (strcmp(str, "k4") == 0) return (void *) k4; + if (strcmp(str, "theta0") == 0) return (void *) theta0; + return nullptr; +} From bf9cb26afb28e2a3edfe380ab8aaec3e8f72e400 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:25:49 +0200 Subject: [PATCH 318/355] add extract() function to angle_fourier.h --- src/EXTRA-MOLECULE/angle_fourier.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EXTRA-MOLECULE/angle_fourier.h b/src/EXTRA-MOLECULE/angle_fourier.h index 8fa5d14b26..c0e30c8e1a 100644 --- a/src/EXTRA-MOLECULE/angle_fourier.h +++ b/src/EXTRA-MOLECULE/angle_fourier.h @@ -36,6 +36,7 @@ class AngleFourier : public Angle { void write_data(FILE *) override; double single(int, int, int, int) override; void born_matrix(int type, int i1, int i2, int i3, double &du, double &du2) override; + void *extract(const char *, int &) override; protected: double *k, *C0, *C1, *C2; From 7b37f1ae1709616383365242bbc0568084d3bdd1 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:26:25 +0200 Subject: [PATCH 319/355] add extract() function to angle_fourier.cpp --- src/EXTRA-MOLECULE/angle_fourier.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/EXTRA-MOLECULE/angle_fourier.cpp b/src/EXTRA-MOLECULE/angle_fourier.cpp index da1667c06f..abcda6d036 100644 --- a/src/EXTRA-MOLECULE/angle_fourier.cpp +++ b/src/EXTRA-MOLECULE/angle_fourier.cpp @@ -309,3 +309,16 @@ void AngleFourier::born_matrix(int type, int i1, int i2, int i3, double &du, dou du = k[type] * (C1[type] + 4 * C2[type] * c); } +/* ---------------------------------------------------------------------- + return ptr to internal members upon request +------------------------------------------------------------------------ */ + +void *AngleFourier::extract(const char *str, int &dim) +{ + dim = 1; + if (strcmp(str, "k") == 0) return (void *) k; + if (strcmp(str, "C0") == 0) return (void *) C0; + if (strcmp(str, "C1") == 0) return (void *) C1; + if (strcmp(str, "C2") == 0) return (void *) C2; + return nullptr; +} From 42f0940aeea06d770491664870f9dba16b6c7b65 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:27:18 +0200 Subject: [PATCH 320/355] add extract() function to angle_cosine_periodic.h --- src/EXTRA-MOLECULE/angle_cosine_periodic.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EXTRA-MOLECULE/angle_cosine_periodic.h b/src/EXTRA-MOLECULE/angle_cosine_periodic.h index f04ed04784..f63029919e 100644 --- a/src/EXTRA-MOLECULE/angle_cosine_periodic.h +++ b/src/EXTRA-MOLECULE/angle_cosine_periodic.h @@ -36,6 +36,7 @@ class AngleCosinePeriodic : public Angle { void write_data(FILE *) override; double single(int, int, int, int) override; void born_matrix(int type, int i1, int i2, int i3, double &du, double &du2) override; + void *extract(const char *, int &) override; protected: double *k; From f916a0cc63b2b029886c27169bf258e33f50034e Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:27:58 +0200 Subject: [PATCH 321/355] add extract() function to angle_cosine_periodic.cpp --- src/EXTRA-MOLECULE/angle_cosine_periodic.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/EXTRA-MOLECULE/angle_cosine_periodic.cpp b/src/EXTRA-MOLECULE/angle_cosine_periodic.cpp index 0b2a6d336d..6aae8d3ff4 100644 --- a/src/EXTRA-MOLECULE/angle_cosine_periodic.cpp +++ b/src/EXTRA-MOLECULE/angle_cosine_periodic.cpp @@ -336,3 +336,16 @@ void AngleCosinePeriodic::born_matrix(int type, int i1, int i2, int i3, double & du = prefactor * sin(m_angle) / s; du2 = prefactor * (c * sin(m_angle) - s * cos(m_angle) * multiplicity[type]) / (s * s * s); } + +/* ---------------------------------------------------------------------- + return ptr to internal members upon request +------------------------------------------------------------------------ */ + +void *AngleCosinePeriodic::extract(const char *str, int &dim) +{ + dim = 1; + if (strcmp(str, "k") == 0) return (void *) k; + if (strcmp(str, "b") == 0) return (void *) b; + if (strcmp(str, "multiplicity") == 0) return (void *) multiplicity; + return nullptr; +} From 25f12bf8007614318217cc322c01f09d9a491229 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:28:56 +0200 Subject: [PATCH 322/355] add extract() function to angle_cosine_squared_restricted.h --- src/EXTRA-MOLECULE/angle_cosine_squared_restricted.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EXTRA-MOLECULE/angle_cosine_squared_restricted.h b/src/EXTRA-MOLECULE/angle_cosine_squared_restricted.h index 674252b7d0..b38b6bc4bd 100644 --- a/src/EXTRA-MOLECULE/angle_cosine_squared_restricted.h +++ b/src/EXTRA-MOLECULE/angle_cosine_squared_restricted.h @@ -36,6 +36,7 @@ class AngleCosineSquaredRestricted : public Angle { void write_data(FILE *) override; double single(int, int, int, int) override; void born_matrix(int type, int i1, int i2, int i3, double &du, double &du2) override; + void *extract(const char *, int &) override; protected: double *k, *theta0; From cc9f45af88ccafb91f7ed8f5c834b4aafc16f584 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:29:34 +0200 Subject: [PATCH 323/355] add extract() function to angle_cosine_squared_restricted.cpp --- .../angle_cosine_squared_restricted.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/EXTRA-MOLECULE/angle_cosine_squared_restricted.cpp b/src/EXTRA-MOLECULE/angle_cosine_squared_restricted.cpp index 2da31ef893..c6d78ea133 100644 --- a/src/EXTRA-MOLECULE/angle_cosine_squared_restricted.cpp +++ b/src/EXTRA-MOLECULE/angle_cosine_squared_restricted.cpp @@ -296,3 +296,15 @@ void AngleCosineSquaredRestricted::born_matrix(int type, int i1, int i2, int i3, du2 = 2 * k[type] * numerator / denominator; } + +/* ---------------------------------------------------------------------- + return ptr to internal members upon request +------------------------------------------------------------------------ */ + +void *AngleCosineSquaredRestricted::extract(const char *str, int &dim) +{ + dim = 1; + if (strcmp(str, "k") == 0) return (void *) k; + if (strcmp(str, "theta0") == 0) return (void *) theta0; + return nullptr; +} From c65a6e8b59ff5d684bafea2d5bef6fe9e0c0de40 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:30:42 +0200 Subject: [PATCH 324/355] add extract() function to angle_mm3.h --- src/YAFF/angle_mm3.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/YAFF/angle_mm3.h b/src/YAFF/angle_mm3.h index 22f5bd746c..126b275e72 100644 --- a/src/YAFF/angle_mm3.h +++ b/src/YAFF/angle_mm3.h @@ -36,6 +36,7 @@ class AngleMM3 : public Angle { void write_data(FILE *) override; double single(int, int, int, int) override; void born_matrix(int type, int i1, int i2, int i3, double &du, double &du2) override; + void *extract(const char *, int &) override; protected: double *theta0, *k2; From 83b2e88b734bedf651796c62c57242de8ff220f5 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:31:20 +0200 Subject: [PATCH 325/355] add extract() function to angle_mm3.cpp --- src/YAFF/angle_mm3.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/YAFF/angle_mm3.cpp b/src/YAFF/angle_mm3.cpp index 3ff7df1653..920041f7e9 100644 --- a/src/YAFF/angle_mm3.cpp +++ b/src/YAFF/angle_mm3.cpp @@ -327,3 +327,15 @@ void AngleMM3::born_matrix(int type, int i1, int i2, int i3, double &du, double du = -k2[type] * df / s; du2 = k2[type] * (d2f - df * c / s) / (s * s) ; } + +/* ---------------------------------------------------------------------- + return ptr to internal members upon request +------------------------------------------------------------------------ */ + +void *AngleMM3::extract(const char *str, int &dim) +{ + dim = 1; + if (strcmp(str, "k2") == 0) return (void *) k2; + if (strcmp(str, "theta0") == 0) return (void *) theta0; + return nullptr; +} From 4272d4a39083f7f97d2e399ba143c0e2d7e6ad1c Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:32:05 +0200 Subject: [PATCH 326/355] add extract() function to bond_mm3.h --- src/YAFF/bond_mm3.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/YAFF/bond_mm3.h b/src/YAFF/bond_mm3.h index ea89ac826d..b9ebf464bb 100644 --- a/src/YAFF/bond_mm3.h +++ b/src/YAFF/bond_mm3.h @@ -36,6 +36,7 @@ class BondMM3 : public Bond { void write_data(FILE *) override; double single(int, double, int, int, double &) override; void born_matrix(int, double, int, int, double &, double &) override; + void *extract(const char *, int &) override; protected: double *r0, *k2; From 8a2564fff196224476745a07ff9ac5663fae71d1 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:32:41 +0200 Subject: [PATCH 327/355] add extract() function to bond_mm3.cpp --- src/YAFF/bond_mm3.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/YAFF/bond_mm3.cpp b/src/YAFF/bond_mm3.cpp index 31ce2dad3e..b3e69881e1 100644 --- a/src/YAFF/bond_mm3.cpp +++ b/src/YAFF/bond_mm3.cpp @@ -238,3 +238,15 @@ void BondMM3::born_matrix(int type, double rsq, int /*i*/, int /*j*/, double &du du = 2.0 * k2[type] * dr + 3.0 * K3 * dr2 + 4.0 * K4 * dr3; du2 = 2.0 * k2[type] + 6.0 * K3 * dr + 12.0 * K4 * dr2; } + +/* ---------------------------------------------------------------------- + return ptr to internal members upon request +------------------------------------------------------------------------ */ + +void *BondMM3::extract(const char *str, int &dim) +{ + dim = 1; + if (strcmp(str, "k2") == 0) return (void *) k2; + if (strcmp(str, "r0") == 0) return (void *) r0; + return nullptr; +} From 8e071495932a30bcf6e293cd64b03b04c54eb0e0 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:33:41 +0200 Subject: [PATCH 328/355] add extract() function to angle_spica.h --- src/CG-SPICA/angle_spica.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CG-SPICA/angle_spica.h b/src/CG-SPICA/angle_spica.h index 539512c0e9..5e590ba7a0 100644 --- a/src/CG-SPICA/angle_spica.h +++ b/src/CG-SPICA/angle_spica.h @@ -37,6 +37,7 @@ class AngleSPICA : public Angle { void read_restart(FILE *) override; void write_data(FILE *) override; double single(int, int, int, int) override; + void *extract(const char *, int &) override; protected: double *k, *theta0; From 3ce4c782d15cebdc827c417e208ea45d39f9ca7a Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:34:16 +0200 Subject: [PATCH 329/355] add extract() function to angle_spica.cpp --- src/CG-SPICA/angle_spica.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/CG-SPICA/angle_spica.cpp b/src/CG-SPICA/angle_spica.cpp index e315e20f13..55ef1beaed 100644 --- a/src/CG-SPICA/angle_spica.cpp +++ b/src/CG-SPICA/angle_spica.cpp @@ -522,3 +522,14 @@ double AngleSPICA::single(int type, int i1, int i2, int i3) double tk = k[type] * dtheta; return tk*dtheta + e13; } + + return ptr to internal members upon request +------------------------------------------------------------------------ */ + +void *AngleSPICA::extract(const char *str, int &dim) +{ + dim = 1; + if (strcmp(str, "k") == 0) return (void *) k; + if (strcmp(str, "theta0") == 0) return (void *) theta0; + return nullptr; +} From 873030982de35fdb593ecdf130d66086eca0e6a5 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:35:11 +0200 Subject: [PATCH 330/355] add extract() function to angle_dipole.h --- src/DIPOLE/angle_dipole.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/DIPOLE/angle_dipole.h b/src/DIPOLE/angle_dipole.h index 2e55722673..de0f958f98 100644 --- a/src/DIPOLE/angle_dipole.h +++ b/src/DIPOLE/angle_dipole.h @@ -36,6 +36,7 @@ class AngleDipole : public Angle { void read_restart(FILE *) override; void write_data(FILE *) override; double single(int, int, int, int) override; + void *extract(const char *, int &) override; protected: double *k, *gamma0; From c5e62b4a47ef7cb6444386e84a8b9f61848be91e Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:35:46 +0200 Subject: [PATCH 331/355] add extract() function to angle_dipole.cpp --- src/DIPOLE/angle_dipole.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/DIPOLE/angle_dipole.cpp b/src/DIPOLE/angle_dipole.cpp index 6ad4a0fb4c..a66f3e1042 100644 --- a/src/DIPOLE/angle_dipole.cpp +++ b/src/DIPOLE/angle_dipole.cpp @@ -263,3 +263,15 @@ double AngleDipole::single(int type, int iRef, int iDip, int /*iDummy*/) return kdg * deltaGamma; // energy } + +/* ---------------------------------------------------------------------- + return ptr to internal members upon request +------------------------------------------------------------------------ */ + +void *AngleDipole::extract(const char *str, int &dim) +{ + dim = 1; + if (strcmp(str, "k") == 0) return (void *) k; + if (strcmp(str, "gamma0") == 0) return (void *) gamma0; + return nullptr; +} From b49f3412df2c30e67bb93919ee02478c6552f9ed Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:36:35 +0200 Subject: [PATCH 332/355] add extract() function to angle_class2.h --- src/CLASS2/angle_class2.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CLASS2/angle_class2.h b/src/CLASS2/angle_class2.h index f5fbd62b57..4ed6f344ae 100644 --- a/src/CLASS2/angle_class2.h +++ b/src/CLASS2/angle_class2.h @@ -35,6 +35,7 @@ class AngleClass2 : public Angle { void read_restart(FILE *) override; void write_data(FILE *) override; double single(int, int, int, int) override; + void *extract(const char *, int &) override; protected: double *theta0, *k2, *k3, *k4; From 7f0ff96324a0c088cec01b8407e1fd69d18968a9 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:37:12 +0200 Subject: [PATCH 333/355] add extract() function to angle_class2.cpp --- src/CLASS2/angle_class2.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/CLASS2/angle_class2.cpp b/src/CLASS2/angle_class2.cpp index 118179ad91..5000f9f629 100644 --- a/src/CLASS2/angle_class2.cpp +++ b/src/CLASS2/angle_class2.cpp @@ -467,3 +467,17 @@ double AngleClass2::single(int type, int i1, int i2, int i3) return energy; } + +/* ---------------------------------------------------------------------- + return ptr to internal members upon request +------------------------------------------------------------------------ */ + +void *AngleClass2::extract(const char *str, int &dim) +{ + dim = 1; + if (strcmp(str, "k2") == 0) return (void *) k2; + if (strcmp(str, "k3") == 0) return (void *) k3; + if (strcmp(str, "k4") == 0) return (void *) k4; + if (strcmp(str, "theta0") == 0) return (void *) theta0; + return nullptr; +} From 423b058820cd88f0a0a0fa20a32152d8f62dfe51 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:38:22 +0200 Subject: [PATCH 334/355] add extract() function to bond_fene_expand.h --- src/MOLECULE/bond_fene_expand.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/MOLECULE/bond_fene_expand.h b/src/MOLECULE/bond_fene_expand.h index cdce710ea1..13524b0972 100644 --- a/src/MOLECULE/bond_fene_expand.h +++ b/src/MOLECULE/bond_fene_expand.h @@ -36,6 +36,7 @@ class BondFENEExpand : public Bond { void read_restart(FILE *) override; void write_data(FILE *) override; double single(int, double, int, int, double &) override; + void *extract(const char *, int &) override; protected: double *k, *r0, *epsilon, *sigma, *shift; From b2a2cefc021f7fabb5cfdeaf7dfa5ed904995372 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:38:58 +0200 Subject: [PATCH 335/355] add extract() function to bond_fene_expand.cpp --- src/MOLECULE/bond_fene_expand.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/MOLECULE/bond_fene_expand.cpp b/src/MOLECULE/bond_fene_expand.cpp index c7821b1826..e115596eb1 100644 --- a/src/MOLECULE/bond_fene_expand.cpp +++ b/src/MOLECULE/bond_fene_expand.cpp @@ -273,3 +273,18 @@ double BondFENEExpand::single(int type, double rsq, int /*i*/, int /*j*/, double return eng; } + +/* ---------------------------------------------------------------------- + return ptr to internal members upon request +------------------------------------------------------------------------ */ + +void *BondFENEExpand::extract(const char *str, int &dim) +{ + dim = 1; + if (strcmp(str, "k") == 0) return (void *) k; + if (strcmp(str, "r0") == 0) return (void *) r0; + if (strcmp(str, "epsilon") == 0) return (void *) epsilon; + if (strcmp(str, "sigma") == 0) return (void *) sigma; + if (strcmp(str, "shift") == 0) return (void *) shift; + return nullptr; +} From d8c0691684191011ee5c6e25b5e98b955cc48644 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 19:55:07 +0200 Subject: [PATCH 336/355] Update bond section of fix_adapt.rst --- doc/src/fix_adapt.rst | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/doc/src/fix_adapt.rst b/doc/src/fix_adapt.rst index 1943798160..88fed772d8 100644 --- a/doc/src/fix_adapt.rst +++ b/doc/src/fix_adapt.rst @@ -322,25 +322,29 @@ all types from 1 to :math:`N`. A leading asterisk means all types from If :doc:`bond_style hybrid ` is used, *bstyle* should be a sub-style name. The bond styles that currently work with fix adapt are: -+---------------------------------------------------+------------+------------+ -| :doc:`class2 ` | r0 | type bonds | -+---------------------------------------------------+------------+------------+ -| :doc:`fene ` | k,r0 | type bonds | -+---------------------------------------------------+------------+------------+ -| :doc:`fene/nm ` | k,r0 | type bonds | -+---------------------------------------------------+------------+------------+ -| :doc:`gromos ` | k,r0 | type bonds | -+---------------------------------------------------+------------+------------+ -| :doc:`harmonic ` | k,r0 | type bonds | -+---------------------------------------------------+------------+------------+ -| :doc:`harmonic/shift ` | k,r0,r1 | type bonds | -+---------------------------------------------------+------------+------------+ -| :doc:`harmonic/restrain ` | k | type bonds | -+---------------------------------------------------+------------+------------+ -| :doc:`morse ` | r0 | type bonds | -+---------------------------------------------------+------------+------------+ -| :doc:`nonlinear ` | epsilon,r0 | type bonds | -+---------------------------------------------------+------------+------------+ ++---------------------------------------------------+---------------------------+------------+ +| :doc:`class2 ` | r0 | type bonds | ++---------------------------------------------------+---------------------------+------------+ +| :doc:`fene ` | k,r0 | type bonds | ++---------------------------------------------------+---------------------------+------------+ +| :doc:`fene/expand ` | k,r0,epsilon,sigma,shift | type bonds | ++---------------------------------------------------+---------------------------+------------+ +| :doc:`fene/nm ` | k,r0 | type bonds | ++---------------------------------------------------+---------------------------+------------+ +| :doc:`gromos ` | k,r0 | type bonds | ++---------------------------------------------------+---------------------------+------------+ +| :doc:`harmonic ` | k,r0 | type bonds | ++---------------------------------------------------+---------------------------+------------+ +| :doc:`harmonic/shift ` | k,r0,r1 | type bonds | ++---------------------------------------------------+---------------------------+------------+ +| :doc:`harmonic/restrain ` | k | type bonds | ++---------------------------------------------------+---------------------------+------------+ +| :doc:`mm3 ` | k,r0 | type bonds | ++---------------------------------------------------+---------------------------+------------+ +| :doc:`morse ` | r0 | type bonds | ++---------------------------------------------------+---------------------------+------------+ +| :doc:`nonlinear ` | epsilon,r0 | type bonds | ++---------------------------------------------------+---------------------------+------------+ ---------- From 68548cbb0ddfde1719bd330667c7482c36f0dfe5 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Fri, 20 Sep 2024 20:53:22 +0200 Subject: [PATCH 337/355] Update angle_spica.cpp --- src/CG-SPICA/angle_spica.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CG-SPICA/angle_spica.cpp b/src/CG-SPICA/angle_spica.cpp index 55ef1beaed..913428cd9b 100644 --- a/src/CG-SPICA/angle_spica.cpp +++ b/src/CG-SPICA/angle_spica.cpp @@ -523,6 +523,7 @@ double AngleSPICA::single(int type, int i1, int i2, int i3) return tk*dtheta + e13; } +/* ---------------------------------------------------------------------- return ptr to internal members upon request ------------------------------------------------------------------------ */ From 20f3b5a7dbeb6d6eb36fd1a87a32606674eba75b Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 20 Sep 2024 21:44:52 -0400 Subject: [PATCH 338/355] update readme --- src/RHEO/README | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/RHEO/README b/src/RHEO/README index 4b6f2a162a..15b642442a 100644 --- a/src/RHEO/README +++ b/src/RHEO/README @@ -3,8 +3,9 @@ multiphase fluid systems. The authors include Joel Clemmer (Sandia), Thomas O'Connor (Carnegie Mellon), and Eric Palermo (Carnegie Mellon). Bond style rheo/shell, compute style rheo/property/atom, and fix style -rheo/temperature all depend on the BPM package. +rheo/temperature depend on the BPM package, so it is required to install +the BPM package with RHEO. -This package requires the GNU scientific library (GSL). We recommend version -2.7 or later. To build this package, one must first separately install GSL in -a location that can be found by your environment. +This package requires the BLAS/LAPACK. This can be either a seperate installation +or you can use the bundled "linalg" library. Please see the LAMMPS manual at +https://docs.lammps.org/Build_extras.html#rheo for details. From 8294bea7a77f87c827e27b6a293029b1e29eb007 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Sat, 21 Sep 2024 10:02:06 +0200 Subject: [PATCH 339/355] Update angle section of fix_adapt.rst --- doc/src/fix_adapt.rst | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/doc/src/fix_adapt.rst b/doc/src/fix_adapt.rst index 88fed772d8..a44ce8e780 100644 --- a/doc/src/fix_adapt.rst +++ b/doc/src/fix_adapt.rst @@ -367,11 +367,31 @@ all types from 1 to :math:`N`. A leading asterisk means all types from If :doc:`angle_style hybrid ` is used, *astyle* should be a sub-style name. The angle styles that currently work with fix adapt are: -+------------------------------------+----------+-------------+ -| :doc:`harmonic ` | k,theta0 | type angles | -+------------------------------------+----------+-------------+ -| :doc:`cosine ` | k | type angles | -+------------------------------------+----------+-------------+ ++--------------------------------------------------------------------+-----------------+-------------+ +| :doc:`harmonic ` | k,theta0 | type angles | ++--------------------------------------------------------------------+-----------------+-------------+ +| :doc:`charmm ` | k,theta0 | type angles | ++--------------------------------------------------------------------+-----------------+-------------+ +| :doc:`class2 ` | k2,k3,k4,theta0 | type angles | ++--------------------------------------------------------------------+-----------------+-------------+ +| :doc:`cosine ` | k | type angles | ++--------------------------------------------------------------------+-----------------+-------------+ +| :doc:`cosine/periodic ` | k,b,n | type angles | ++--------------------------------------------------------------------+-----------------+-------------+ +| :doc:`cosine/squared/restricted ` | k,theta0 | type angles | ++--------------------------------------------------------------------+-----------------+-------------+ +| :doc:`dipole ` | k,gamma0 | type angles | ++--------------------------------------------------------------------+-----------------+-------------+ +| :doc:`fourier ` | k,c0,c1,c2 | type angles | ++--------------------------------------------------------------------+-----------------+-------------+ +| :doc:`fourier/simple ` | k,c,n | type angles | ++--------------------------------------------------------------------+-----------------+-------------+ +| :doc:`mm3 ` | k,theta0 | type angles | ++--------------------------------------------------------------------+-----------------+-------------+ +| :doc:`quartic ` | k2,k3,k4,theta0 | type angles | ++--------------------------------------------------------------------+-----------------+-------------+ +| :doc:`spica ` | k,theta0 | type angles | ++--------------------------------------------------------------------+-----------------+-------------+ Note that internally, theta0 is stored in radians, so the variable this fix uses to reset theta0 needs to generate values in radians. From 0d6abcb86a693eeff6aef717c7d3de102628ea06 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Sun, 22 Sep 2024 15:31:01 +0200 Subject: [PATCH 340/355] unit test for extract() in bond-mm3.yaml --- unittest/force-styles/tests/bond-mm3.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unittest/force-styles/tests/bond-mm3.yaml b/unittest/force-styles/tests/bond-mm3.yaml index eb7443f1c2..f5ba5c237c 100644 --- a/unittest/force-styles/tests/bond-mm3.yaml +++ b/unittest/force-styles/tests/bond-mm3.yaml @@ -17,7 +17,9 @@ bond_coeff: ! | 4 650.0 1.2 5 450.0 1.0 equilibrium: 5 1.5 1.1 1.3 1.2 1 -extract: ! "" +extract: ! | + k2 1 + r0 1 natoms: 29 init_energy: 4.247265008273143 init_stress: ! |- From 96d99e3dd63b99adcada4016feffab4018d42d1d Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Sun, 22 Sep 2024 15:33:46 +0200 Subject: [PATCH 341/355] unit test for extract() in bond-fene_expand.yaml --- unittest/force-styles/tests/bond-fene_expand.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/unittest/force-styles/tests/bond-fene_expand.yaml b/unittest/force-styles/tests/bond-fene_expand.yaml index fc859d477c..250f89af15 100644 --- a/unittest/force-styles/tests/bond-fene_expand.yaml +++ b/unittest/force-styles/tests/bond-fene_expand.yaml @@ -17,7 +17,12 @@ bond_coeff: ! | 4 650 2.4 0.015 1.2 0.15 5 450 2 0.018 1 0.09 equilibrium: 5 1.5550000000000002 1.117 1.321 1.3139999999999998 1.06 -extract: ! "" +extract: ! | + k 1 + r0 1 + epsilon 1 + sigma 1 + shift 1 natoms: 29 init_energy: 5926.020859124294 init_stress: ! |- From 25c9f5a6ffcc54efc4a05f3ea2fcb2ca9556ff28 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Sun, 22 Sep 2024 20:01:52 +0200 Subject: [PATCH 342/355] unit test for extract() in angle-mm3.yaml --- unittest/force-styles/tests/angle-mm3.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unittest/force-styles/tests/angle-mm3.yaml b/unittest/force-styles/tests/angle-mm3.yaml index 9fb9460183..381f43187e 100644 --- a/unittest/force-styles/tests/angle-mm3.yaml +++ b/unittest/force-styles/tests/angle-mm3.yaml @@ -16,7 +16,9 @@ angle_coeff: ! | 3 50.0 120.0 4 100.0 108.5 equilibrium: 4 1.9216075064457565 1.9373154697137058 2.0943951023931953 1.8936822384138474 -extract: ! "" +extract: ! | + k2 1 + theta0 1 natoms: 29 init_energy: 44.72461548562619 init_stress: ! |2- From cd4bada16f2d3a3369942fc61044233d1fdd65af Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Sun, 22 Sep 2024 20:03:33 +0200 Subject: [PATCH 343/355] unit test for extract() in angle-charmm.yaml --- unittest/force-styles/tests/angle-charmm.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unittest/force-styles/tests/angle-charmm.yaml b/unittest/force-styles/tests/angle-charmm.yaml index 52e78abaf6..8ff5118390 100644 --- a/unittest/force-styles/tests/angle-charmm.yaml +++ b/unittest/force-styles/tests/angle-charmm.yaml @@ -15,7 +15,9 @@ angle_coeff: ! | 3 40.0 120.0 35.0 2.410 4 33.0 108.5 30.0 2.163 equilibrium: 4 1.9216075064457567 1.9425514574696887 2.0943951023931953 1.8936822384138476 -extract: ! "" +extract: ! | + k 1 + theta0 1 natoms: 29 init_energy: 85.42486388459771 init_stress: ! |2- From 1287977beeb14e7c1ae8dc99067e952196852d1a Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Sun, 22 Sep 2024 20:04:32 +0200 Subject: [PATCH 344/355] unit test for extract() in angle-spica.yaml --- unittest/force-styles/tests/angle-spica.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unittest/force-styles/tests/angle-spica.yaml b/unittest/force-styles/tests/angle-spica.yaml index 7f88553c70..46e8238349 100644 --- a/unittest/force-styles/tests/angle-spica.yaml +++ b/unittest/force-styles/tests/angle-spica.yaml @@ -20,7 +20,9 @@ angle_coeff: ! | 3 40.0 120.0 4 33.0 108.5 equilibrium: 4 1.9216075064457565 1.9425514574696887 2.0943951023931953 1.8936822384138474 -extract: ! "" +extract: ! | + k 1 + theta0 1 natoms: 29 init_energy: 38.36438529349082 init_stress: ! |2- From d6d63b87d16403eb890a1090fe98d743d65d9b5c Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Sun, 22 Sep 2024 20:05:32 +0200 Subject: [PATCH 345/355] unit test for extract() in angle-class2.yaml --- unittest/force-styles/tests/angle-class2.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/unittest/force-styles/tests/angle-class2.yaml b/unittest/force-styles/tests/angle-class2.yaml index ae2e3ff5ee..8901157d17 100644 --- a/unittest/force-styles/tests/angle-class2.yaml +++ b/unittest/force-styles/tests/angle-class2.yaml @@ -23,7 +23,11 @@ angle_coeff: ! | 3 ba 10.0 10.0 1.5 1.5 4 ba 0.0 20.0 1.5 1.5 equilibrium: 4 1.9216075064457565 1.9373154697137058 2.0943951023931953 1.8936822384138474 -extract: ! "" +extract: ! | + k2 1 + k3 1 + k4 1 + theta0 1 natoms: 29 init_energy: 46.44089683774903 init_stress: ! |2- From 517b4f43426a68dcf44d796180e348c8264af3ec Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Sun, 22 Sep 2024 20:06:17 +0200 Subject: [PATCH 346/355] unit test for extract() in angle-dipole.yaml --- unittest/force-styles/tests/angle-dipole.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unittest/force-styles/tests/angle-dipole.yaml b/unittest/force-styles/tests/angle-dipole.yaml index 877ffa19c7..3914973f0d 100644 --- a/unittest/force-styles/tests/angle-dipole.yaml +++ b/unittest/force-styles/tests/angle-dipole.yaml @@ -20,7 +20,9 @@ angle_coeff: ! | 3 50.0 120.0 4 100.0 108.5 equilibrium: 4 1.9216075064457565 1.9373154697137058 2.0943951023931953 1.8936822384138474 -extract: ! "" +extract: ! | + k 1 + gamma0 1 natoms: 29 init_energy: 1003.6681304854917 init_stress: ! |2- From 662ea3a191ff1e7829b14e63f6a57828921bc96c Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Sun, 22 Sep 2024 20:08:52 +0200 Subject: [PATCH 347/355] unit test for extract() in angle-quartic.yaml --- unittest/force-styles/tests/angle-quartic.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/unittest/force-styles/tests/angle-quartic.yaml b/unittest/force-styles/tests/angle-quartic.yaml index 6ded709e84..15ec06d82a 100644 --- a/unittest/force-styles/tests/angle-quartic.yaml +++ b/unittest/force-styles/tests/angle-quartic.yaml @@ -16,7 +16,11 @@ angle_coeff: ! | 3 120.0 50.0 -9.5 -1.5 4 108.5 100.0 5.0 -2.0 equilibrium: 4 1.9216075064457565 1.9373154697137058 2.0943951023931953 1.8936822384138474 -extract: ! "" +extract: ! | + k2 1 + k3 1 + k4 1 + theta0 1 natoms: 29 init_energy: 41.0458477552901 init_stress: ! |2- From 8355d3796a05974308057d35be6da192c2578e05 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Sun, 22 Sep 2024 20:09:37 +0200 Subject: [PATCH 348/355] unit test for extract() in angle-cosine_squared_restricted.yaml --- .../force-styles/tests/angle-cosine_squared_restricted.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unittest/force-styles/tests/angle-cosine_squared_restricted.yaml b/unittest/force-styles/tests/angle-cosine_squared_restricted.yaml index 400babb3c0..341ccb3919 100644 --- a/unittest/force-styles/tests/angle-cosine_squared_restricted.yaml +++ b/unittest/force-styles/tests/angle-cosine_squared_restricted.yaml @@ -17,7 +17,9 @@ angle_coeff: ! | 3 50.0 120.0 4 100.0 108.5 equilibrium: 4 1.9216075064457567 1.9373154697137058 2.0943951023931953 1.8936822384138476 -extract: ! "" +extract: ! | + k 1 + theta0 1 natoms: 29 init_energy: 43.16721849625078 init_stress: ! |2- From 950bcba5922b9e7f28b922cf7aa2fc7005bb84f0 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Sun, 22 Sep 2024 20:22:43 +0200 Subject: [PATCH 349/355] unit test for extract() in angle-fourier.yaml --- unittest/force-styles/tests/angle-fourier.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/unittest/force-styles/tests/angle-fourier.yaml b/unittest/force-styles/tests/angle-fourier.yaml index 61165c5a92..275d62beca 100644 --- a/unittest/force-styles/tests/angle-fourier.yaml +++ b/unittest/force-styles/tests/angle-fourier.yaml @@ -16,7 +16,11 @@ angle_coeff: ! | 3 50.0 0.0 0.0 1.0 4 100.0 0.3 0.3 0.3 equilibrium: 4 3.141592653589793 1.5707963267948966 1.5707963267948966 1.8234765819369754 -extract: ! "" +extract: ! | + k 1 + C0 1 + C1 1 + C2 1 natoms: 29 init_energy: 400.84036632010225 init_stress: ! |- From 11aa128951b2a3bbd7b89012c3e47bdfbd9c193b Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Sun, 22 Sep 2024 20:23:32 +0200 Subject: [PATCH 350/355] unit test for extract() in angle-fourier_simple.yaml --- unittest/force-styles/tests/angle-fourier_simple.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/unittest/force-styles/tests/angle-fourier_simple.yaml b/unittest/force-styles/tests/angle-fourier_simple.yaml index e1a394ee3a..bd72e67912 100644 --- a/unittest/force-styles/tests/angle-fourier_simple.yaml +++ b/unittest/force-styles/tests/angle-fourier_simple.yaml @@ -16,7 +16,10 @@ angle_coeff: ! | 3 50.0 1.0 3.0 4 100.0 -0.5 1.5 equilibrium: 4 3.141592653589793 1.5707963267948966 1.0471975511965976 2.0943951023931953 -extract: ! "" +extract: ! | + k 1 + C 1 + N 1 natoms: 29 init_energy: 2474.0748013590646 init_stress: ! |- From 6f6e365682f2b68a63dead1bb4e8d65c9796db92 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Sun, 22 Sep 2024 20:24:26 +0200 Subject: [PATCH 351/355] unit test for extract() in angle-cosine_periodic.yaml --- unittest/force-styles/tests/angle-cosine_periodic.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/unittest/force-styles/tests/angle-cosine_periodic.yaml b/unittest/force-styles/tests/angle-cosine_periodic.yaml index 5c8227fcbd..ee3e5c1469 100644 --- a/unittest/force-styles/tests/angle-cosine_periodic.yaml +++ b/unittest/force-styles/tests/angle-cosine_periodic.yaml @@ -15,7 +15,10 @@ angle_coeff: ! | 3 50.0 -1 3 4 100.0 -1 4 equilibrium: 4 3.141592653589793 3.141592653589793 2.0943951023931957 2.356194490192345 -extract: ! "" +extract: ! | + k 1 + b 1 + multiplicity 1 natoms: 29 init_energy: 1178.5476942873006 init_stress: ! |2- From 2429c89eae2d6f680c3b454911f4da9e76f1fadd Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Mon, 23 Sep 2024 14:59:51 -0600 Subject: [PATCH 352/355] Fix deadlock by always deallocating views of views in serial --- src/KOKKOS/pair_reaxff_kokkos.cpp | 15 ++++++++++++--- src/KOKKOS/pair_reaxff_kokkos.h | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/KOKKOS/pair_reaxff_kokkos.cpp b/src/KOKKOS/pair_reaxff_kokkos.cpp index 741d7f846e..d3c4214608 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.cpp +++ b/src/KOKKOS/pair_reaxff_kokkos.cpp @@ -105,7 +105,16 @@ PairReaxFFKokkos::~PairReaxFFKokkos() memoryKK->destroy_kokkos(k_tmpbo,tmpbo); tmpbo = nullptr; - // deallocate views of views in serial to prevent race condition in profiling tools + deallocate_views_of_views(); +} + +/* ---------------------------------------------------------------------- */ + +template +void PairReaxFFKokkos::deallocate_views_of_views() +{ + + // deallocate views of views in serial to prevent race conditions for (int i = 0; i < (int)k_LR.extent(0); i++) { for (int j = 0; j < (int)k_LR.extent(1); j++) { @@ -409,8 +418,8 @@ void PairReaxFFKokkos::init_md() int ntypes = atom->ntypes; Init_Lookup_Tables(); + deallocate_views_of_views(); k_LR = tdual_LR_lookup_table_kk_2d("lookup:LR",ntypes+1,ntypes+1); - d_LR = k_LR.template view(); for (int i = 1; i <= ntypes; ++i) { if (map[i] == -1) continue; @@ -1392,7 +1401,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeTabulatedLJCoulo const int tmin = MIN(itype, jtype); const int tmax = MAX(itype, jtype); - const LR_lookup_table_kk& t = d_LR(tmin,tmax); + const LR_lookup_table_kk& t = k_LR.template view()(tmin,tmax); /* Cubic Spline Interpolation */ diff --git a/src/KOKKOS/pair_reaxff_kokkos.h b/src/KOKKOS/pair_reaxff_kokkos.h index 0fe47fcba8..4c7127c17b 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.h +++ b/src/KOKKOS/pair_reaxff_kokkos.h @@ -384,6 +384,7 @@ class PairReaxFFKokkos : public PairReaxFF { F_FLOAT *fi, F_FLOAT *fj, F_FLOAT *fk, F_FLOAT *dril, F_FLOAT *drjl, F_FLOAT *drkl) const; protected: + void deallocate_views_of_views(); void allocate(); void allocate_array(); void setup(); @@ -497,7 +498,6 @@ class PairReaxFFKokkos : public PairReaxFF { typedef typename tdual_LR_lookup_table_kk_2d::t_dev t_LR_lookup_table_kk_2d; tdual_LR_lookup_table_kk_2d k_LR; - t_LR_lookup_table_kk_2d d_LR; DAT::tdual_int_2d k_tmpid; DAT::tdual_ffloat_2d k_tmpbo; From 2e05cfeea915891b245018a429dc456160da3ab8 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Mon, 23 Sep 2024 15:12:30 -0600 Subject: [PATCH 353/355] Small code cleanup --- src/KOKKOS/pair_pace_extrapolation_kokkos.cpp | 20 +++++++++---------- src/KOKKOS/pair_pace_extrapolation_kokkos.h | 2 ++ src/KOKKOS/pair_pace_kokkos.cpp | 20 +++++++++---------- src/KOKKOS/pair_pace_kokkos.h | 2 ++ 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp b/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp index e7d376c870..58ba8d6c1a 100644 --- a/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp +++ b/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp @@ -84,7 +84,15 @@ PairPACEExtrapolationKokkos::~PairPACEExtrapolationKokkos() memoryKK->destroy_kokkos(k_eatom,eatom); memoryKK->destroy_kokkos(k_vatom,vatom); - // deallocate views of views in serial to prevent issues in Kokkos tools + deallocate_views_of_views(); +} + +/* ---------------------------------------------------------------------- */ + +template +void PairPACEExtrapolationKokkos::deallocate_views_of_views() +{ + // deallocate views of views in serial to prevent race conditions if (k_splines_gk.h_view.data()) { for (int i = 0; i < nelements; i++) { @@ -244,15 +252,7 @@ void PairPACEExtrapolationKokkos::copy_splines() { auto basis_set = aceimpl->basis_set; - if (k_splines_gk.d_view.data()) { - for (int i = 0; i < nelements; i++) { - for (int j = 0; j < nelements; j++) { - k_splines_gk.h_view(i, j).deallocate(); - k_splines_rnl.h_view(i, j).deallocate(); - k_splines_hc.h_view(i, j).deallocate(); - } - } - } + deallocate_views_of_views(); k_splines_gk = Kokkos::DualView("pace:splines_gk", nelements, nelements); k_splines_rnl = Kokkos::DualView("pace:splines_rnl", nelements, nelements); diff --git a/src/KOKKOS/pair_pace_extrapolation_kokkos.h b/src/KOKKOS/pair_pace_extrapolation_kokkos.h index df8a0c1740..c1c1debd45 100644 --- a/src/KOKKOS/pair_pace_extrapolation_kokkos.h +++ b/src/KOKKOS/pair_pace_extrapolation_kokkos.h @@ -296,6 +296,8 @@ class PairPACEExtrapolationKokkos : public PairPACEExtrapolation { t_ace_3d3 f_ij; + void deallocate_views_of_views(); + public: struct SplineInterpolatorKokkos { int ntot, nlut, num_of_functions; diff --git a/src/KOKKOS/pair_pace_kokkos.cpp b/src/KOKKOS/pair_pace_kokkos.cpp index 4407d1231e..8d05c26239 100644 --- a/src/KOKKOS/pair_pace_kokkos.cpp +++ b/src/KOKKOS/pair_pace_kokkos.cpp @@ -84,7 +84,15 @@ PairPACEKokkos::~PairPACEKokkos() memoryKK->destroy_kokkos(k_eatom,eatom); memoryKK->destroy_kokkos(k_vatom,vatom); - // deallocate views of views in serial to prevent issues in Kokkos tools + deallocate_views_of_views(); +} + +/* ---------------------------------------------------------------------- */ + +template +void PairPACEKokkos::deallocate_views_of_views() +{ + // deallocate views of views in serial to prevent race conditions if (k_splines_gk.h_view.data()) { for (int i = 0; i < nelements; i++) { @@ -240,15 +248,7 @@ void PairPACEKokkos::copy_splines() { auto basis_set = aceimpl->basis_set; - if (k_splines_gk.d_view.data()) { - for (int i = 0; i < nelements; i++) { - for (int j = 0; j < nelements; j++) { - k_splines_gk.h_view(i, j).deallocate(); - k_splines_rnl.h_view(i, j).deallocate(); - k_splines_hc.h_view(i, j).deallocate(); - } - } - } + deallocate_views_of_views(); k_splines_gk = Kokkos::DualView("pace:splines_gk", nelements, nelements); k_splines_rnl = Kokkos::DualView("pace:splines_rnl", nelements, nelements); diff --git a/src/KOKKOS/pair_pace_kokkos.h b/src/KOKKOS/pair_pace_kokkos.h index e22c61f0ea..6b43e52614 100644 --- a/src/KOKKOS/pair_pace_kokkos.h +++ b/src/KOKKOS/pair_pace_kokkos.h @@ -283,6 +283,8 @@ class PairPACEKokkos : public PairPACE { t_ace_3d3 f_ij; + void deallocate_views_of_views(); + public: struct SplineInterpolatorKokkos { int ntot, nlut, num_of_functions; From e95de835c05cf3d43d78677b57b8e759f0e6d8a4 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Mon, 23 Sep 2024 15:38:29 -0600 Subject: [PATCH 354/355] Tweak build defaults --- cmake/Modules/Packages/KOKKOS.cmake | 21 +++++++++++---------- lib/kokkos/Makefile.kokkos | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/cmake/Modules/Packages/KOKKOS.cmake b/cmake/Modules/Packages/KOKKOS.cmake index 691be4cd67..adb3abab6b 100644 --- a/cmake/Modules/Packages/KOKKOS.cmake +++ b/cmake/Modules/Packages/KOKKOS.cmake @@ -8,8 +8,6 @@ endif() ######################################################################## # consistency checks and Kokkos options/settings required by LAMMPS if(Kokkos_ENABLE_CUDA) - message(STATUS "KOKKOS: Enabling CUDA LAMBDA function support") - set(Kokkos_ENABLE_CUDA_LAMBDA ON CACHE BOOL "" FORCE) option(Kokkos_ENABLE_IMPL_CUDA_MALLOC_ASYNC "CUDA asynchronous malloc support" OFF) mark_as_advanced(Kokkos_ENABLE_IMPL_CUDA_MALLOC_ASYNC) if(Kokkos_ENABLE_IMPL_CUDA_MALLOC_ASYNC) @@ -19,12 +17,15 @@ if(Kokkos_ENABLE_CUDA) endif() endif() if(Kokkos_ENABLE_HIP) - option(KOKKOS_ENABLE_IMPL_HIP_UNIFIED_MEMORY "Enable unified memory with HIP" ON) - mark_as_advanced(KOKKOS_ENABLE_IMPL_HIP_UNIFIED_MEMORY) - option(KOKKOS_ENABLE_HIP_MULTIPLE_KERNEL_INSTANTIATIONS "Enable multiple kernel instantiations with HIP" ON) - mark_as_advanced(KOKKOS_ENABLE_HIP_MULTIPLE_KERNEL_INSTANTIATIONS) - option(KOKKOS_ENABLE_ROCTHRUST "Use RoCThrust library" ON) - mark_as_advanced(KOKKOS_ENABLE_ROCTHRUST) + option(Kokkos_ENABLE_HIP_MULTIPLE_KERNEL_INSTANTIATIONS "Enable multiple kernel instantiations with HIP" ON) + mark_as_advanced(Kokkos_ENABLE_HIP_MULTIPLE_KERNEL_INSTANTIATIONS) + option(Kokkos_ENABLE_ROCTHRUST "Use RoCThrust library" ON) + mark_as_advanced(Kokkos_ENABLE_ROCTHRUST) + + if(Kokkos_ARCH_AMD_GFX942 OR Kokkos_ARCH_AMD_GFX940) + option(Kokkos_ENABLE_IMPL_HIP_UNIFIED_MEMORY "Enable unified memory with HIP" ON) + mark_as_advanced(Kokkos_ENABLE_IMPL_HIP_UNIFIED_MEMORY) + endif() endif() # Adding OpenMP compiler flags without the checks done for # BUILD_OMP can result in compile failures. Enforce consistency. @@ -38,8 +39,8 @@ if(Kokkos_ENABLE_SERIAL) if(NOT (Kokkos_ENABLE_OPENMP OR Kokkos_ENABLE_THREADS OR Kokkos_ENABLE_CUDA OR Kokkos_ENABLE_HIP OR Kokkos_ENABLE_SYCL OR Kokkos_ENABLE_OPENMPTARGET)) - message(STATUS "KOKKOS: Disabling atomics for Serial Backend") - set(Kokkos_ENABLE_ATOMICS_BYPASS ON CACHE BOOL "" FORCE) + option(Kokkos_ENABLE_ATOMICS_BYPASS "Disable atomics for Kokkos Serial Backend" ON) + mark_as_advanced(Kokkos_ENABLE_ATOMICS_BYPASS) endif() endif() ######################################################################## diff --git a/lib/kokkos/Makefile.kokkos b/lib/kokkos/Makefile.kokkos index eb059d9b81..eb95c5448d 100644 --- a/lib/kokkos/Makefile.kokkos +++ b/lib/kokkos/Makefile.kokkos @@ -41,7 +41,7 @@ KOKKOS_STANDALONE_CMAKE ?= "no" # Default settings specific options. # Options: force_uvm,use_ldg,rdc,enable_lambda,enable_constexpr,disable_malloc_async -KOKKOS_CUDA_OPTIONS ?= "enable_lambda,disable_malloc_async" +KOKKOS_CUDA_OPTIONS ?= "disable_malloc_async" # Options: rdc KOKKOS_HIP_OPTIONS ?= "" From 7e31a4f482014781fa4eb41e92205e151e2ec00e Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Mon, 23 Sep 2024 16:14:06 -0600 Subject: [PATCH 355/355] Simplify view deallocation code --- src/KOKKOS/fix_acks2_reaxff_kokkos.cpp | 4 +- src/KOKKOS/fix_qeq_reaxff_kokkos.cpp | 2 +- src/KOKKOS/fix_shake_kokkos.cpp | 4 +- src/KOKKOS/meam_dens_init_kokkos.h | 26 +++++------ src/KOKKOS/meam_force_kokkos.h | 6 +-- src/KOKKOS/pair_adp_kokkos.cpp | 12 ++--- src/KOKKOS/pair_dpd_ext_kokkos.cpp | 6 +-- src/KOKKOS/pair_dpd_ext_tstat_kokkos.cpp | 4 +- src/KOKKOS/pair_dpd_kokkos.cpp | 6 +-- src/KOKKOS/pair_dpd_tstat_kokkos.cpp | 4 +- src/KOKKOS/pair_eam_alloy_kokkos.cpp | 8 ++-- src/KOKKOS/pair_eam_fs_kokkos.cpp | 8 ++-- src/KOKKOS/pair_eam_kokkos.cpp | 8 ++-- src/KOKKOS/pair_pace_extrapolation_kokkos.cpp | 4 +- src/KOKKOS/pair_pace_kokkos.cpp | 4 +- src/KOKKOS/pair_reaxff_kokkos.cpp | 44 +++++++++---------- src/KOKKOS/pair_snap_kokkos_impl.h | 4 +- src/KOKKOS/pair_sw_kokkos.cpp | 6 +-- src/KOKKOS/pair_tersoff_kokkos.cpp | 6 +-- src/KOKKOS/pair_tersoff_mod_kokkos.cpp | 6 +-- src/KOKKOS/pair_tersoff_zbl_kokkos.cpp | 6 +-- 21 files changed, 89 insertions(+), 89 deletions(-) diff --git a/src/KOKKOS/fix_acks2_reaxff_kokkos.cpp b/src/KOKKOS/fix_acks2_reaxff_kokkos.cpp index c0b263d736..bfcb66e525 100644 --- a/src/KOKKOS/fix_acks2_reaxff_kokkos.cpp +++ b/src/KOKKOS/fix_acks2_reaxff_kokkos.cpp @@ -365,7 +365,7 @@ void FixACKS2ReaxFFKokkos::pre_force(int /*vflag*/) // free duplicated memory - dup_X_diag = decltype(dup_X_diag)(); + dup_X_diag = {}; } if (neighflag != FULL) { @@ -1419,7 +1419,7 @@ void FixACKS2ReaxFFKokkos::sparse_matvec_acks2(typename AT::t_ffloat // free duplicated memory - dup_bb = decltype(dup_bb)(); + dup_bb = {}; } } diff --git a/src/KOKKOS/fix_qeq_reaxff_kokkos.cpp b/src/KOKKOS/fix_qeq_reaxff_kokkos.cpp index f93f6cb70e..7ef4505b06 100644 --- a/src/KOKKOS/fix_qeq_reaxff_kokkos.cpp +++ b/src/KOKKOS/fix_qeq_reaxff_kokkos.cpp @@ -291,7 +291,7 @@ void FixQEqReaxFFKokkos::pre_force(int /*vflag*/) // free duplicated memory if (need_dup) - dup_o = decltype(dup_o)(); + dup_o = {}; atomKK->modified(execution_space,datamask_modify); diff --git a/src/KOKKOS/fix_shake_kokkos.cpp b/src/KOKKOS/fix_shake_kokkos.cpp index 47f932d8f2..52826d7b04 100644 --- a/src/KOKKOS/fix_shake_kokkos.cpp +++ b/src/KOKKOS/fix_shake_kokkos.cpp @@ -459,8 +459,8 @@ void FixShakeKokkos::post_force(int vflag) // free duplicated memory if (need_dup) { - dup_f = decltype(dup_f)(); - dup_vatom = decltype(dup_vatom)(); + dup_f = {}; + dup_vatom = {}; } } diff --git a/src/KOKKOS/meam_dens_init_kokkos.h b/src/KOKKOS/meam_dens_init_kokkos.h index 68e69430fd..dd63be96bd 100644 --- a/src/KOKKOS/meam_dens_init_kokkos.h +++ b/src/KOKKOS/meam_dens_init_kokkos.h @@ -294,20 +294,20 @@ MEAMKokkos::meam_dens_init(int inum_half, int ntype, typename AT::t_ Kokkos::Experimental::contribute(d_arho3mb, dup_arho3mb); // free duplicated memory - dup_rho0 = decltype(dup_rho0)(); - dup_arho2b = decltype(dup_arho2b)(); - dup_arho1 = decltype(dup_arho1)(); - dup_arho2 = decltype(dup_arho2)(); - dup_arho3 = decltype(dup_arho3)(); - dup_arho3b = decltype(dup_arho3b)(); - dup_t_ave = decltype(dup_t_ave)(); - dup_tsq_ave = decltype(dup_tsq_ave)(); + dup_rho0 = {}; + dup_arho2b = {}; + dup_arho1 = {}; + dup_arho2 = {}; + dup_arho3 = {}; + dup_arho3b = {}; + dup_t_ave = {}; + dup_tsq_ave = {}; // msmeam - dup_arho2mb = decltype(dup_arho2mb)(); - dup_arho1m = decltype(dup_arho1m)(); - dup_arho2m = decltype(dup_arho2m)(); - dup_arho3m = decltype(dup_arho3m)(); - dup_arho3mb = decltype(dup_arho3mb)(); + dup_arho2mb = {}; + dup_arho1m = {}; + dup_arho2m = {}; + dup_arho3m = {}; + dup_arho3mb = {}; } } diff --git a/src/KOKKOS/meam_force_kokkos.h b/src/KOKKOS/meam_force_kokkos.h index a546ab54d4..1875e22dcf 100644 --- a/src/KOKKOS/meam_force_kokkos.h +++ b/src/KOKKOS/meam_force_kokkos.h @@ -75,9 +75,9 @@ void MEAMKokkos::meam_force( if (vflag_atom) Kokkos::Experimental::contribute(d_vatom, dup_vatom); // free duplicated memory - dup_f = decltype(dup_f)(); - if (eflag_atom) dup_eatom = decltype(dup_eatom)(); - if (vflag_atom) dup_vatom = decltype(dup_vatom)(); + dup_f = {}; + if (eflag_atom) dup_eatom = {}; + if (vflag_atom) dup_vatom = {}; } } diff --git a/src/KOKKOS/pair_adp_kokkos.cpp b/src/KOKKOS/pair_adp_kokkos.cpp index 1297d62651..999a67ca49 100644 --- a/src/KOKKOS/pair_adp_kokkos.cpp +++ b/src/KOKKOS/pair_adp_kokkos.cpp @@ -297,12 +297,12 @@ void PairADPKokkos::compute(int eflag_in, int vflag_in) // free duplicated memory if (need_dup) { - dup_rho = decltype(dup_rho)(); - dup_mu = decltype(dup_mu)(); - dup_lambda = decltype(dup_lambda)(); - dup_f = decltype(dup_f)(); - dup_eatom = decltype(dup_eatom)(); - dup_vatom = decltype(dup_vatom)(); + dup_rho = {}; + dup_mu = {}; + dup_lambda = {}; + dup_f = {}; + dup_eatom = {}; + dup_vatom = {}; } } diff --git a/src/KOKKOS/pair_dpd_ext_kokkos.cpp b/src/KOKKOS/pair_dpd_ext_kokkos.cpp index 636235d1c8..95c9d304f3 100644 --- a/src/KOKKOS/pair_dpd_ext_kokkos.cpp +++ b/src/KOKKOS/pair_dpd_ext_kokkos.cpp @@ -207,9 +207,9 @@ void PairDPDExtKokkos::compute(int eflagin, int vflagin) // free duplicated memory if (need_dup) { - dup_f = decltype(dup_f)(); - dup_eatom = decltype(dup_eatom)(); - dup_vatom = decltype(dup_vatom)(); + dup_f = {}; + dup_eatom = {}; + dup_vatom = {}; } } diff --git a/src/KOKKOS/pair_dpd_ext_tstat_kokkos.cpp b/src/KOKKOS/pair_dpd_ext_tstat_kokkos.cpp index 213b344fbb..91d1183957 100644 --- a/src/KOKKOS/pair_dpd_ext_tstat_kokkos.cpp +++ b/src/KOKKOS/pair_dpd_ext_tstat_kokkos.cpp @@ -212,8 +212,8 @@ void PairDPDExtTstatKokkos::compute(int eflagin, int vflagin) // free duplicated memory if (need_dup) { - dup_f = decltype(dup_f)(); - dup_vatom = decltype(dup_vatom)(); + dup_f = {}; + dup_vatom = {}; } } diff --git a/src/KOKKOS/pair_dpd_kokkos.cpp b/src/KOKKOS/pair_dpd_kokkos.cpp index f888b5f6ce..0ebf8ccae0 100644 --- a/src/KOKKOS/pair_dpd_kokkos.cpp +++ b/src/KOKKOS/pair_dpd_kokkos.cpp @@ -207,9 +207,9 @@ void PairDPDKokkos::compute(int eflagin, int vflagin) // free duplicated memory if (need_dup) { - dup_f = decltype(dup_f)(); - dup_eatom = decltype(dup_eatom)(); - dup_vatom = decltype(dup_vatom)(); + dup_f = {}; + dup_eatom = {}; + dup_vatom = {}; } } diff --git a/src/KOKKOS/pair_dpd_tstat_kokkos.cpp b/src/KOKKOS/pair_dpd_tstat_kokkos.cpp index 63dbda3b59..d51cce629a 100644 --- a/src/KOKKOS/pair_dpd_tstat_kokkos.cpp +++ b/src/KOKKOS/pair_dpd_tstat_kokkos.cpp @@ -211,8 +211,8 @@ void PairDPDTstatKokkos::compute(int eflagin, int vflagin) // free duplicated memory if (need_dup) { - dup_f = decltype(dup_f)(); - dup_vatom = decltype(dup_vatom)(); + dup_f = {}; + dup_vatom = {}; } } diff --git a/src/KOKKOS/pair_eam_alloy_kokkos.cpp b/src/KOKKOS/pair_eam_alloy_kokkos.cpp index b02faced1e..90a82616a6 100644 --- a/src/KOKKOS/pair_eam_alloy_kokkos.cpp +++ b/src/KOKKOS/pair_eam_alloy_kokkos.cpp @@ -309,10 +309,10 @@ void PairEAMAlloyKokkos::compute(int eflag_in, int vflag_in) // free duplicated memory if (need_dup) { - dup_rho = decltype(dup_rho)(); - dup_f = decltype(dup_f)(); - dup_eatom = decltype(dup_eatom)(); - dup_vatom = decltype(dup_vatom)(); + dup_rho = {}; + dup_f = {}; + dup_eatom = {}; + dup_vatom = {}; } } diff --git a/src/KOKKOS/pair_eam_fs_kokkos.cpp b/src/KOKKOS/pair_eam_fs_kokkos.cpp index 4da146e68e..11719a8979 100644 --- a/src/KOKKOS/pair_eam_fs_kokkos.cpp +++ b/src/KOKKOS/pair_eam_fs_kokkos.cpp @@ -309,10 +309,10 @@ void PairEAMFSKokkos::compute(int eflag_in, int vflag_in) // free duplicated memory if (need_dup) { - dup_rho = decltype(dup_rho)(); - dup_f = decltype(dup_f)(); - dup_eatom = decltype(dup_eatom)(); - dup_vatom = decltype(dup_vatom)(); + dup_rho = {}; + dup_f = {}; + dup_eatom = {}; + dup_vatom = {}; } } diff --git a/src/KOKKOS/pair_eam_kokkos.cpp b/src/KOKKOS/pair_eam_kokkos.cpp index 54ffa84f2d..1e870555dc 100644 --- a/src/KOKKOS/pair_eam_kokkos.cpp +++ b/src/KOKKOS/pair_eam_kokkos.cpp @@ -304,10 +304,10 @@ void PairEAMKokkos::compute(int eflag_in, int vflag_in) // free duplicated memory if (need_dup) { - dup_rho = decltype(dup_rho)(); - dup_f = decltype(dup_f)(); - dup_eatom = decltype(dup_eatom)(); - dup_vatom = decltype(dup_vatom)(); + dup_rho = {}; + dup_f = {}; + dup_eatom = {}; + dup_vatom = {}; } } diff --git a/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp b/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp index 58ba8d6c1a..746055f28c 100644 --- a/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp +++ b/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp @@ -808,8 +808,8 @@ void PairPACEExtrapolationKokkos::compute(int eflag_in, int vflag_in // free duplicated memory if (need_dup) { - dup_f = decltype(dup_f)(); - dup_vatom = decltype(dup_vatom)(); + dup_f = {}; + dup_vatom = {}; } } diff --git a/src/KOKKOS/pair_pace_kokkos.cpp b/src/KOKKOS/pair_pace_kokkos.cpp index 8d05c26239..0afbb7540e 100644 --- a/src/KOKKOS/pair_pace_kokkos.cpp +++ b/src/KOKKOS/pair_pace_kokkos.cpp @@ -753,8 +753,8 @@ void PairPACEKokkos::compute(int eflag_in, int vflag_in) // free duplicated memory if (need_dup) { - dup_f = decltype(dup_f)(); - dup_vatom = decltype(dup_vatom)(); + dup_f = {}; + dup_vatom = {}; } } diff --git a/src/KOKKOS/pair_reaxff_kokkos.cpp b/src/KOKKOS/pair_reaxff_kokkos.cpp index d3c4214608..b0a53a27fd 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.cpp +++ b/src/KOKKOS/pair_reaxff_kokkos.cpp @@ -118,10 +118,10 @@ void PairReaxFFKokkos::deallocate_views_of_views() for (int i = 0; i < (int)k_LR.extent(0); i++) { for (int j = 0; j < (int)k_LR.extent(1); j++) { - k_LR.h_view(i,j).d_vdW = decltype(k_LR.h_view(i,j).d_vdW )(); - k_LR.h_view(i,j).d_CEvd = decltype(k_LR.h_view(i,j).d_CEvd )(); - k_LR.h_view(i,j).d_ele = decltype(k_LR.h_view(i,j).d_ele )(); - k_LR.h_view(i,j).d_CEclmb = decltype(k_LR.h_view(i,j).d_CEclmb)(); + k_LR.h_view(i,j).d_vdW = {}; + k_LR.h_view(i,j).d_CEvd = {}; + k_LR.h_view(i,j).d_ele = {}; + k_LR.h_view(i,j).d_CEclmb = {}; } } } @@ -1101,19 +1101,19 @@ void PairReaxFFKokkos::compute(int eflag_in, int vflag_in) // free scatterview memory if (need_dup) { - dup_f = decltype(dup_f)(); - dup_eatom = decltype(dup_eatom)(); - dup_vatom = decltype(dup_vatom)(); - dup_dDeltap_self = decltype(dup_dDeltap_self)(); - dup_total_bo = decltype(dup_total_bo)(); - dup_CdDelta = decltype(dup_CdDelta)(); + dup_f = {}; + dup_eatom = {}; + dup_vatom = {}; + dup_dDeltap_self = {}; + dup_total_bo = {}; + dup_CdDelta = {}; } else { - ndup_f = decltype(ndup_f)(); - ndup_eatom = decltype(ndup_eatom)(); - ndup_vatom = decltype(ndup_vatom)(); - ndup_dDeltap_self = decltype(ndup_dDeltap_self)(); - ndup_total_bo = decltype(ndup_total_bo)(); - ndup_CdDelta = decltype(ndup_CdDelta)(); + ndup_f = {}; + ndup_eatom = {}; + ndup_vatom = {}; + ndup_dDeltap_self = {}; + ndup_total_bo = {}; + ndup_CdDelta = {}; } d_neighbors = typename AT::t_neighbors_2d(); @@ -1501,13 +1501,13 @@ void PairReaxFFKokkos::allocate_array() { // free scatterview memory if (need_dup) { - dup_dDeltap_self = decltype(dup_dDeltap_self)(); - dup_total_bo = decltype(dup_total_bo)(); - dup_CdDelta = decltype(dup_CdDelta)(); + dup_dDeltap_self = {}; + dup_total_bo = {}; + dup_CdDelta = {}; } else { - ndup_dDeltap_self = decltype(ndup_dDeltap_self)(); - ndup_total_bo = decltype(ndup_total_bo)(); - ndup_CdDelta = decltype(ndup_CdDelta)(); + ndup_dDeltap_self = {}; + ndup_total_bo = {}; + ndup_CdDelta = {}; } if (cut_hbsq > 0.0) { diff --git a/src/KOKKOS/pair_snap_kokkos_impl.h b/src/KOKKOS/pair_snap_kokkos_impl.h index 839240c62f..97c7d17ea9 100644 --- a/src/KOKKOS/pair_snap_kokkos_impl.h +++ b/src/KOKKOS/pair_snap_kokkos_impl.h @@ -525,8 +525,8 @@ void PairSNAPKokkos::compute(int eflag_in, // free duplicated memory if (need_dup) { - dup_f = decltype(dup_f)(); - dup_vatom = decltype(dup_vatom)(); + dup_f = {}; + dup_vatom = {}; } } diff --git a/src/KOKKOS/pair_sw_kokkos.cpp b/src/KOKKOS/pair_sw_kokkos.cpp index 01b856a7b5..d62af5a78f 100644 --- a/src/KOKKOS/pair_sw_kokkos.cpp +++ b/src/KOKKOS/pair_sw_kokkos.cpp @@ -186,9 +186,9 @@ void PairSWKokkos::compute(int eflag_in, int vflag_in) // free duplicated memory if (need_dup) { - dup_f = decltype(dup_f)(); - dup_eatom = decltype(dup_eatom)(); - dup_vatom = decltype(dup_vatom)(); + dup_f = {}; + dup_eatom = {}; + dup_vatom = {}; } } diff --git a/src/KOKKOS/pair_tersoff_kokkos.cpp b/src/KOKKOS/pair_tersoff_kokkos.cpp index 1a0d45e435..c2099f95b5 100644 --- a/src/KOKKOS/pair_tersoff_kokkos.cpp +++ b/src/KOKKOS/pair_tersoff_kokkos.cpp @@ -293,9 +293,9 @@ void PairTersoffKokkos::compute(int eflag_in, int vflag_in) // free duplicated memory if (need_dup) { - dup_f = decltype(dup_f)(); - dup_eatom = decltype(dup_eatom)(); - dup_vatom = decltype(dup_vatom)(); + dup_f = {}; + dup_eatom = {}; + dup_vatom = {}; } } diff --git a/src/KOKKOS/pair_tersoff_mod_kokkos.cpp b/src/KOKKOS/pair_tersoff_mod_kokkos.cpp index b941755d4b..3e651f1433 100644 --- a/src/KOKKOS/pair_tersoff_mod_kokkos.cpp +++ b/src/KOKKOS/pair_tersoff_mod_kokkos.cpp @@ -283,9 +283,9 @@ void PairTersoffMODKokkos::compute(int eflag_in, int vflag_in) // free duplicated memory if (need_dup) { - dup_f = decltype(dup_f)(); - dup_eatom = decltype(dup_eatom)(); - dup_vatom = decltype(dup_vatom)(); + dup_f = {}; + dup_eatom = {}; + dup_vatom = {}; } } diff --git a/src/KOKKOS/pair_tersoff_zbl_kokkos.cpp b/src/KOKKOS/pair_tersoff_zbl_kokkos.cpp index 08d6cb17d7..3d6d1ea27b 100644 --- a/src/KOKKOS/pair_tersoff_zbl_kokkos.cpp +++ b/src/KOKKOS/pair_tersoff_zbl_kokkos.cpp @@ -296,9 +296,9 @@ void PairTersoffZBLKokkos::compute(int eflag_in, int vflag_in) // free duplicated memory if (need_dup) { - dup_f = decltype(dup_f)(); - dup_eatom = decltype(dup_eatom)(); - dup_vatom = decltype(dup_vatom)(); + dup_f = {}; + dup_eatom = {}; + dup_vatom = {}; } }

WtlT6#uny@ECb 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 diff --git a/tools/lammps-gui/TODO.md b/tools/lammps-gui/TODO.md index e3782e6446..2c190dc907 100644 --- a/tools/lammps-gui/TODO.md +++ b/tools/lammps-gui/TODO.md @@ -6,8 +6,9 @@ LAMMPS-GUI TODO list: - figure out stacking order of frames and whether it can be more flexible - implement a timed "Auto-Save" feature that saves after some idle time. set timeout in Editor preferences. - add a "Colors" menu to the image viewer to adjust color settings for the - current image (unlike the defaults in the perferences). Support color by - property (e.g. scan computes or fixes with per-atom data), define colormaps etc. + current image (unlike the defaults in the perferences) including assigning + colors to individual atom types. +- Support color by property (e.g. scan computes or fixes with per-atom data), define colormaps etc. - implement indenting regions for (nested) loops? - implement data file manager GUI with the following features: From 3c2fba111226a423ab694d4c4e322b9a6ae781b0 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 25 Aug 2024 00:17:17 -0400 Subject: [PATCH 076/355] add support for extracting the current lattice spacing to library interface --- src/library.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/library.cpp b/src/library.cpp index 097cffd68a..560c26aaaa 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -35,6 +35,7 @@ #include "group.h" #include "info.h" #include "input.h" +#include "lattice.h" #include "lmppython.h" #include "memory.h" #include "modify.h" @@ -1414,6 +1415,9 @@ int lammps_extract_global_datatype(void * /*handle*/, const char *name) if (strcmp(name,"xy") == 0) return LAMMPS_DOUBLE; if (strcmp(name,"xz") == 0) return LAMMPS_DOUBLE; if (strcmp(name,"yz") == 0) return LAMMPS_DOUBLE; + if (strcmp(name,"xlattice") == 0) return LAMMPS_DOUBLE; + if (strcmp(name,"ylattice") == 0) return LAMMPS_DOUBLE; + if (strcmp(name,"zlattice") == 0) return LAMMPS_DOUBLE; if (strcmp(name,"procgrid") == 0) return LAMMPS_INT; if (strcmp(name,"natoms") == 0) return LAMMPS_BIGINT; @@ -1649,6 +1653,18 @@ report the "native" data type. The following tables are provided: - double - 1 - triclinic tilt factor; see :doc:`Howto_triclinic`. + * - xlattice + - double + - 1 + - lattice spacing in x-direction; see :doc:`lattice `. + * - ylattice + - double + - 1 + - lattice spacing in y-direction; see :doc:`lattice `. + * - zlattice + - double + - 1 + - lattice spacing in z-direction; see :doc:`lattice `. * - procgrid - int - 3 @@ -1917,6 +1933,9 @@ void *lammps_extract_global(void *handle, const char *name) if (strcmp(name,"xy") == 0) return (void *) &lmp->domain->xy; if (strcmp(name,"xz") == 0) return (void *) &lmp->domain->xz; if (strcmp(name,"yz") == 0) return (void *) &lmp->domain->yz; + if (strcmp(name,"xlattice") == 0) return (void *) &lmp->domain->lattice->xlattice; + if (strcmp(name,"ylattice") == 0) return (void *) &lmp->domain->lattice->ylattice; + if (strcmp(name,"zlattice") == 0) return (void *) &lmp->domain->lattice->zlattice; if (((lmp->comm->layout == Comm::LAYOUT_UNIFORM) || (lmp->comm->layout == Comm::LAYOUT_NONUNIFORM)) && (strcmp(name,"procgrid") == 0)) return (void *) &lmp->comm->procgrid; From ac91b4fbb333c5f28308505fbbe4f595869cd044 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 25 Aug 2024 00:21:07 -0400 Subject: [PATCH 077/355] add field to set atom size if not determined otherwise. seed by lattice spacing --- tools/lammps-gui/imageviewer.cpp | 48 ++++++++++++++++++++++++++++---- tools/lammps-gui/imageviewer.h | 4 ++- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/tools/lammps-gui/imageviewer.cpp b/tools/lammps-gui/imageviewer.cpp index 725d557606..b1c4207962 100644 --- a/tools/lammps-gui/imageviewer.cpp +++ b/tools/lammps-gui/imageviewer.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -135,9 +137,10 @@ static const QString blank(" "); ImageViewer::ImageViewer(const QString &fileName, LammpsWrapper *_lammps, QWidget *parent) : QDialog(parent), menuBar(new QMenuBar), imageLabel(new QLabel), scrollArea(new QScrollArea), - saveAsAct(nullptr), copyAct(nullptr), cmdAct(nullptr), zoomInAct(nullptr), zoomOutAct(nullptr), - normalSizeAct(nullptr), lammps(_lammps), group("all"), filename(fileName), useelements(false), - usediameter(false), usesigma(false) + buttonBox(nullptr), scaleFactor(1.0), atomSize(1.0), saveAsAct(nullptr), copyAct(nullptr), + cmdAct(nullptr), zoomInAct(nullptr), zoomOutAct(nullptr), normalSizeAct(nullptr), + lammps(_lammps), group("all"), filename(fileName), useelements(false), usediameter(false), + usesigma(false) { imageLabel->setBackgroundRole(QPalette::Base); imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); @@ -163,6 +166,11 @@ ImageViewer::ImageViewer(const QString &fileName, LammpsWrapper *_lammps, QWidge renderstatus->setEnabled(false); renderstatus->setToolTip("Render status"); renderstatus->setObjectName("renderstatus"); + auto *asize = new QLineEdit(QString::number(atomSize)); + auto *valid = new QDoubleValidator(1.0e-10, 1.0e10, 10, asize); + asize->setValidator(valid); + asize->setObjectName("atomSize"); + asize->setEnabled(false); settings.beginGroup("snapshot"); auto *xval = new QSpinBox; xval->setRange(100, 10000); @@ -179,6 +187,7 @@ ImageViewer::ImageViewer(const QString &fileName, LammpsWrapper *_lammps, QWidge yval->setToolTip("Set rendered image height"); yval->setMinimumSize(bsize); settings.endGroup(); + connect(asize, &QLineEdit::editingFinished, this, &ImageViewer::set_atom_size); connect(xval, &QAbstractSpinBox::editingFinished, this, &ImageViewer::edit_size); connect(yval, &QAbstractSpinBox::editingFinished, this, &ImageViewer::edit_size); @@ -249,6 +258,8 @@ ImageViewer::ImageViewer(const QString &fileName, LammpsWrapper *_lammps, QWidge menuLayout->addWidget(menuBar); menuLayout->addWidget(renderstatus); + menuLayout->addWidget(new QLabel(" Atom Size: ")); + menuLayout->addWidget(asize); menuLayout->addWidget(new QLabel(" Width: ")); menuLayout->addWidget(xval); menuLayout->addWidget(new QLabel(" Height: ")); @@ -307,7 +318,7 @@ ImageViewer::ImageViewer(const QString &fileName, LammpsWrapper *_lammps, QWidge doanti->setChecked(antialias); scaleFactor = 1.0; - resize(image.width() + 20, image.height() + 75); + resize(image.width() + 25, image.height() + 80); scrollArea->setVisible(true); updateActions(); @@ -356,6 +367,13 @@ void ImageViewer::reset_view() createImage(); } +void ImageViewer::set_atom_size() +{ + auto *field = qobject_cast(sender()); + atomSize = field->text().toDouble(); + createImage(); +} + void ImageViewer::edit_size() { auto *field = qobject_cast(sender()); @@ -560,10 +578,27 @@ void ImageViewer::createImage() if (useelements || usediameter || usesigma) { auto *button = findChild("vdw"); if (button) button->setEnabled(true); + auto *edit = findChild("atomSize"); + if (edit) edit->setEnabled(false); } else { adiams.clear(); auto *button = findChild("vdw"); if (button) button->setEnabled(false); + auto *edit = findChild("atomSize"); + if (edit) { + if (!edit->isEnabled()) { + edit->setEnabled(true); + // initialize with lattice spacing + auto *xlattice = (const double *)lammps->extract_global("xlattice"); + if (xlattice) atomSize = *xlattice; + edit->setText(QString::number(atomSize)); + } + atomSize = edit->text().toDouble(); + } + if (atomSize != 1.0) { + for (int i = 1; i <= ntypes; ++i) + adiams += QString("adiam %1 %2 ").arg(i).arg(atomSize); + } } // color @@ -607,6 +642,7 @@ void ImageViewer::createImage() dumpcmd += " backcolor " + settings.value("background", "black").toString(); if (useelements) dumpcmd += blank + elements + blank + adiams + blank; if (usesigma) dumpcmd += blank + adiams + blank; + if (!useelements && !usesigma && (atomSize != 1.0)) dumpcmd += blank + adiams + blank; settings.endGroup(); last_dump_cmd = dumpcmd; @@ -617,10 +653,10 @@ void ImageViewer::createImage() const QImage newImage = reader.read(); dumpfile.remove(); - // read of new image failed. Don't try to scale and load it. + // read of new image failed. nothing left to do. if (newImage.isNull()) return; - // scale back to achieve antialiasing + // show show image image = newImage; imageLabel->setPixmap(QPixmap::fromImage(image)); imageLabel->adjustSize(); diff --git a/tools/lammps-gui/imageviewer.h b/tools/lammps-gui/imageviewer.h index 94632bde89..0c175bd03f 100644 --- a/tools/lammps-gui/imageviewer.h +++ b/tools/lammps-gui/imageviewer.h @@ -42,6 +42,7 @@ private slots: void copy(); void quit(); + void set_atom_size(); void edit_size(); void reset_view(); void toggle_ssao(); @@ -76,7 +77,8 @@ private: QLabel *imageLabel; QScrollArea *scrollArea; QDialogButtonBox *buttonBox; - double scaleFactor = 1.0; + double scaleFactor; + double atomSize; QAction *saveAsAct; QAction *copyAct; From 6c13e8d0537f210442a0f212fc364710cd777e11 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 25 Aug 2024 00:41:26 -0400 Subject: [PATCH 078/355] tweak docs for better formatting --- src/library.cpp | 66 ++++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/library.cpp b/src/library.cpp index 560c26aaaa..78e1557ff1 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -1514,9 +1514,9 @@ The function :cpp:func:`lammps_extract_global_datatype` will directly report the "native" data type. The following tables are provided: * :ref:`Timestep settings ` -* :ref:`Git revision and version settings ` * :ref:`Simulation box settings ` * :ref:`System property settings ` +* :ref:`Git revision and version settings ` * :ref:`Unit settings ` .. _extract_timestep_settings: @@ -1556,35 +1556,6 @@ report the "native" data type. The following tables are provided: - :math:`N_{respa}` - length of the time steps with r-RESPA. See :doc:`run_style`. -.. _extract_git_settings: - -**Git revision and version settings** - -.. list-table:: - :header-rows: 1 - :widths: 16 14 10 60 - - * - Name - - Type - - Length - - Description - * - git_commit - - const char \* - - 1 - - Git commit hash for the LAMMPS version. - * - git_branch - - const char \* - - 1 - - Git branch for the LAMMPS version. - * - git_descriptor - - const char \* - - 1 - - Combined descriptor for the git revision - * - lammps_version - - const char \* - - 1 - - LAMMPS version string. - .. _extract_box_settings: **Simulation box settings** @@ -1656,15 +1627,15 @@ report the "native" data type. The following tables are provided: * - xlattice - double - 1 - - lattice spacing in x-direction; see :doc:`lattice `. + - lattice spacing in x-direction; see :doc:`lattice command `. * - ylattice - double - 1 - - lattice spacing in y-direction; see :doc:`lattice `. + - lattice spacing in y-direction; see :doc:`lattice command `. * - zlattice - double - 1 - - lattice spacing in z-direction; see :doc:`lattice `. + - lattice spacing in z-direction; see :doc:`lattice command `. * - procgrid - int - 3 @@ -1779,6 +1750,35 @@ report the "native" data type. The following tables are provided: - 1 - string with the current KSpace style. +.. _extract_git_settings: + +**Git revision and version settings** + +.. list-table:: + :header-rows: 1 + :widths: 16 14 10 60 + + * - Name + - Type + - Length + - Description + * - git_commit + - const char \* + - 1 + - Git commit hash for the LAMMPS version. + * - git_branch + - const char \* + - 1 + - Git branch for the LAMMPS version. + * - git_descriptor + - const char \* + - 1 + - Combined descriptor for the git revision + * - lammps_version + - const char \* + - 1 + - LAMMPS version string. + .. _extract_unit_settings: **Unit settings** From 67bba08b3df59fdd4532cd596137a5ccbe7f87d6 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 25 Aug 2024 00:41:58 -0400 Subject: [PATCH 079/355] add unit tests for extracting lattice spacings --- .../c-library/test_library_properties.cpp | 27 +++++++++++++++++++ unittest/python/python-commands.py | 3 +++ 2 files changed, 30 insertions(+) diff --git a/unittest/c-library/test_library_properties.cpp b/unittest/c-library/test_library_properties.cpp index 96d63629df..3d0eeef5ea 100644 --- a/unittest/c-library/test_library_properties.cpp +++ b/unittest/c-library/test_library_properties.cpp @@ -466,6 +466,33 @@ TEST_F(LibraryProperties, global) if (!verbose) ::testing::internal::GetCapturedStdout(); map_style = *(int *)lammps_extract_global(lmp, "map_style"); EXPECT_EQ(map_style, Atom::MAP_ARRAY); + + EXPECT_EQ(lammps_extract_global_datatype(lmp, "xlattice"), LAMMPS_DOUBLE); + EXPECT_EQ(lammps_extract_global_datatype(lmp, "ylattice"), LAMMPS_DOUBLE); + EXPECT_EQ(lammps_extract_global_datatype(lmp, "zlattice"), LAMMPS_DOUBLE); + auto *xlattice = (double *)lammps_extract_global(lmp, "xlattice"); + auto *ylattice = (double *)lammps_extract_global(lmp, "ylattice"); + auto *zlattice = (double *)lammps_extract_global(lmp, "zlattice"); + EXPECT_NE(xlattice, nullptr); + EXPECT_NE(ylattice, nullptr); + EXPECT_NE(zlattice, nullptr); + EXPECT_DOUBLE_EQ(*xlattice, 1.0); + EXPECT_DOUBLE_EQ(*ylattice, 1.0); + EXPECT_DOUBLE_EQ(*zlattice, 1.0); + if (!verbose) ::testing::internal::CaptureStdout(); + lammps_command(lmp, "clear"); + lammps_command(lmp, "units real"); + lammps_command(lmp, "lattice fcc 2.0"); + if (!verbose) ::testing::internal::GetCapturedStdout(); + xlattice = (double *)lammps_extract_global(lmp, "xlattice"); + ylattice = (double *)lammps_extract_global(lmp, "ylattice"); + zlattice = (double *)lammps_extract_global(lmp, "zlattice"); + EXPECT_NE(xlattice, nullptr); + EXPECT_NE(ylattice, nullptr); + EXPECT_NE(zlattice, nullptr); + EXPECT_DOUBLE_EQ(*xlattice, 2.0); + EXPECT_DOUBLE_EQ(*ylattice, 2.0); + EXPECT_DOUBLE_EQ(*zlattice, 2.0); }; TEST_F(LibraryProperties, pair1) diff --git a/unittest/python/python-commands.py b/unittest/python/python-commands.py index fcf731bf3f..b1432e67b9 100644 --- a/unittest/python/python-commands.py +++ b/unittest/python/python-commands.py @@ -656,6 +656,9 @@ create_atoms 1 single & self.assertEqual(self.lmp.extract_global("map_tag_max"), -1) self.assertEqual(self.lmp.extract_global("sortfreq"), 1000) self.assertEqual(self.lmp.extract_global("nextsort"), 0) + self.assertEqual(self.lmp.extract_global("xlattice"), 1.0) + self.assertEqual(self.lmp.extract_global("ylattice"), 1.0) + self.assertEqual(self.lmp.extract_global("zlattice"), 1.0) # set and initialize r-RESPA self.lmp.command("run_style respa 3 5 2 pair 2 kspace 3") From 180593e53cc42cd19df646f7d0c0a5d5f8513cc1 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 25 Aug 2024 00:51:43 -0400 Subject: [PATCH 080/355] add missing picture --- doc/src/JPG/lammps-gui-dark.png | Bin 0 -> 79448 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/src/JPG/lammps-gui-dark.png diff --git a/doc/src/JPG/lammps-gui-dark.png b/doc/src/JPG/lammps-gui-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..31921bf537d0609fb60d24b36517198a9c240603 GIT binary patch literal 79448 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 a7WO}=B=|}E0RWi=fhfo*OIJ#n1pf!JI5)Qd literal 0 HcmV?d00001 From 97c5bcc00c725012b44f8222c37fc0df6037f733 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 25 Aug 2024 00:55:56 -0400 Subject: [PATCH 081/355] update --- doc/utils/sphinx-config/false_positives.txt | 3 +++ tools/lammps-gui/lammps-gui.appdata.xml | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/utils/sphinx-config/false_positives.txt b/doc/utils/sphinx-config/false_positives.txt index ae4b52efee..cfbddbe5f6 100644 --- a/doc/utils/sphinx-config/false_positives.txt +++ b/doc/utils/sphinx-config/false_positives.txt @@ -4130,6 +4130,7 @@ Xiaowang Xie xk xlat +xlattice xlo xmax Xmax @@ -4181,6 +4182,7 @@ yflag yhi yi ylat +ylattice ylo ylz ymax @@ -4229,6 +4231,7 @@ Ziegenhain zincblende zj Zj +zlattice zlim zlo Zm diff --git a/tools/lammps-gui/lammps-gui.appdata.xml b/tools/lammps-gui/lammps-gui.appdata.xml index a6a384af1b..2139cc6134 100644 --- a/tools/lammps-gui/lammps-gui.appdata.xml +++ b/tools/lammps-gui/lammps-gui.appdata.xml @@ -56,9 +56,10 @@ - Added search and replace functionality. + Added search and replace functionality Converged command line argument parsing using Qt facilities - Added dark mode adjustments + Added dark mode adjustments to syntax highlighting + Add field to enter Atom size, if not determined otherwise From 765212eae267d0108954553bd15a5d4a7899bd39 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 25 Aug 2024 01:18:29 -0400 Subject: [PATCH 082/355] LAMMPS-GUI doc update --- doc/src/Howto_lammps_gui.rst | 19 ++++++++++++------- doc/src/JPG/lammps-gui-funnel.png | Bin 0 -> 333260 bytes doc/src/JPG/lammps-gui-image.png | Bin 117460 -> 87874 bytes 3 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 doc/src/JPG/lammps-gui-funnel.png diff --git a/doc/src/Howto_lammps_gui.rst b/doc/src/Howto_lammps_gui.rst index 364c70d50f..21e6a31ccc 100644 --- a/doc/src/Howto_lammps_gui.rst +++ b/doc/src/Howto_lammps_gui.rst @@ -437,16 +437,21 @@ instance when using reduced (= 'lj') :doc:`units `, then LAMMPS-GUI will check the current pair style and if it is a Lennard-Jones type potential, it will extract the *sigma* parameter for each atom type and assign atom diameters from those numbers. +For cases where atom diameters are not auto-detected, the *Atom size* field +can be edited and a suitable value set manually. The default value +is inferred from the x-direction lattice spacing. -Otherwise the default sequence of colors of the :doc:`dump image -` command is assigned to the different atom types and the -diameters are all the same. +If elements cannot be detected the default sequence of colors of the +:doc:`dump image ` command is assigned to the different atom +types. -.. figure:: JPG/lammps-gui-image.png - :align: center - :scale: 50% +.. |gui-image1| image:: JPG/lammps-gui-image.png + :width: 48% - Visualization of LAMMPS "peptide" example +.. |gui-image2| image:: JPG/lammps-gui-funnel.png + :width: 48% + +|gui-image1| |gui-image2| The default image size, some default image quality settings, the view style and some colors can be changed in the *Preferences* dialog diff --git a/doc/src/JPG/lammps-gui-funnel.png b/doc/src/JPG/lammps-gui-funnel.png new file mode 100644 index 0000000000000000000000000000000000000000..6f24f8bbd93b2851a0a0d5f5e3faeaf31b2e22bb GIT binary patch literal 333260 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 a_CFk&C;0rfnkrk5YhEjBD^)94efodwrzo)i literal 0 HcmV?d00001 diff --git a/doc/src/JPG/lammps-gui-image.png b/doc/src/JPG/lammps-gui-image.png index 969aadce0b6cdf6a0da8da2de4b4a42fd0ffcd2c..5f71cb2be5229853f3363f2dab97ab98f3a6f8f9 100644 GIT binary patch literal 87874 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�SuXuut6Q*{dFz?3US_vBCi1q2R?JNyl;7I^P3%KO3Z43E>+}9} z7eO)?eZg)HPfj-9@o{fHy=LhgfK)jryG(Aa%hrVre*#bR9fwQpdvA1Is z9d8tAzv}hZpw>)SIh!o=348A3xZ9r~S&{mAK34l)|JOod+dslfe%`};?KWdPi}_iz zKH76joYCR8es_ML_mhIljFDIrNjfUIR$P05$=)!_sK{nYaBFeLXtcp535%knqy&`| zJ>8gdYlf{w3@_OoB~Y`(oAZQUiZpsBX(!ej*Avap9}nDoW=?W(XgoKJYTZw@5s}Vd z%3H^aMaPTjOebHP9<>Ul^!cnfH6AZD9=99sroVSzRXO}ieXN5kgTd>&72c{LI<=l0Oums#caV7RifcUI`Z}j;`GEb1 z2Ze2Wr5f|9-woaIbiA-)pWg44)7*ZgZrxnI`f-lm&f>|j{?4xwYm4W>mq}Zk7&hk4 zXG)fO?Og0hHvnqV#Tq9(Fke76hvNg;i;_kq{e+X*55 z5WH7OQFl+zrHkR;BPh`(#>RVps`x~d2 zKjWzY(xS*nJ1Qa*S69}g9=;bI4JV6vMU3v#)46iRoE$0gT_wLf3TMA>K2+2!f5Lb5 zoJq~Q)YMc)MuttFsj}|RFP2i&{L)9hC!8C*9VOy8pzv|<8opv#ysUg}Jm=e@ktqV9 zt&{W$M7;}8L@Y?1Y)Cx~yBXbKTUiSvwx*^Mj-HlwKV@og3!$)OQqQ>~-W)t3xI$>!@w8xGxtxrJfhyw|4%i-wgFR z9>aAbpI9Ew`%X^H(~s^St$nkPt7!Ce*og>G-_DUX;Vr^zyS;rjsdoMCxFCzcpG2-W ziko}$t=y)H7=8@%C6?}jGc~L7BX!Gk`w<27H!rj3YTbWv*0)JmzXjtTFnd()XM%a8 za;V~6!TzArqA;drbE;*1Wp3f1bZm0%Y~3QQk=wq-O<|s1U;U!GaIs55d94EnQK2Va zvG!~tA1W!5>NIkWb zppiSpE3R9vIhrq6#@y}TYI?Tr8C8cO&hKw)h5~x5IGvNYd2Dj$%la6T$WbRpkF2S? zqoV3s2*?`@-C;)K>J#KKWWweq)0t(z9=GW77C!Yj0{d+$MMb=>0B7 zk^Pt;C@F~VX>mXEZz|kv9xhm?O7$>5mO9G1*SN<)x*U*xXd#%A@<5qo`Ioz+fp_|3 z=g1LffOj!J|Iv5BbtX-vn*DmyIqyG`aL%ND<9=M zIsMJf2-hBFn^Ywo?GG*yiD&##o#>b1OoRnEu3TWo%vi2GpKYf}O?#-o~1PLTiXzQbWzT41x z6H)1wzZt;eZDMb~mRX^Dh`>Moa;HU2tpkmwQ$q$_@El>6Gx0B_BjwOO@jGnx-xKm1 zJNznF;kW8laB_H_=P1dzT080^NEaH~nX**gB`uK*taZ04s&$DUbPv4uUS-cao4i47 z#2dkwauPT}oKrI1m2trB#OwEb zGtbh;@dqdPtsHJlc24FeOr%cu)@#>lU3u#K#M;&NcfUPFNB+!LEOU&y;-!?m>K{3{ zESvU1t@zd2o#Q+nm)dZwO-uP$B|vJ0kCsX9t7#|R%z#whnRQ$Ggy>9Jw^tz^sw9QE=DO{TmB9v2Zc*Y?slJY6=|+AF1d(9Qv zRFvW#Ueh%R%+!*1+{LQ_PAeX%pB6rgEKNli9XC3uX}%e~JTK9wuIVxtt1Urd3X{Us8&|~=+-~Gy zHqtAkKUukm0cr5+*N1Yz!WQK$cF9F5hp8Bpdb&lY1DA2|Jx}i7d}oARWMpJsNJY($ z#r^+TLxLAp8CKoTmm^Rh4v@JP-<6ZQ=wVa|+L78o6Zh0xA=pgI*2fOO5SwdJtb!T` z!sUKa<6aU(vZ>utsRMo`ob}jo!PI_%o%bvq5NAfAXSlR3lX-27YCpTzvT>_%q~p?w zVy<5--HRe(9sj0bfoG%L7j*@BwDL52D+><24dal%&e=*eaWpO#)do?$ZD9m+b_H8-TR@9VzLN z&6i2Gf9Okva*JM>WLtU~S4)LlpW1oJOzi(bE000kemW_r(Ph0n+MgK}Qo^U7j}39k zlS?C`w(;APp|GtuPBTHZ9i2E;2z*?s;q0>W&7t$5y53b%xRft2RDB|4ApG}yYuSnC zPl>(dNUKCyq0+?>ox@{ZGKoGX2K<+2?dmyC0J(G)ttrGU1Uy^k{EJFz69n>3IH}{; zhDRGw-^Mq4WKbA&h^J?%4g zV8A?~q+I_(WU8p6x0ouH^^O%MkTWptw<-5jaw8%5zD)hz6{&}jQVk#*F6ZK{!}tUo z*MA&|g@?=hwS<#cMl^Q+t=!b!CjSYagirCHRMq0@_nPqQU5=tDRIXC4Kh2C&lIdwj zWx`+C{w6CFI?=-*qdPu|mzYiN?Rsw=_NT(*LrTmJpS+!%M>+q8L3YhG$fi%)(v4Lr zVZ>5}^fcV~(=8G!p6)P?Rg{kYNsPRFknnylVd`jNYFJ>I^8N0Y#1n&W+jq_(>;uZ> z-8Yvk=e&HYjJA#<+ZuaqeZR=0t$m>cCE*uJ=yC%&&p7lEd2Eb$-+1hJSld_cY*{0@ z)!&rM9)LvhFxgwu7^E?KhoBC4WVXi2p|-?NVhZv& z8=r%N$#oJTkxqpWto4uL$mQ=ltkeb*C@d-jn~mxme)eRw3iGqrcR$N7QE;_rFR{7Y z=YK`e$)7)eCK^1wvQob@87pUy->iS!Nk35%WHVa2^fsw}=t^Tsr7go~jmxh@rkblG zJ9c&MeG&m``Fmz_JWF^r=#O~Ef1rvkshIg;^ZgK@lUSCKOm$^QO?3GjQ?62wHA!; zvH>Of8M(cnjX`U-?Pu?u38-|9|=aJr@7}11owfw0{nP_|`Hs z6qB1vKtA+~MyR0;aX$iqu)PLzcb?F3E48IARc_jTNfUl9`hOU<@>$ZK+COMKIp8G| z+FyaUULd^H3{>&oy`~O~bmPo)<5=X8Xx%s$LqAx_nU+7a8~2Byav#_sMts`-bcr0{ z`3(d}lYb-JUYVfeFPkbN(b5#c_z)}RcAE6Y!Ldzc!Zpbge*Gm$K(p86 zMyj_Y6BAD z-U3vDrDX>%*@(;7vY^t$X~gDb!ae-wP^Ye`8O5R-$5w-hMq-fq1#z>&;8X_Kw$&Zz zF~$KMO6N0+Ks+Od5aB5e8kXI5DMsy8i(p;k~V_7NNOOcXV8M#h04sh0Pdpb#bf4OcyX1xPzn~X5xR}VtWN{qpB2` zR8ycUU_wDoO4fo3fk&Xg*jfwSyT(9m=oKVp>K2Va^B^*CMz*gse))fE0SM2#AOIq0 zCG=>qVUW=2dbJUfSX4;%DFvFqR6I1sL+h|106?JHfy{+Cci1XNgPkfg*{Q4KH?&bb zeL7n+vAHSmlL0Y)5!U_mHjEsT@9qq!^K7OX3^6gF95kwP|I1;#7h*F8*O#K)MM!^; z=v2H*t)Xrl$YV{rOY-#!pu3J>jvvJ6nXQWA<;ZkoWQekLSHi$^W^TEjjk)=`zn2gzjD&Bfb>D<$F0Vhg4M(m3 zIUaDDK1Cc9evKe7YC9VrVU8Fd{-j=o%0;h0t7wEEB**Qo$2WQd z2yK-Z=zbpIMp*GWc2K?f?bJ?j$*UpJDav>dWCHc8kf8=TLXws@m#`lw*|OD(u_$nw zNKtm`g?OQ<=`#~R4V+}0LWV!Hs^%SHf51$k4G+>Qf&*-zWn$qHqm(Z+@X`~mT?VfG zlP|2wzLUc~5V2w$BDQNl_E2h6#KXlEVDeQYvXsF^Ja|eA39>ak-%5}`LU-9Vs2d?= z-9NSXh(7RIYb`S|w{e4_dJWytIQTv(jnC5|p4Xze;}3q_lBx!bFwr`EWRO5Rl&l_< zb;UNnuR5I~B|9gf8)vD$41ERw6{p0WmBJul9hHtc)3YrSU&I_&v_SRV315n^x1)a#AV?gl0wMl#}^vV>tyz}{iW86 zn-f+>qG1xfePF$L?+n74tb6culjnmh90D_SkhF<2bVs7p|^ctvu-ODbNbF@)baL(SS@P?S=)^ zTYk^mup~@~rU^0#Gv8j~Jv53pe2k-56kH-^Rr#FgZ-K84CkwvvNTdd4v1M9H;^YibJpAJ;WjA(o;nn)1zM!U0_Et)cNLN$WRU z>Tnf^fQJQoz|2)S>#X!A&aMiZz76X!L9!dfr_mT;e1TTpge`pa{p->lk%CqfO1S^y zc7veh3&NdloJ1d}q{{RkySm1G0~p}Grl{cvwEr!DE_6UyCwJQ;^(jr@>VZEqk@O>) zHc$uOQKF-v`LJNvxA8Oh2Q#r+&XBAWH=^krVXN7qKo3AmiOm>ytO7)ww-1P34}%^xNCvgy*sv5KsBlyEMFH~~$~(oyR))i*4` z72njF(=$C&@fZ9lRx1&RswBev*uT)S23a*sz68a=1!Rs!r;A~yRhf_$#jAk~L(jT{Y7W#ue+rbF8tZP_h|`q$wa3<*i_H~qSiEm3_+ zyUDsy-Y{scv`oxIkqoav7Yi7IfbhAOX34d-VYRk!sm7Ks;Uhy8u?NClsHM6LVSgT$ z3ZZ}J3E$=j*o%mWfH>HaQkEdLQyUTw01_g+!3^;{{1rStw=ji_5@^Xy9J@pIQo$|R z35|*Bc#^!9tq*~m&Ibf#bcDot1p;te3FpRni&Zcf(7Cz}o;)z0p4VQ58y+`tHhda} zx!A``*6v_oGu0Warx#v1;6{S@WJ1_2dZQIGvbP*6$1>9U?qkC8+-eHdC5ncvY&KI) z!|EpNZX5k-;y3}eUzj3OP0b==51kOWG%mvb+!z?3hR7W#J+b~oH`9iyrm)?q95s8Y zY2pImx*m*L$6aPDXoHnRL1Z}MVyKd#=5QN!>F@@1#Pf@U8REunIAV$Y#!H41NJpt5 z12QdU`Ha|OAJ^6VyTnPtB`TwB)X7@FLFpgBBd=!Af^z|fh2@hu?ZxQXiGLrq6{y~Y zn7)*0Nqe)-wlb+}PJ@uqjcf%m*{+Um)L7CEd$?>=-R7U#I_#F6;zTs9Nhuti?Ld?fMb4oT$X(34qU z(r9ZpK)1z_BC7PE{%T^d^UNH#b+53gE?n?%g~SgP{8%RHa@0rmkYtYBSl^6y?egUa z`^tamOsYu+kC6sfiz_|OMQOyPMV0toz->dMi>}ttD}x+VQs4j$j20CkpCmPE+&$t; z#V4dbJugjz<}*!Xw($SJ9umdH-g3`MvbO5qurR`~e4uvj8QLs+Ok1&(nEJH3kdEjy zp4PO6#WITJ8C!Lc3L{gSFI+7LTGW% zsgu(AWFZihgyp&UIzum`;#WfEvT!C`1Ab4E^j_@ka3?g_d!*a#X$}EcL;`^A;>$*s@uE#a053@=`F3Y~9MjKtfh7axi z){)V2Qs->x+a)_CjA)`I1j$s#l`Y5W$MZF8^5^i0v%85@S4fU>I|aC6%z~V{b_!6B z!;1gf{BbZpX&LF`8DbUMZ-Y_3{0$z3N`n3$JB zT|r-s`RJ?5%^yekDu&qtn;nAXl*e+0mRx6{RO9BjsNK&F>GpZTkiI+!g5@rY6ao7jB zxqPe~A)JuA@?w7ZQ(TZ?2{E9kLOIGm4ryiy4!%B{+;RCKYpdhHF+)10l8WV$l8;oTOiDMSV&V%BWh8oS? zrT8Swsf~#Bp?}!aYeYZC+5s6t3yc9JAk~2eW5N$#!jmxFxUE5eWR{;9>+5`f-tu{J ztFQ+9H41sbzx~}!Vg~7c8VIy+SmY>v_&<6OMTu@eox{@bQ57?Ax*St=iytC`&T7YmP4=tZ)eip`@DzGEL*_Z+Cx;G`K=fol9-PM6KAtHc)$* zk%Nn~ET$36<xly;pHpDMj3_E|JK@-&UtEX!DkSeg9fW9g4V$nOFKzVbBH_;)5Jv z!LuI?@*Jt)t5xg0FMR(<4 zmYl?@Zm*a>v%`u*VL%!xB$Zj%{}I2r?Aw-ieL77O{S{c$ljPFZrM#Wvi?vWEG7+*@ zTG*dpHbSwI0?vGnt z9`b|f+25^y6Mdde=Kt+jXwMDhFL49PGprhv#RuJ_f0Ql6Fkgh9hB=w*4sg|I0=N_i z(pWfFp~Q@5&8 z-0!`y@;%Mueu3*FvA3(GP58og{pIPO-P3yBpfRu|{WqLIji}|^C!F_26WFk-E&-Gc z^sz2LQ4oyH)?gn9D`0`{;D>pP$`nLOxO3;uS(P8K-eS;PR$=WT>~OvBxL{RSkjQY5 zC7FCT>5gFuLKiNfNu89rxpH!uIbdP;%|_6Tx+00hiK{HpgUOH>D!3v_%%PDZKhko2?bLywyK8JBQbMp zXNMf5iQ(bUnQ5gLqg)AhK%`Akgnz6Rfu9CK5fcLd>SVJ z>3O-Hp^grq2o=lc8Qq!5?h(?5SThe$k;1Ok+iP@k7mI=t5b&FsnVErs0bo@h7o)mL zSR`)A8@B2B_ak4inl7Uj9c`Uf>_>#?jLI2xw4UgilwL7NjT~gdXrkpG*KqfzQHwur z=%bE1yipV(^4bm2??lLO0_zVZRxkNaLk}(nsT-%sNu{0>!%Pe~$biMWOiSHwD-*TR z9V~c*D3-*GSQbkwEA|j9^a>h|Y*o@{sG`W=L4ns37+I2&Q-`7<)290FvO!7t6kU83 z&DB;DrkgzWcJ=DTM$C?WL4Voxv0&x*nnsQ9p#4c(uk&N88b(X=tz7rU_4Zk6?$}}v zDum}1LQ3)uJd(gaKF}mXEg?14R)98U?LJ^iLd4J=1=+DKcYIE>ToSZfd`PJ{bLI@F z5wj+$>;&##fBON;z!u*Hp}&o+#iDYo#~f1o3|xVNS*nvdMRLDeN`$uYc2dEIXZI^8 zHJ^R{8{N7$=UX%V=O=}!qWW)5wEcJ)6Snj=VpEtfX1AfeGlF^&I)W8dRQh^)%t}WI zQk}=w-+nKBsv?+lvE@zTFN|>dE4d_lz?^Kxpi<}lC~?)9Q5L)_S68zh2?+{`?@9cQ~w;| z(+Xjb&Z*9XyK487{rUd=9o}QY*3Hr~c-yG|8%d|rkQ5q;CWK0$^!0E8(i8?GKy-NJ z{KnVV5~^?TV_LZiKVbf`wA5u@ibZ**%DL1EPnPR(weWHqd~#bQ?y}6&BNd%!a&@m| zQ0$w>^JpoOE-fOaZG#|23VqPwtSRjBW0q_O*R79gIjyZ|t5g{#r3^KVg!EuN!x3;B zl4&1NlP_v)oN|F&n+w_sGH?rIWMp7>%k#dktAsw#Bny|;V#795KDKOI`HUveOgCcI zd#`Il3|!^^%kIU?syF6Q%$HCvdR^Y8DI<6B}WUk?1oGgJA&6 zns$bDVSL9EHJpHGyS1 zJ8XridnTBaq8zksuM=lBR)O~jKlh&IiW$g^E1W%w{n8tSAQk+NFzBbC&)dU8mx9RZ zIo6Ukb}9Q8prh5G6yD>rseX;k*5MpXqlUNz?#_N3#E^$L=d?3=G- zObO?EvICvDtA2iY!`K?b($TOb*O+C4k&I&TKCj;a_rD6+Y`UFAOji&{3?Xv|2Mon4 zg$$7XakYltV@y#_g3e9AVLo${wW#F)g2lri;Te^uA^0T^SbW4;k~nFs zQ4-CVG9Azqw9QI|Rao%0(T2BeZkC}!Dr|7d`ulGPaK;Ywlx)H{OuPP@dy2?b&w3*i z5(Yr~H>w5GoGvo{xLBuXL@`>}aOR!S7nlyVA6L#-Cxu&UMDN}ocoG%6KViJ42D6hw)c;Na|8Uudb9 zqXF(3A53UAueCb>q6gD9SuXlhSz81xGvJDVap7#)W3dsQ2!y@FznwL)C(}%Ks8%@S zx_lJmCm@&x%ZhTnj5e21>k19_oLo_1VI645IXDnY>0Mvvvw?l+z4EmsXHfyVhplzO zGXrCL-i)J{5Yw^*q=EfvAoxhL$U7Rw%h1HGSZL>ScEL9J)sI%MC5#d}UuPO=u*BEo$Y5YNRf6S9rVJ@wo?JSvKcEYb4P6aq>( z0VPdM5)Uk@Vr&`uSy3Sb#9KSgY--2FFzc0^&Du|8R9TL{m3tYjT{_c$PyiDE;=G|{ zSAq|sa#l`kFe)Sj`(+gNYr_%#1hSsWn!;y^nS?`2vvX@eJr6RcCw$PFC&15-vnp4_ zeSAfIbXPEGVqT62QKkOx4yy~tECl9J{Ot|^|3LrV`g)1YSZ=O4UxKr!()+7{X-hRj z8m$|3U>5?L3ON~Bzgit!?2=~|?l##=*WYSe1SI6qFyZlU5lPz{jyNPkBSvpupE~u= z``1l$wGJ-~(Tr9sqe8r=RI+d-oLM-_#RNGoyPSpFDR%HdG=ahQAEz_HGr#mN4a`D? zEY+0jDUEDFw!#)(F{&0Vp4&EH4(Cf`=Y(Wh4zYX8DTwV;lz)k4xo}n-@jQu8WaPyw zXQ45Q8PG(2ExH37COjs=H7)y;W1We=lk$tc9Ut8|i1pm@OG`@}NRlWPk%$dKNQ%`q z%F)V;+2|joK}@s4BN=xAJxQAC2TkV+o2hPy0RZ&vd8htNH=$rX+6 zq{KwR=}pBtLjvJe?#UaVkB1Zm(zqfhDdrsaG6_RQh}!nlqtFf?dH6N*mt(aetLfPG z`1m+DyAnLwn2EI<%9)7)&Jj)FMw0mW_>5X>ML|y@TgY_=b*dA2yH_UQQz~@l4th&m zl+1L_z((@@RQWS7R@Ym?pF>nFK~XTy#Rxb*IG@Eht<>1oPHc^*ISuN+zugIsX3k2nh-*2KwmcNLyE7R#UseSp6Yd?|>W>Cd+2 z-@oo~L<*=Az?1}7NUptWQ1JV|qzp{AwFB$ipK%Q@&wmNsRZHG*A5SX!1s)$Z(EC61 zsLy2rni$@Tx>&i)TmhH{7FYi)Q0K6%TPB8JEfQHnt`9F;{EivC(o<>VR|hIl$Zht# zjpF9OybKrM8+kJX3r;u!>JvDBOc!@7>i~JRxYjTD7654=oWIR z{3X$pS9vIKU+|oMaUP_n4|(c`tOd1w0Ob?AbeZvPWdNwI4~wN}9uP&@2>W-0`KO`Grdnvw%dm(Qqx0w)9slgUv0Fjjq1Fl15%~q04aW z*%>a!Y6HL%3Hv}^E-jHXY<+#Y6Q;j6_Pkc><8xnXe2ifUHh)7J&^$CW1WH%>cHN2A zQ-~E|*qz|a*8NMPA#=hC7v<$4mJO$Ek=eX#Egx%elbN(sA8vI))K-&$6ZitWv` zfipEL`h_oGkUW3Al?!S8365=$5gxZStb%xJ#Ht)TID{{?$|W9hQH6pXp+FBDmCyyz z1{9jPqN1XS06>1oMuo7*o6uz92&gsq+<`0Aun*34by6t{i=m_vWxSzW3>(wVj&Y6i zg#!`9G&d}MdoK^*h{)-M@7{WjxsnT>Kx#SOt3cDX2{ky}izoQ&fDMU1^M?Ym5>By* zdv^V=^38MuO}-aAb~>rv)9s;TXuZ*S*I-lhymvbOZ$i%AgGgtUU)ti|iJrKNC=jlTM%f z#7vA!xY{M9-a)MkB=eU%zB9a)k&ynxPmIq7ARC8v+*`Cim%6>}dlv z0*OHxG7Q7Z&Ab9i(K7E)Ap&1IL&4DYR;oMJfV*g%McU_&fYOGKpN9va~1;7lh&%)wsg^l3|bQ{A%M!zLoA8x_fLW;R41_a&k9*Ltv+-#6s?NgjTO^ z96Gp1pvV1X>v_qw7f=v>g-RkVH`k%+BiDZpY!NHr&EsGKGSYbLlKY?900;^D@#;0$ zeR{Mg8Ur%i2EMqn(swD#Q}JM{ZeF?|+?yoD>|?_kJ>VkYE1=-=_juHu``vy;*_aJU zdyJdi=e~=Ocmn-lU(2h+4@P1Ch17h*%=EZGmi6$WT(+7^IVwa}R+g2O6@>;8t+dQp zAYt_YI2||mFcpWje*lPp^EcFT zEE1`t*tWVMaY9mPY8H$vL6)_`c8_$GzWp{q0|F0RH17tt$zUXKs{73aThyg7}PN%S7xZ??4jNnxc-irej z%dxZIj1|ioV5SQi0O)PvAn6(VW_1UHtexQQLE?z=W`%J$9DJJ{~}%ku~f zg`n?z0}Sa|qtounXDGBgG#oA42_-50c=XU1jn72_5x62S{pHbYob z^I(6!bI!3?2fWlvI1d`8Aksv?SUs59|5ka4LA1*5kI7kq{(4 zed>p7S3ux?ttbc|#KpuOhX1vwB)UV)i6n_L8_2(1unn^X505l!sm6pR&WwwT%b^#n z#2~hnj(%L}(HQswZo2=7;&Ce2#h_KA5jG>`ta~OA?5+)~aGD43<&a!l@V}e()WQOQ z&zppD%z7-2rDFs5O*36adU{Y?BU)>P*$3W-VBxS4$7*r#xdHoCcZ(KYz=pFQP(d^n zd7LPcfwnI5?cJywG4d)HTQ2;0HM-+he}Dh@cygwtbIda6am*Az)MTs{m5u@$9S%aT zk9JK4p)9Hs46)nW+N{T1>Dx>4VVPSd%048sY^HT8K_CbjSO7CIh?bz7{Sp3c5OYSv z>amcRm_7l#x-a>j2T20kSJ{7`dC3%B#JqtRU}43#@<@*K0JVmBt2nX+&Y)Rkkf*|q zO-&N8%X>R9!^JROYhu9RKY>%8;4Yfv(XGK=4&tEEQC+J6xg=N1FR=`<3q`Bmc(GZo zVh0~g6iW*)xOP}dF!{to|MqQO-suC$Wj5XP_ZJXN3?PtrGz(mZOxg!aS~EV;98Ozd z#nq3ItOSPOt#X!SItRK^IN_``1y63eA+5u+D~W9xf-X@k0NM%9aDkCMH#bPp3IXkKi!dGx4Xe{EM}>f55as8;SDL*^tYH>L_d`Bv6Eyp!)dsygK0i974?<^O z_JTppGHaRy6Br~x#WuG5xPt*sR%<@<{XtnEz&}sn8CB)TI;ZS7mfL`EO-eT#a}}; zX$|=h49-7Vzr(!LA-xJ_V&2qz*bgAebb##7J}9iCgNAf*l(BG37IsCoj2+0 zG(Crd%^-bSDB@WC1#!5-2BvO9gQVdI+j1*IFdTyetEu#Nq%k;2cl>H@KAoXL;$%V- z5f&y6wg#IY%MNYlbf5mcWSr=ndG*)l&(dGS5YJn%Gzq945GuGVY0coW3 z^FPB`Vz5;R(1xq!z#@Y`u&A=e8PL%|q^g`3d!xwuFhi|CD_zSwc>|0CCBY%Xxdmh| zneuX-ZK0%=9J1cO+t=23@5G$?Ul%7!6X;y1%^J-{6$+{WwFtZi0ZkU5WLicC2dx7M z^x;OYhGxb~?wSei451Mh(9#JA3Ic8fSaYF^^V+lYf$Whb5cXgP?^NEXDio8FGA_^p zf&TJf{ctVSeD?*b1K38v%X@fgYKj1gg!R!o(Fd`904VCA=jL31%qh@sO(znb)VXEe z2P~I!a}}cQXFyzrO=nivL9C|AllqppZrn>|UZrSP#@z-LF(U$)<78`VYi%8-TmiN_ zP!?bx+#3A0^r!kxL$1)*9YgU0l)^V4w}6#|K&y)Dt;LZ3BcYqwG7EBz>0 z&SX9?ZOm%#<;$sG$vL^8+>5;eGyRPCPbWT$^TC;x!S})8{eK|lA80x>rrN+L=-@n} zijpv`$vW+au4GKuJ}uRik0K-iK$ygqmfaq4v9yE!4md?iBPjh)ZkTi5B7H#E_0Kyq z-G3OdrY1Co7wdd1Ak*lGHrv~$oP>^s?DwHIV^E4`20S0xdlwpdzWGzWr3@=2Uz?#2 zuToMmbBd(gsrYSD_3DU-o)2T`yop_Ngdz(dUN{?OZynCA0oA)dvqia{a}6zM>nS1q zKKEgeVXm40q+r3%606#7Dtd5-DoaA|O1lUTRj9^X$UU{XnV1l7kY@VMrv*Xt3S1iO zsFPp+VIH}YY$0F~Ho0yQ0{s^tPt5Vycf9fMunW%53X`Z` z8E{kIjK*R=F|Y9$e4=p@A-{Ssa{i;d)rbolH2m=-C<1p=SyaSB><>fJ@k9|fH<9g>)h`L0>+_A&!Ivn9$-{p1a>p9LI% znfMjc2U9D{khsDR&~uSw1-=Y3fY)g3h3bDBh$AM65ICy{g?GREw(g7DdW^S-{o{KA zTd{9Ccy+0`4IM=)&q_ZDtF@IL;Zc>z@VS+W$LvHi6VK;|zJ2@OA}vfF&Mpuq0YZNn zYDY)W}_I*I7B1-^}Z%Py%$~MTji5y7gZ>*imaXZ77ca{q@`^k2k~kAfB2Fm^V#zsypslpgboEg!7qCZdECay0h)gCqq2`pwaRN>qF`Bi_h!d-N$J z91%AhPEEUl0_oFaGw(<9MzB!Ms?qjdVYf-jXu5yEMeK8}d@ti=^?VA47d7r6hmLo- z@WVx_vW9R$SJkhu=m|XC9i{Z1i`sN_bm{c+p@$&(?NXEds7M)TQg_o|vXchrsM7W@ zwvLa{iY1D&Pr^w3>Cx9rFXK&+0TU}a*BxG5W|ROb<3rM~WWbNjORqBRNY8wG6a9&`K6`wz&ir?21pqGHRzBtNzyYN12=_%fb~t2CNMz81T?75 z&dyNXz)@jk0WxuOyOfZlnwK`LCS_KaxVMR9F_1gIp=Q8;TK^if+Xr0c6Fy5_D$Tm= zQ+z(LoLw6A=~CdTU#wHNFA1rYr9t9Y#=z{4Yd2~3RSkqc_F**N-o6$Bf`F(msO8sK zB>F%6b^Iw)@sQIkG*?=4IV*o&umEuW=JkS>^il`N%&HBjh_1yd49t|qnJv_$_L%Dz zeA~ZO2Rj1f!>S`LBQCQoewL-chk?Es5DoxC4Dq$65mi7vHPrwB3r^DT14YMspw4%P zi+t}8pC(b!NKRKs1qy#SG>cCJ4_UuPUV4?R$x_IIvNXluf`Cmf7m_5<1Y#HnC(Ccc zC9xT0;Y0xFq0>x4kq#OT)O})s5(KKp8QD0qntP%WVVS|O4_ptp{o;0P@oOj1pc|J@ z{I)0m$n`EY9Ejt^y(G%D-CVYsC#x*e%;YmuzJ?lg=}&&|O>*FCVs3i!oaQCXS50n5 z{*xxL_*Kmn*1N3kFCKBT^8OKiOW8sz%;?UkA9oXXSqNj`S+H2RHToW(%uj8+QT1JE z;N$%gwngu|6s?PTsad(BTgy}HM|{oi9M+%AH3t;O$SAz9iJn=LpYnTI{DNJceL)v_ z_VhSdh&|zZ;qZZgm~#>PtK}fuZ=3#^iT?{sV>K#7;}JK>8}}}IDmXf%_e=yW9F;)P zPok3U;Z74+TvJn1Q2|_$>I;y+LgS(&;cOFAr_*Nq)^sRxGGpJNH&lsL}3k)gUTBlaL|`DDNyWI}`fPce!qvsvHBsBmPdVYMaq+}O3;`b+xey&eTx z_mD}l;CXySZ^lx9{6gDzXpyUKXoyI?k>M;ryX7g-_WjSFKfiyoc3N6}Uug=2JxZZ7 z+luKI^X2*v8!_uIcR{HMZ;LQ(cTmI;&ai&`>8hy(N|}o{#$amFwv- zjz%0IR>)xy@>dViACR5XcX9$I42z<@6!euFcAY?z-3G`~($-Xe)gDxSphA}mQ@x^K zyK!xQdAq(L7#aj`ef#!pnh%xn>Umo;U(GVHPN~g-5tA;6QdfBX-SQcQ1>ifrjf{Y2 z&p7DzSYe2N?f6J{7G zScEUf`ZQ*@DgjNWweRQC|*RH`HE1<;HLaeT=ezFF}YHC3wz^1^Oj^{JMLfI<=b z`!-6^UwT#!{9f+J(mS3nuR&N)T_7XTq|oSR&Sw&c;UjxV!G8bq9-J#AXzk@?9;m_Y zw!zxl)8q4-|NV=_)}+@moE4hu;^h8tB3!G3c)@ePihn=&H??X0HA+S@!C5mwAZN=S zTV7dN**-YxcygK!PC2Bg5uGT8QwGipUSsav-q|_-@>GRz+IQ^KOn)9wYxqtulM$P={wO0>x z$2IJXFse8KREEqO?6g{(;f_w+*1Gq5ifb8fq9~hDcdlJz%aGZPxdgdBme{Or#{T<) zbLSiY;0>$sUBLRa@NW9}3D8dH&PY_2mfF%_wI~DM{&zh@#ymm#tN)VLdY$*tet>Da zPPr%E?bC#pIWh`CB06RbGQl;i(i$HZz%ha9*EaWoQn5lAlR71eqh1heyjMA+=(A47I31aU;?Med;Iawt#@e18|;($P3zvcbgQ9Yt?W*`duScs6O%ICIM zW9kcMTb%$(9I>U6h2034$B2F+q z7V!dOEBHHArxclwe1W7wr6DU{t8kxf{e|m*|Fo2wEHg(d?_V(IBsMuU1xuHwk>YGi z$UDeQw9JWha=@TxOWZ-f5mlC@AHO59A*ICKD@Yww#ATj9ix=b;M_II5H*Bf;`Aijz zIP7;_?W{Ta;b-sj(`-BR>X_eOwUgX+G427)uZUBGDR!l|Uj9XMq`z=fn~#AMVp*Fn3fZQ5bg{Nq zRi5RVf59I`mug6=bGPEpji^(APN<1)I1# zxlj=T(PfSHd1*p!ai%_^EYZ8af1e4EsnXw(S_efj;Fq|otSQwt71--x2c59U2$N-e zo?A~ku61+Ay9^5}3!Rm_`<7Og9*01YqcA9T)FRhc*2wetjWYqqnL1vX2Gx&K(Mm(Y z(ev9)hc(|TGE}5@@-=EErpCt^qT7{X@61j8@GOP3FU3ZD+19RheOv=g95KL8Pj5qt z((Iv^m)FCG4|UT68}7%FguhYeare+g=f?_G>4W}=J7-=@7BVyk zbNjyaQwoDrts%}#4-;rpmP&(KY1=5!LVaK}isyu4S2scl#b0s{XM&>{+l-;k4v&bz z&@5Jc^*Bhwt2z6|mc?51Mz#CnDG|GAKZSii?YU##>4KWUz{s)mA^Gu z?j?B%vMZ7@*njMLpZH00yDG*nGBA0F-}Lm6VvMY=K8e2eIvH!kcelER(B_o9(5W zX#D45FF|qYwEW(+!gj5%RFowpTJ8@?VSxW7W-=V}acqvQ}T z^xCpR?!LeeUC4ULz7csz+IdP3WuCqp_ULU@d{K#|DSZpI8u5lO6K}}0bVL~p#^IQn z*}c}?!o%^KOEG&8w8~9QU}n0%zd!Ns^R_WO3(>2G!r4!N#?c`i!Nb!nC>HdlobKbd zN_47@$M^HWrechd6s`A$X)GF~`l;HUq0QW^X{#!#3}9@7=S!fTtgo zw0UTYW@J^++*ASni)53^xA5!t`|odh!{Z_ZZMs(T6%JazdX9h|qt+lDGOY{@U{*3> z8}Aw=!NF(7)ZWZfNaU=K7c6wSgjd;DoO58R|%S|-s}M4A+`UJ zKLVf;7}>#*U97|0E`vE85hzYL^gRXxD`TIT{&MBcdaf{a7a^6c>x;~7OX`de(mu0( zRq5mg&hT7(>9aC%?U(>hFoxNZqW`+OzCM3nU6!6%j^)f+l)p6nPv&0=I@HK-l&>C> z&EL9r3z(Gc{ta?B&ofZklBh?LeS2H>a<=Yz6fz(n!D75R@*;U&P_<*N0W>R6Bf#ga zyMMu@==Kl`P14C_H?VFlR-hCZQbZ^XYhq;2#id?t4&)DR{lGs|EAEaRw-$b5@q&vw zfn(D^;YEmqh^S8lX+}0NKqs}I!Mg3I4r}CouMECDcWT#PrFwZyLJ}3X~)S;#}%LUn41mK%C zDFc4lgNX1NrmFm`XWA?uk>*JKx?#!c0VSk89up#U5P3puQWr5dO(IAWs4#N#_A?1p z_KB3-(7aY|wY;@QJye&0eR1cqnTWCg!mRN%YMN#@RElaeN&n$LC@d6`>V@*U)6=}+4Trzp}C{}N7_ zX$D1N1z)}Md{ABPh%IaYGINl61J9c$p56>$1HOF&OkCUyL1j%%(N9RVx=miYsI`NR za9&J3QZFH&h8)+!BY@-8egJ%G@aYHs%tUp38I%5do0Lam=Qq!8v=?qYb4|J|lE7 zdPYV%lz~^6&i`2{SYXm=zl(@6>)vW9$Jx8KQ$<}-j??Voju*`!>8!2Ai|aU&Wy*fc z!Kml|BK^gj2ldwO3F385K#->I`mXQ#LT4N=FK^lwUVZsOXYl>=8`f>Td@b2^?d7Xg_rVUvXBmE7 z$&btCPxQ7z+#i#d-qMROW7DekFkpMxA43^@G8_zbyoe7zaUX)<|c^%7|1IdfWs^N|MV zzNAXNH>TGuuII!Nq@vD_R^{NGmA~oca^E2Dt6cyfMWKfgRc@fFmNwAF1+CklJh6rn zX@LEUxQ{`lguE-mZD?rNyX9?VMSxHesCHKpl41e|WKQ~|xel-Tx*XR740lVXuO-y(hb@6S7aI)>Rga`>CC zJe@dBl&$zzO@g~>ef@m|6g4{?u=f|RpETVNJM{Ilv%AlHjRosoQ}*642aoP_a0o+i zpC*99hG}asBoWQDZiIZhq1z^)6nnXQDC-_Hg$uImn6OUV^tz)2R0m7>h0_1W1*pv+ zRtiiDME9ym!fh)(y0*`=rs3Esh=G|G*~|NC8F&Ug>?Xxx_K@zmoOXx$H(AnKd(tD6L!1Re4ox#<0gXEZDOjar&!o40Pg}y?782TmVBX8& zbh};U%JGYA>n8D}k%7}CD`N!@3-p;^vmb?0TP9dot6h}$fA|P9unYK%^etafW|1jh ziwJ1Hp(j{AQzp^Q*Zr35BsTaP$Mne%skutpxYD#w%;$ni835`{1Db z{LO9;`dD9MLC&ieR1x83w^_R9_zOqE^m{KwPh?Yx_Hq(5Gz{>VU8|dny;mZMd;GP% z#^X>m?~|S_)%FnsnXFyn$2+BdkxOhJ)oJ9R=D*^gE;PU40nhR^E|t4|*5*cR{fUu! zXk0SHoKGrrfyn>ww~I}Co833$yz-6M3p~|*2bBGG9{9lyGBBdu4=U~i3u2bl+L{AX z`!I_KE=DX$peFD9e;N#8H%N*LuCLgKd+Q4O4{S?Oc2VC*oEJmlZGSs_HF`b%*faeB zPdowzJN-W*aKR%M+qhz#vzbI_PARsy_*w&kOW{$~>3uHb44g`kT8Mnw^(SYv@VDoE z+y|LJsFjjUSH)DYNJJ~wW`k3jGCG}LNfWA>*J4{WxL%JN$Z`@ho;`?5Q?s_e!t;x`HdXmy~K%-y&i9TQMLobFd@|mEzwD`e&MbZE#Nolcm5} zUK$wQpI<%HplMuZ6*0OEo}MH!^g&4F*12DIuxj=uugpVuH|oHrAzv%TMY7l}5DDvf z#8yT6K9HqrCf)&KXnj9w9*j#L+t}m}TATb5P@=xOC|k?YaZaw}E2weYC{qNgo0@8x zq!VH_(PS@V&7p-2#A6d8TLuy_uWc;#@JJn+PME=u6bL0~^YAI%O^Dx|pT&h2OEucp zj&^;>ww{ofc=6&za1hZaIGwYx(jciToKE)zxvCT!(GF}E0)YUqtzGJh z^Q=vlLhoBzFVXh@uRd*Qcuf)no+K zv;<=$zO2JAH|J0l|MMRV!Wu7bVVC!{s4MyOH%dj&39KK?2AQ&b1cL%PUS=628}6LE z0YuL^sT?%zW#n)_vVOeYDf)qUK$ zSG<&ClR{9?_#E~vzqhw|IM4WS_Qos_&xmYiQwN9hZJ&Wgz(8?E8jMq+0b4H^VSr^= z`+!(GJ7kXjfR|lZrHq`s_}RHHQvDa{=pcbCD2&Qhizof8;E6Ewq7R4=MW|=b97s|^ zs2q^?flTA#>&sGM4U;=Bulms+=gyp3=zOF|+aF$831mg$>Dq04pGv{;q!PRon>FL=TX98sr(@0`N+Zt z0b-4UlJ#~e@XrV{qL|sE=3#2e6ORu`Gg^A~X~SPEQs;+NS|ZsC9KYpk zE@UsG!c~UBuK~I)D~eW+KZ}WjqeMKz>)%;S=P>zBKva}aR}zMWvzO~U>QC2}R8e4- zZxE&BqQ9v9%2_ibE}l1OmK9in)vnY!wE18pFZJ{%J>PB%KOj|8@Y?w4eERw*^og@G zmHR=Q^q;tBzk_*Q>kl%joN+*I$z6t0rOOZs&LLZ^0uVnH(R7Lv;%~Ojg}= zEzvouDne39>#E8zfqg@g8B?e+Y9G=@$S?kB;Ca-)%5zx{=cB zrTgm7D%n#BV#0LZ?H%qn5{rC@RO^}D2M-=(Kq7g@(RSXha5HRL!gq+Z3fgSwbRvF7~+fb@gI2Q$M2|!46s^qlJQN;rsaI3!_bdo8#19w2XwEt(y9U=)qGcY}! zE?A@ayCcT9OPaE=cl}eMcxivnyI#%>DcO6BLBe6G>p#b24~&I`)IGSAvet`ITCnM< zxmXjgd5D`^`0Kgb5_oI4NN@?AtoY(4li=u0=HO&b?BC@9XW-2xOtGsBcbW@D^?C_5 z13@xl9|{gIG&MU-8YNP&5L!c4#&vI2e*Gcw)FC6*sSlB|kRU@<`(}&+`Y<@-tEsV= zuBY|AMB9r!51nMgVY>FN3nW5T(0}$_{vg~7MF~I?BO@c2d~2H*BUZ}4H$gQOk3L<0@~ukq z8w1cMyK7%NZoGIr?BShj1hSx|{n(+)QQ>ScGBLrXykU*}^ao~D1q;j~Jilzc(h2)V zhJBJOx%xMWKv7wlT7GSXkO+k+qQ`f}iOFTte5QOQ$TS|!F|#|I3dCyo)N6qW;qD~S z-U~`z%fIj#7?B^(;X~b0a^I{}P1wV|fd2E}s$g?w0&jc=d;4PjV*La#(tEULcl-x$P!Sl%h2I) z+ev73;L&gG>$xCLh^f8I{*phE@{P1t5;~_v92D(H?5`973bVSmHTCsgVy&xc!L3#W z+Ipqo81BSa%VY{U77}@6hcL4hL$_c?%r8q_rpL#iNz9Y|%B-tjPKF8(Uta8f5c`oa zIx;@K=4#wu>$^X8>gL5pY-tNcepP|e6X-NRI~qN89e6(1uY-^2(cI6Ud`2f4&hj6( z+J7!3=Q&AYJN?8^88e-z34IIeKZL^J6yad9))nw`z>j)LbP7$+oTs{I+d>gW&LK+s zs~}4w2M@d+>4QC@kJ{+~TJMC?65=PTG9WM-Qb7R!J3RD>c-ib2?P_#AG0#p5PE-%1 z);eGJ^VhIU^o!32%W(v}4c_NBmK!NRFt%4fz+M+s;cs|w`({5Pc{!Q(W2rU8nGR5 zX6g}Z*#WWKtk-{+#D^QdghcFvGye}5g_);ynFo+RDEu-e*d{$M7L|oKXHBwy``z$o zYvE`P{kf?i-zIT-KI9|nN5~d323Bd`#W$4bt+&pG7h@6K1zk<{j1>1Xzh@Qs*SJaF$M{?eatgyed!i2pV(EQ!=OF+=!XYp&}m%vD*Jxn9uK0YWpcyLqHh6?J@Er$+P&34(>KZil)J*g zt)?{c{0XTdcNEfi*P`Tc!9~Bcrh~e+KInA0E&YVE^@g*7y-1|z)TU2&`D7MMT`xtG znVsW?qgITmt(tySzVN^f+`Yheau2p|WAa1hoTS4(n(#mCa5YSHJ%WRsy5;;h zib#mF)uTJGGZ-~AG6IN;_{qp<4=c0KQ?DED9!)2hitG0}z}W^ifHgJ21@`no|KN*V ze;tpeoEgW4KTF05-7i(!t$Mm#d82y8IQ4Yy{n_As@CD%Y`Jdz6iatpxil-;43rBch zvq`uN&N_)b8dl3HkRF=)hL#I;DTsy_RK|X3LyU?zb@;6scFrvW5DCF`CKZPs?hIBDFJR*O6 z?R?w3w{QBbSx=){1BD4VI21I5B_$<6cnOM^Dn_)Y294=?%0b3zd)N_}7QquGV!G0H zGP~ydI5AHEp}fTu@pJ4aMTA zA5!rv{Pt z0>#kc3~x0kWSL({rLfd20^dh2@4zLV3vO*T zd0STI&-}3J-u@5cK{%MoI}t-_XDI#i2qWz=EOI^&9qMIhPq8 z)6=Xj%k!XwPc-#G^xv49ia`Y6kDPt-4n!$fme;i2+RxdXnA)A8uno<<<~Cw3D4=pd z^AG>dhXLB4U2Rg$oy|=tMZFMLqrws2JjxD8L#~@z1DsR4bQDFSHTE^~k|=OA-Pmgp zIhf_Hcp+JKd5tHA?!pqs=iafr6^ zD5Hf?mq!`+IP=24jMvdSzakw z1Ovqvihv1`GJ9LS2!Q~k9Ga8kwjl+Y0d)rcJm%L$-Q{hF=E4ubwp^?OM?y5SB#bFu4P(N$XkS>*cHlZuY=o6NE%44v09)<*4;5aI}d0q|1!M=N~tkBlOPr&<0PZ5>{ zri9PlcWa>T3bK?gV8QUMjXvk}ks4JC4o0_nG<-_d$hTSiu{e1u73u^+E^>_T+Uo@j zBi=LO6ECkqY)4kiz(;(_-UX6RZ9%n_+jshNpgsXfKEk}-b7#e_^nIK7F&O(FL^^r{ z@{oV};(^cV*DBV-?}}0LuYQ0^k6|I?JPF~k&}S9@=;=CeuRl3!9qL=a+P2_ z)zbswV;F*K@sObuvSB1zW$)#gsU11QD@h^trnV&9<^1iX4W&}$*!cH|+|6|sP!bR+ zPEGKw=Waz`fyPyeaZ%O@#h#NUFwD<~wnrt;roHB7h+dpd7p7RLQ%GP4IKdpP#I(hi zE&l@Xf?VSsAZd7)HujtdjBH3VJ;4AO5UVX zt|AlqLSkii1sBt7(pfD}^TIcSK4@Dimf*&3l1&w9rbDw0RCHBD%Dt3mF8a5wM3=XA z=iH0zFuabByLNkaB>2|nOAYq{N`mbsolx?FPTpUMFXX>Po1$-hS~I_muy+@jQFlYq zw(cUXlZdL^M}rE~#uLjKo)zP)eCE_QF+r8U`Cn3bG9MJOy#-$=#V%n#VtcU8TW&*q zLR*v6MhKlI-~=gZ_XknC=xsilc+08wxU)G|Td!RIGtW)zk0F|}jU6T25d-?9NP zF`cY8+>oPT07sWy_}W-aeez@V(QaT4Geq47s#nc!Z z;_u;Y6g!37yX;Iq(PZW4Y|rvpbc{C)hSj!F7QVNqW}?OVac{-d7+kqIg68NxrYvBE z!WNvkxZ_PbRN~Ic41Gq(QOP^UQfztS38NR@o*hB zFgTcPKS=cZlL!qio`3}gMEwwy8^RD_?tOvGW=yBAyywXj&=hK>J;PmzG4I}3EQ8}^ zd>t}t^atw2$gxO>;uZ+Fd}l2nQni+bS%6i>0JIaT1idlv0I^l2+p`^$!i zQL6gqvJen@3)e}Gia`}(gtgq3H8OTdKW%ZQ<)yiD4>ny#yueE&h9zSpoEXexJH&E> zP+Yw^5#p?if7trH^yr+@?}ti1$DnX8329q+WB^7Lp?D&CNEU5$eP&|v8wyNJ0jnzK z1Hxd3pXO0dguu)$h+ToZpi!)|aa8bq4s3?Ag$oPb?y=lyj&}8ZSYr#-IM&JgyR_8W z1(y}d+CQRKNoZ-Df1KM^-d3c6;Yl0CesNqUP9P|D+CwU8UiVKjcYfaW*EV4ugPOxtt$47zrAv5{^o+!o zliEuJ$~Yo2NBiW)cNcDsr7gD2&yR}mzTK)?=|@+vS_D;No_qgbE`EaD31@E)Ee!kS;2l;;w79wADx*stKUYqXUApBH?1-dx$5< zzP(O~rqO>NfKJD{1#p%HZ3#!GRxNO|5VrKR6&|)yG|Lw}_a@)F`9|Ur|j5vF+STTTWtH zwHIyQD`@z9(CLA0&-Gzt%c;HOBO_I=y@G*}`e?qAAAp!bJMT-}UnZIk9}WxW;IZis z69UDr37h_N3KvX%gPyKzVm5d|JcryQm?zH`maD@E8?^iDz)yVVe5%XD!C~!9|NiZP z^&9$h?o(M>mlp-OC()|C67@r~yb0uZs`Tm(T>&dhtpd_@TPvB@NYiu+s)@l0F>v+^EeQ-*P znGvgQlhg=|RF}K7IKJ)`H;I)&IyyRj|6s|ihCJ#Yi3tgSGV9QM+BUrX`J;1=PFw`7 zm~k70?7)GR9F;JC;*5*FBVB;mc>lE)JKDJi=6Lq`S}@TW|6J%c<$QhAwmnELn+t)8 zGDXS)&0FlrN+Xj!aD3e)&@TztsEpGZGyqir;9T_aQ8S(6%wl)q;0Kjhk`%RG9ZS96 z^jfAcMGJ;8I!(=Y5pGI%v*q3isq<&(QtB6or85)=&Y*9EWaG=5#2JY3;mU)i-S_K@ z*b;@R&sV{MH^}r3p_$&olilyh6WVWg7^Y0`8Dp2oP;imAf3kQC-%BPlB{!uxt)yw2 z65&eSE~ONL6v?4O3FP2oLXa+jX*8GDJT_!gDT7c;|aq2g$&u2-Ih<0y;aE zOUWSZza3#6NctWB>`G1jD~H@ah<_(PxGCXch=6m+5B-Gr+-1P+Y*L62wfkC{{X|#q z-PL{`-g;q`)ZV3S!J;2^T>)lwk&^aqk5iV+;?(LY=4UFC1Oi@}L~tmnh9t8qq)X~? zhRFiSJJdLh3u-P`uZdS~>iJ1&TU=rMOJdhHamZT}MxpZypScA&UahyESZ~;0t4EXo zra2aac>W5iVbAwmVZYA3egFP_ef_2Ovi1G>jWK5?E~$p~0tqixN@XInN?ZKvga7HX z&y7DhUK`EVyET<_c#mIHbhk&S|4vNvfk<~bpbNFN<3Dx~C}_S_Kkcb2x*N+I;7rAU ztz;5P9la!WUSTiO5SSmq zHS%s3)lt1h7grURg{Xtr*4Et5A~GRyZB}_Q6*L3G8AIKeUF~Uq;_*3_Y9q|3PLP4o z*0Y-{%_dHVf1hmI-s*=FwXm>I{m+{TnbVv~n_{$&h^vjN_}4RxSz)!#}g{rXalaFiLh1=Q3=Mz;^%2AK+{ z_S#kzv{0jSV(llKnJ9zh-Xv#>O_3r9&I#i-U%BFKWyP=#ueo0;e-n$!5zY}73!XFN zw2(nq^u!E3Dypn7qx-ZIA0NNGN~*p08c(;l89<)|bUWY_i~&?(U;wa+N2(aXnTZH5 zIXGDA67?r$zAU}`3uo2v=dW$vIW^O~uxK;aCzcHLm0cbr z64&(SPGAcg;%W*c0*_V?1gxGueQIhtch-3C`T9Orzb?^^4G1%|YSM?5_|5g>qjl6e z4yhD7x!AY>zGef~!nvoY`7<5$bn>;JM1*lS5tcvVW97-g=+d}GnT=P==c7n?$-Bho z4P4T5mlI4q@ZU&ZC~@cE1(!5z$*3*`+0(Z^$0&tt zQtvEE+EGKFC5Cm^3|Fas+0Y--M*)qSVaVs?FYYDh!E9uS+d@}1=W{Mz?uLLRJo1|~ z_%1Pm`t;jZk}W-y=w;YD0YXh6{BaWdI4xWa9&z7y_hI8weDe4o8k+viBvEI2<)zD( zWAQ4>Lt+X7rH=`}yTT%$=h#nR)BQH%7J3-|tRJTzEnE%7GoW5` zapvlMV!s4C@Gq%^#u?g{J4tun8!4uf)OW&d9?Y%Fv?i)2|8Couf7#hTBwCj}-4Raz zADX=62gg(4xg}Zw?(Z;C+u1`>x<;PMInXt;I7)CXaOLono-F4JyW9bMx-2eO^}c~* z4fQ0No;(PDNXf01Rw(`$-wXe@;AOyBBVtuG zzhQQ-SjP+Gd-1R)=R|~r=J#)JGgNcTqS7qJ%>_3O7xtWklJxpc9URAEE>l+9^et6PoO1bhb<_;4DO+&AB+w8Bqqg%S1g2 z40-MB0MrjO^;jOtfiv05hmO98OXgYBwb)+pqco1kM77V8x@IYU0F6?pv%+4ei3)8L zyRNpLablWU?1BUV)wcPX4}pqoON|neA*2Yvi}qawU(#~PO)D=qYVMh982VZqW`YNZ zaoKm>ZJJF_h_3p3o9xOS&-xaDg^Sj*>Gz&*W)tN_5W^7A{116;o#X#U+qWAnc^_@n zjDrIfv^QK9WMCTvyg68|fXzBHokc}8a6Mracm77gWMwdVQ$?uX)%trD7iU~(fnK^q zmEWpyFBj_n=2y+O#;5fK5xZA$KYqjiuUt?$R}1HJ89Vs`|XGV;cg z2lW6xQaXC+eDHTeP*zW`Z%GR3=;&BlS^3P|NbHWO0EJbCGAEd@D4m>{6;{c?uZk$e z@)Faq%_gdmXQt13D0-z|1dp096?dEHUq3->gVxY&&!$$XY=A|j7^UM8EN(#iOgqdY zy43deB+N3MO-)Z&&{It3i+g@l%dr6&6_NyM4)yrgyUiX_z7S$aOs#@#142MP4Q+UU z7``q6G^3QyXB*QeUu=B3;k%J+l@6AtK(yQE?JkE_n-{b)jo#u))J5tzQ{ea$Wj;+{ zrQ~^wo1?ByHUvt_cIQ|ypI|bzcoUgFWLP*N5nBOG9ykqRx}^FAO!FIGTb_d|{>UAga8ikzJ=m6c%9Z%M~)Bc0j z+hBZoJZ_ayJYpL)e@8Lv;sH@TomjgT90lO|7GOgpfWI1V5Y zyFiPFzErAU9%f>!7SJCAPem+e`mx+A9+BeQ-akBh@w!-4b_xB4si?8KP9U59z+%z9xw3~=_6x(++inCOvCp-{^KbPlB>x$)=1xXd}_0txtYGSA%oaxg*4I5 zs~HAHMo$e#`6Gl?1Ozh(f50=4HT7s&ZWfdR=;_ReU|JF`sYNm{dBX!T2VShL0cS+j zkJ{+%;YkAbXNdnasJ9&*aE$Prm ztL9ZyRRx+*(s!HAoXq7xK7B&~!BqcN)EZ4Eh|PeoJ1^!?r5{rfqI34S2|xX-6OoZ$ zmpLeiz(kUDplD>;-&5z+=9d|#&CWj7gr9(F1kankWVF2F3Fc<}qRGAtKz~a3Nj#m?KZZc=)G}r#Ew>JeDBSGL7!1gT0 z$L~uhu~#)7Aa`U`KuBs;N*Z(yOD!?cv8L1$Rz?cL!^2_bU4KP4lU9~pX=wE+$O60c zYa+7&NJ_AP%&lcN;C8_KWk1nz08=lnrCXP6Okmd}G8+j}L|a&MJ-Vzhe5-2dsANmW zIk>*T4098S(r+J_rcHmCP34SOPshP=0B8iMknQ)jGcwPvB^Z@nBsn2sN*d5?zYf-o zH1f8tbhp%9!?GjGlg^y!1766E=hPEJ1JIg*MNp}6lX3SbanT$NkW(_zH`Euuy`pJ6 zVl7;wh}ih@&=KGh)ZOgCXUX@7bV%@4Br0?#rl%WQ$xpV8a^=uhSJ*!kk7bj%9G0b! zz2T(x%e_Zm(DZ>xL*1n?fom0Wf+*__1NazVMrKcd;!sap|}_5B2E6+A5O zcYyKUsSHg`TrS>`5|8)DG!~9rYR;lzq4w973L+;JqL`<0{zQbL6zQ(%JX;y+%FmiQ zk!`l4zs#7fu=QW_PiAU=s*7`CQJc!m$muJOlCt2dMDsztx+9_NsKqpvLHqSkHyew9 z@teH<>j5-(nMr0=&%eVcWhPX|gWQv=S=J})4fp%y_=oV6Um;~bn8=@-YyL~0?XgZ9 znhC#;5w`x}0l6QQ?u~GzEQsL*k2e#h2dc%x)6M)uJusZP+zZ9$xi<%Rv*qD?{Q~#2 ze%B;+*0<6X|U!Jchov) z#LrvTPy9AtMorMbFSzrJ1Mq&m1n0goRmp3c^k$wkL?z4GGdkWo4T@6^j8n8j)-2T3 z_OLzu6vUanatNj??t`}l`2L|sGWlY&zV2+{U&JaS;qqB%LUMb{`BXH8ZF3HKkL#Be zR{y_$cMQ*(Zu?pFhq&)n0&(t6dCJXZM54H>O#P-*-ycIMz_tgy?+Qf!{f~wD^W{(e z*YpicJQ1LVPclh_wz|`tItXR!0Xk^sG?*x}LL z7Bfp(zR$r38=6q>r%$Uf`TT95G}Zz8E6;RL*_sPd$$F#*|JXll#19d{MswnJR|zkz zvZ7wc!$h7pEKP9FIe&cs5%fz{D_>t*k9WO z@X!*T84W2N-+5!T%Fpn_C$Z`KjmE@~v)=~?-WB6_wH>8cE<#?yi?46wUnS3rwa%#n zPfZ~^SwSuS1jp1uajfc1r%%NXh+iAzml0)P4+<`_#ZX;7zmPsJW*e?EMUS$~wQ=dI zI#CX{N;%sOmRjn4+J&e4beKFnYhg-86v=kl`hK;&WK=7C9HxckMiUv`4WxAt(*$IQ z5yW#UfnvX-L{#Mr0u98}fp& z;UGzwB?kS`P#ujT&%s+S=@WTDKkD3l>pb84{@q?y%>zG8FzJ5|01-5U+T-g0MEt|E zKFn=MI(g*l^b>{u$X5PWCEz6;4GxZAPx6Bp^9MhOGM8cj!Gf?#_hLCx79EPL;HOCK z&BYIu^BH91F0VPLE~nwW9UR>DYWvb}L9~@re?YrSIssgK|5UZji|H_o#xY20Pqw+-ZZYX*amSs=x8? zN@2A<4D$%EoWQBh0iun1rH|&NEhFH7;A(>NQ^=~%sn2gaoAr}((bF4J{lNR-xFw6= zamuxUjDt((GXx@H@h|y%ZWyyj^f_o0_4cHdjNnZ1Vu@HVJDXhnD(cVyokVHTD@+AdWxvO?}#u;Fchs*4uMZopxVeDoMeRv zKxoMovtIyUYjTaRRWV*e|=_&TJXCIy_)_G%-%UyweaI}9s0v= z=dxRpZrr_Y@ks9+itd~oW1F?FuMRqaphFYNo~gl(vyN&X@ApjrB4^b4J_xRF9)$b~ zyx~3f>7wzr&y282Wd(4ZVT}Dd08+d2q!05;#Nef{Vaa4QQVeU@DQ_@jSaXxesoast7Obt;Ctl49g%a3R57R^%7U^-(zPTQu zkRd|BnPbwEFvM{e-I+s!iLJU>%Jp;b4Sn?L96vAl{g4!7zJ@t;{U*89$nqY?kO zmRrgF=D^`Utv?hYAe@~6>1x6%#(sx8iYwr5&^ri|I5D=VHNN#qt1zelVEq#jIp%h< z=WN}AD4Z!hluJ-cW4bIWYMYuU&($afANiHAG4n^`)yeDv zX4Uqh$`vUVXKg(D%0;67-n_zvRpq=evs=c^hXc8zs(+g6LIVUK_%d#LCn2~{2w8B`V$+BI>Sk`=?wl{MPF&>1?2`yPn#CahA zvE|lO@Npfi34V(EIkD^ez;3jFUo1w5sg^|(v~$0!%k@N{{C=!H7;w= zTOAZB+hBL7n|bFexHDtA__~d}H$M3tj{1EPYzDJzlG-3~sj6Flk1N~>(obI!Ue3-e z{&a7u`5hQ8{~{O8Zw~Q|*ogU!p2|qJJDkhQ{{@`!d}iX?(e&at%MZbS=gJ3jUw(dP zcN0l_FQz=RPcHYvIPPcNeZf~=u;#lq-e2EK8I5(ZMag62={Rn)(g!|tFVuYCG}le= z$sh!J&4P2S{o}__?w-Pc)g&%YpiPW0yj+dfL^HP;pqbI)W>EsqG2uwxWsEe%)Tie` zsB7d8&{9#l}XaAN$b{XU?S__+yvY06&G-y77KSwVJfn^!i1;%fNAU&B(T25&v301MX zzsXKu;af)G)>?;#sI`91N|nJDpv(8l=D!pxRC~k0W;ZO`HbDKrk=sO^TK3W;f7_kl z6C~Vyf7;p1TexB>SW zNrIF!T?CaH0qZa!Lc__vd4dn z+*-wt+^t2@OHNA~kji{-ynB4)K<4Mt||#;^arqYlv6nD?nA>nUsp0O zrvIayFFZ>f4I>@CZt&_#$qi_p^_Q-LcL{7~JKm{(2w+UdQ)56xiO1|^Ufq}>M{Vmp zBcs$(DGxp~Ke9V*`d03HGav?1{ic&-G<8f+1hGTrNy+tmXsRso^s0zsP3My3J6+{(V_#?1^>rqy6Y00O7w_Mn*s4v<#S0YB@RI8# zRC3*|H*DNxS>8!wi|OUuIvjeUSJ7!uBjq=px&1hupcJchHXAslO0hu4hv3WAr!dC( z_EO+X$xL~XWd-mCiL|XRUjlsG0EkL+4XH+AGd&@mA|?0jZC+f+{N9CQ5Y78MHEAhK z3YaPnR>=AORSuC!S_YJ#%1V;@MK%Jsd_v>{KhX$bi>c1q{pr&zt~!^+uC}(eObqdAFZu@djO6xv<$?yL>$Lgz&he|nUha=8 zPqP}AK68TU^-|rMxgho}nuV$3?AD_Q0VmH?3{Su7-sEe%291p5HCe<7SbZlaGs9ly zkNnnMMNwc8w?Y8K11Y6!D)9OFsX1WIpcTs=lLf;b8hKm{0A|3#9-BOKKMu_u;tq#! z_$OHe3J0>8-M!%(hC`0;Z))1!?9W4UcN6zi*~k}YTC}|L&x@y>4ZWnk$e8F~n|?gR z#^vMpvrff&FYu`$F)aM;E$BUf8gS%R2OQ+5?EvU{w;9OZ)r;CZNZ#06zqS4#>QwOC zTX$OOo#vj~Phh&h<6PX>*f^NkgBXqcO)SVTLX7=1$_>FO^&wia{Tp&JIC$VHV2woB z?|~6EXY@yHiQPmsh9~Uu8(h2vY#IvLR8m-QeuLtffA8F13a%VyCMcaD_JEjm4p3%o z>Dpk0m2`u1YbKs2USH>95~-pAS3FldWL4^|O0Bai4Og#@_mG`j!kXJGkD87>7G_+1 zI`PEtyX9f9-yZ3kLTq}?(U*r0n~ticY|BPQJ~;E`=<4a~eaZZ68DXI(sdUs??$Yh~ zOfcO-kEr0PuBib!_?N${H8W7e1LYb9Q*OlIas+D4sN*i;RxLgy*B1u@eS&ze-Bc)LyTK zB@jqKGvk%sPS4v-{rk`1mh|z~%<Z8f%iztYfv?D@N>Md zzFu_8yS3U2Ux(Mnf8{@;*M&`K>`o%=U*s!n#|~E$;s1fFXXE}nXA7C9R~`R{rjtZw1D`_l+KUZVQ>}&Ud6Rz+sloVV@G{hIh$~%h&+eMTPsT30c^i~ zSQXB+P)N~7U=kM9V9o{C$a**l4rv#RUW$uttbI?Wd-wlnI`4QY|NsAMag;bkI<`|P z3ek{pkW(6`BB!ilcN}H!om39WN|}`rI>|W6IQAB@SN0}*?@;`n=l%J8{nPE%`<-*0 z>$+aA*K<6c_Xp0uxdw7Xa}(ZI+LmMG+`zT#WKNxX-> zmGJfww|1t8ju`e90NGSqvyLcxG|63wRx!d|9Ez$?QKGK(tj$nn-jzMKw)(K<*fR%^ zK|8B+>Q$07OB3x6c;8k=VhSOLa`)Zm!3CgXORIA-M&HK`UzGXWZe~hV1XoBX4C6%L z7>O5dCHVIaJY8vk%l3Syx)1hjZ)Ub%6@4uX&TxKVC*Nj>&KSEZ%kdSop+A02dGd4<7aUN#0AU z&KvK0Tp#}{-a)P@ExWfJfufs3vr-<4gnbd|V>`JoPOEESBTn)Fo{uH;#k}8E^CrPj1A?f`C0F<>nDGiryS`lwF2~B^8td%)M<{|PV z_?N5una>?57j6iYBqN9{XimyLs%lmy~I4)e=81dcnqq(6Jn_V z@9Qvo*2H;n&O_4N`%U;N;<)>*HnJ3X$j!uq?E3vJUgw0-vp=^)cl^2H&O8LE0gV7u z1n_seUn^O4_70zq111}Rj=AYQcLUEA{eJNKSgChX)AJm&9SgB_j5GgnwGe_lh#$BEV*)&3F? zr)_ec^Xa8{Wh0ATVM=De4bUY^i)8;IJZV4-hvKoxnVo`FN?dHk)T8`A6cC}Hg8+n& z>Wz0oHVW3D#itQqQ9?%$q#EJo-pG=_W9Ns*pkodj*?qpKyQc-fx+m!w0TK5Af&<>O zjeOMBe$%Lf*vZ?!rX0TAOO!<4Q7%h(^`N%Zv%K8&|Fi(1y;5p^2aS$-*Jk7jkCV6# zzUA3>QvgRaq#WDJ8#-^8mreLeeJG!;9bT5oIa$%w)AJ5*Ms+V5pv!yDvL4049}67H zN+NtLi~!eP!O%-jbe+O8C<+)gUYmUVk~s~#@HEw1#x9i$F6Oa@#=2GC6CI6nK|F*u z(Uako2qQuSR^#MeuizFH;Y z+sYTZDmbS!#ac`W)h~2WL%jVW5@j;%IsnZi(J3G`KQu4EgObwpgB17SDpdVkrs$z( zj;l*qw- z0E4IuJJ&?g9?bp-uBwu_J48FmXG!D&o~oEJ*SNW&@eZJ@%5NR#<^%($5h%@BjQHAkBk4!aUSgV8+~EP6d)5|i$zdsQAYLH+KtAj z8t%hdT>z{5_be6(7~vI+Z?X=JC+$OU=fVbzw$aZ}EFy|184EApo6$%~c({h6e7X#M zF-;R7-EBAJin6b^rB{M#dr`#V1!DQN$=k3*BE15$#(yRtXAMpFU4spo!Evh9*T1qs zXlia@!{Q_RBgAW-z@Hlx?d?Q^y)ta^`f3~m^J-he30{hWsi`T39{{YtHonlba_L@z z@XNzbq3y6p=<;Kl&_SSwRJ6!6(L+*ym5l2%SWKqMmWpAo!`yWlYE~&Is4uGUCXkyY zT?)i{=UTS5CKU{{l#3wxgUOM6@%`YN)Sg-(S~ql^fc^Of780~MOP>`a|KnUAR;oMj zL*bPhE~ye6^S~hQxuq-HXy;9Yyp_c89ygnj{cHc21@tpYN))42>M|}>l}6`4%Mv3QC>#0}H^Z|)cSN^+dQybgBY(<1kIpk}JE@O^kbqk!R z!hUT#Vn?uV_MR@3b(3gSpUGY$btibB5NL-BPX{CLK-(5X7~1jAW~F>N!9z?wVUW`c zYK2AYlaHs!^(L86L2B#9o%zgg^oZ+Ymh%ctTC?pe=oG7mGAD8s)8hsjRw1j)(q@h_nn zZBxeDjEQYnrmCXCH=##+O^&#|8=fLx6{vSwTXIn!txWG-Mq($0)R}%^e|pIcKe-TT z&6INlJ2?*l@HJm`-VcFHIcsyii%sXSlSVS3bJK5GtsG>d+X`gK&54Ys3vopn&a1yN z5;ob;WEFl&mRX$lElpLIp{%qc=v9&D%xQK{iY4W{F#ZA@*vYtab?^E$N(^Q^<*qzq{Lt@51LWoW|Vuma_k?a@P?a z!Sz=I4qLP>12IrO$(EYHuOPa&%6pB>#_Nx*f?>%eSvDi z*EG>oZfgv$fHN!yUb&-{yj+nlkytjhaGJ=Na=L7^)8ovvbIO+6`>LL?`>62t;wrH$ z%v{myq!VO?18^^hIfE^PDb^HcxWnRg`Nd6K#83iR0*p8;BPcrI*RbPc6!EvI!Ab07 z+a7j(KKXYO)7vZmz0guOL=r+P1Wv$NMv?46K?rgEGIw{9ZU;Uljib`p+lDJs6IL~cGV)E@-3EO_;Bkn+V*^F7^ zf2tuh3%OQly@z!>9|zp2sl|_Pwka=RQO`Uv#n0yZ5=zdJoBLs_=e4AMZL98o4R@vn zg*CnWJcK(T?)l>Y$s13^taioR9Ci#ZY=ZwHOmO#DHf@MeTs~w7Nd{a-xDsX-jJtPN zhJ7oVJm!xWp#Jx2BouM@t`7`r&=IJF_CGU@@I~g!MfqyVd>EL%M_V0|hqvGnEb}VSG%C%g zE?N>Na?7p?mE`!%(e&=^SA~ux5!|>TnOEj+6OMMlL?jySB#KbPgg5UDF=F_@>Jr9ZX+)u6(@Xwde8;GdT}VF&4fG& za&u9sdXKFA@7}F0jsSS`Lauy_w$Sb+{VgG5Za_WZa-S?)4-jpgu8hTD@4VI_Nnb~{gBoFl(}NAU8vaOGN#z!{y>f_ItDNqsKhY{zlQvrKtUvi- z)t|vP@%O3zh48V>yE9Re5$RR)R{6!^{8g1t6<9n~lg7f1$)M^@F3QKk^yZ4p66PK0 zp}Abp<|B05!{Z0S@WNrHdQa}bxEYM$v5i6#ZG#OqSB;dEhnlg^oS(er^mm1?{C2_v zCo;h~kxj}A&99Os^b!ilTI5@}Q|M4htm$2m$MwS)RN%{+&*_DQCHdUZzrsYvHX~O4 z))mDRWjxZ*EP_5sd(Xc&yKk?*Puu~ zNVSL~%3d_(O0-n*1HA#*AFiP2zv z!4v;B|9VcY6;j1adKpj}pm}e$a{em|mJe zAlxBxuWTC1Ht0F$78VX#3~aPgXYwJT(5s&6meMoG36IcDA<3|c+zA)rIW8VB=-yOs zk|msFX^x}e?;?P{t`<;dCw@JicA*OSPDGhN^<8-ZWnrGvh52rK7A9i~x439E|M=o@#@U0KaLTKf<&kqR^gge+v6iygR zj9B3$)}lxO#-hO)GTfIx|2@;_I%8PhN(kv?4`D`!uk|W!|3_Db{$eTsuPm*53ktIO zvvngxDieOzPZ(bq4tafi|FDA@bD5a`g)f1xX)5Gbq1=bk@8e3pD-dtg)g`XRogfFj z_%HeRlPcqK0}68TukJ*Mdw?=))nm6{kkpn`R5*KuTZ;Jikiq2ae3ieq7@5=?fJ~-^ zO=JnXXous!;7(O>RH3AoP9Vh9aDkFGBi@qbDAT)bwty;wus3Kliy&4NIg<~r7dXJv z*Mf7Q&~Q{5m1=2iZEaxzEmO!3E`7Y$VWkto+;oB4$3d19@;XLk?dibXcOD{#DBMx* z0g~9yl1cctp>cSMWNjpVCMk4ydfL>p6cOhM$>c+doQ6*P<(v8EP}$moWd5rqgLD$z zvHg1{`m*QFKWiy)tb5z4R*QRJu5jj`g8S~jc+vMHTc|o+7dc7N2#dJ=>TF9YO_I^hIGhAY zaDb!;iQehAZR8>nW~_3+nn64FDE8JnAtRKOw-$8MA9jm3Rb)glOCEo8dhD4tV^mv@ zALHk`==fZ_^;9z{)$NcQ2tIn|+(UGBARJ_rBR6|Mw|E)A+d%t=v=XQf|9Nb|pfN~W zqE?>8?u=h~DwSaLwKY_yLoi73H|7&)yLl2qabbR0jR-aJE#f6g5Xmt>GSp0XRBamh z2%#1}miH}%rhzN_CP!U^E((h18UrRW-4>uBARI#LLzfmRi)!X#l&A(12pSb)RY(2| z&dmm20)C-dxIOv{{s#UaF+g(RvuUYTX9fm_p7T0hN|OM`E3U?md<-8h=i57YxQJ?;eF$I3H%KbS$-LPNxbUckAQ?4HBPh~{BWu^o+( z1dH3oJUk9+)!#+IwM}QCaD{G*EmQ(p7-coy>Rru(f7a_8iP(B?En(nRnHTAVYi|Gh zQ+^;*OZxU+&%?@@N$K{San-2Pm``jfQeT*I`qY^Szf`lYBH}8<^$Y(@r=&T_@^w1$ zRlgu7hQ6HUqx{MctwPNPo+SVE-8I}`JGXgu@8 zJ<18G7~|g2s9MApE4g=@m2ggd@ilsdja>h1qWKKmngp z5|=K^M3g=2oB4%>-PJ@zAc{0*h?aocp@3YY=4Td4;ka}~;9+lXS+YX3#Be*GY)3r* zRB}-z6i<<|1C{>A>GtY_D;VLP?rbs2rK(Vpc3L_Yk<(upOKSxBnn+4i_}e9ro*kY>?eFR5qnMs) z(iBa}Ec|;8R}pWxnEC1IPZS{cUfy#7%ZV@*9J35tmEbjAz=FL%9Telni41PICY}_i zIpc$LbAemaPV9>#iY1G=eShSS#QK(zjfDHl2$nR#l+Yfa(RX#d8kJ_L05>tA+h76n z0sIfnI?l~@uMT)NZ{-UbN0F|?3!lBykjKWe^C@SanF~`~>vlh}7{k~Yf-!jr8 zC+adXMB{2F^R3c4-6LZm_h~63GX?QK&{&{Q(|S*ko0m|;=IZ+k@4e??orf+%TVW)s z+EzCl9JY;6fUk0t#+WE&w@rzP!Lew4Tv?Jk5PYC4q~un4>7~#!WN=%RV3BX~ifu%> zPf;Q)dg&8FzavQ9^ojwevRWvKxe`O+(6y)27t=;wb3kFpuv{*Az{2*)0^c}9_k72c z_f+5nO}L0dDfqKNqr!e5e;t1B8Z(&hI0K%t((4|@km|EBdS!^xItVjmhA_;-9auF# zFa9IrT0Wi7$j9nSA9J=JEcY_RabMQB~^Wy+klbqIE#Oz$iB}GeoE+-Flgp=5g4B zr#i14>eoOh9w5m+ z?V;?GU!~XiFilWs$Pk4(m*Jl1!bMA0p6jYlV!Y>reK(T?$tp~z3lCB~itfCtI0a=T zzW#hiuvP$l*ZQ_c(+UE8-IUO%J-C2qm0p|7 zJjl*3?VwfJBHok02f?Jn&6UlkU`gM3$B=0NDpPk`ylM@gku;==VKizXO$dTp#lPpYo3xb=dx#S?NnS8gD74V2mY%=i;`a_xQK>% z8s^bLuaSGXO&5Nt=-BeY))Dhkq}ucY_Eef|H5SLUe~;*ilVpVyD57;`KmA%ZGAWWZ zyWR%xBUyuf6bIzT3}js+f&@h1|;8j`Tz* zNUl~}mUv|`waeXw%1%hh9yoy%Gtid~t>B=VvJ7yt7%1(=VG%Y|q~*0+|B* zpJCS$L-?YjUamk*m`x42U)@XDarS!q*1>zIjd%5ib$XZ2P(HY8z7SZt_3R9}ISDom z!P2rsqo9ClIR|~N4)|whz|i-qhl;tLccxheABJguSL=ROO3FqnHxY(+uI*St<%wlu zSe>FW z>tbJHO?*zNlAEu!SV)1|l8=6}-3Ns$uMcpufIrf2b&fJmV$wgbKA`lEjnfr9QI-(7 zh;%y&>XOwV*1|2>pK(bp@SK+w!0;M)B~YeKz3nJGw>Hz6DXOEbZL7C25b*>$MROHi zy{HAGo1LNBw6MKU4OgGLFVd|m*;E9%FkN&6U>dm_-274QjJH`$(jKQcjt?yzTT}5Tsuo^Fps|QNm!**_#Y=%>VlMc&3e$0V2*GN8imtAw2naK3@n@Oj9O`V;;?{DGfO<+>oYv8@c$o&I@dziqk(iDC|#<41Jf8B zXgNkWV7tMT;r=rO0Q)8s3yO%xq4$H(i9T2_mT02gVIhU?DuZAG+@?T&XWyvT8HNuh zF*9`z9GERiv93%R;p4yQ-^_jhin6qts%rj6O6DOXn;$ILSAUArLSOFXAA8=;e=Hah29|VIpof-x<)i+<@^XQe$N}$b z2pdq(oie40)g>Qv@m(J-v!u&F(DY^XyGpNWXn61@8TleX&*)wUgtO4k35K4+%lA&| z<*(HwFhssxi_mKUZ)FTv*f$3rQ4{4;hr!0`oe=aH%uIp@3^Suc?G$2fs_my{K;eE} z?x$sYRqn=sds=de`b?&4OoiOlOpq~>kzK(P(kjO~1>4n`G=(YP@e1OI4-Ml0qK6c0 zLUGr%H+xtj0OEs+HR&LBW$+c&4)4yEkA<>q@j~o6(`%%gFg@eUQj$~^*g%$X^JBkW z;C}->K;G)4e1kYHCrGF0p@fkl@9bBv2?pUQb#Hm>Ej131*_Z?UAZCK3eH1$)4UieL z7Voc24P5OwafhKslZ)u<&@RsDg?$e8*fZxb(BuHyWPqD~Y%pv`aoYI96vN(Sa3t=7 zcylPXpg?E$@_GG2VR9}|nWj8iLC*97Iv&Jc(}gTeXwCfI0@>=}Ghvmsv-#ghV0|je z=`VD(JM>%o=Tpef6=JWILvr3^FWvFd-YVcls)qN)t`$wO z*`rua{{xq7O6O%^|DHqD`YlE!*CK|pk$cTtOg~(xit$+2#{SG~QaIFJ2G-)>YEf8d z0YeoR@}R*1kgL-aOi>dL+Ce~Km=8Y~=seph2K!&LHS>SDXtzanULL^AjK7!>b=5es zEj1t4m9L0{GGGi49^n_*fz2WhhO`7NeR7BiRN6%-MkzT4%YyE?*1 zyGBZ==?4w>?dhS03JHsx-uAS8B1p!JtLG7^d=*ML&^IFocN%!|J=>3+l=Nk0YwiiW zcJV>7qmI+>8bsWgXvuMSb3xgmO&|c4&c;StI~-;b%=^t~f`KwY`_L>-Q-Rf^$=fj3 z@xzuy?>NEy)u2^$Bg%iWw+^1mTcn0w_-{tw+SqcIq6@hhvTCMzeE3DCUeWJ9zDU{9jMNNK z>;8ONLBS#BG&kX((P^pKu8UQGJ1==*d_%;NMnG03*PO;8L@4-!I1+O9)18DZ&his| zJ|;RY&p`Xu&8ng2jkM*x2?06GIA@#;U7hYUINX%t#NKho!!^b|hA9upCBi~847tNKP<);WkX&*5i~7va$~LR!X39=IZ}x0os$_Or|9JEIw%u zQ6nkjqUtgXpRAnx`vj8Q!|eK%edyBmwt?sm5)c*bKztrpO{8A_wVs^M{j2L!+#Z#) zDM$5>%_e_4B)q?zRq>Wf@Hzr*!)T?(A%Ug{`z60fcowSNF0%yCXny{e^LnUJ&FJ7m znh5_wn?1eY}4CTcwA&k@Kb&j#H}8ft{pB})iak3^1b{6Tj=CDhu}jg?Da$HSi~P&sIWFK?m?$bPiVnD zJ5h0UJh^5gRN*4j^vlW4h^&UzQAWO#JKTQFrgyj6pwS) z4u=ELsbDVO3c0_zMon>s#Z5YS$>n?3!I|&mV5frU2^q}8do9ph4R` zDPy;dq)UWVuF9*Z*Cnm-Y|u~#fl6Ir&&wn$wQ9Nc2-H7Ma(cm(?F68c+NMJ7^$+8H z)CpSCkWFC`T@A8@InOQD?iZ{-R*R z*=wZ%eD9)6bOMXLu8?_RCjM*FrqRF!Ea}<(cS7HBy%th&mYgv%bW=PD;(>wBJ$?`_ z*3(0v$DbbF$-4Pb;(x!o{cGN}Yd+^T!WL0P<)uEFdD<6KU;Fd-in;^tCcYj0>q-8L z7%-DSUF8vddPp&!`?)6d_fafJGkL2)b92`9yPU+m2a`F_r`v(!`+?3^d8w}VUb`Sw zRZUiC$mJH%J$(1Yd)JK}EC0&2Y>%WtXPF$7J#8F+kUC|&>@Bp!H{niLC@Yrj3e-GY zMrGCj)3f?v(UEXzpovBpC^U&upGy7ka*8PG-)&2QYPqL5{X6APoy%d zqY6QNfW3sCFlrLu6`9$7=2)!kc(wb?Zcz+d#R9HI#8Ko_)ero@S-;)c{L!#5)tzxf zoIAXOywIX-#$dcCP(ZDD>7Y())3}qx7M^uFw#hc&xtnNi{-u3O zNO3RPVF8XTM~6hdirbK9v1?NOu!Sj1qQcaEQpXZHYCAwCyTPIty#~fL+!p7HRS6RwAi3B{a;q5zdoVz^PQxDX%6^c zzCpyHmXU5k5LZl>c5h7*?Y|F>bfvq}J2lhjw4Gyg9+&%50o-d#If>>*)CtN_>}?Tf zFNH$0W3|dB%aY0GJ5*evIVA`OK8W%E$wB?zEID6SRmIrfGji^nQ%Ys%h}!it>?Q%H zD5tIuX2&iUwlp?+4)*@UN^X{ijmq#ycz?02@Bp9e(%y5>7E;~MEt1^cWwx!? zuIye?u~@#i(r>lhK^<>T;YWu{eiro&7{gvCJg&M$1obkgKK9S9z-ZR9N7~`BHZPYn zSNi50>TCVzO`CKjujdh`qVIm~xO7kZolTvc44brpbJWL#77l3Evk8EpLa)x|<{`(` zpF0>R;9D!J>{V$hugXoE^ATu;&=syO;7c5~F0L;7-!({g%>#uL zP_1pMih7FG@v>>DgxImD3D;+9)9u%+_ulj^Tk-xgs6&0+*)jS4;%uJ`SbgXlXnMUc zlcJ0w>fS|dXZJftQKjdx8{+k{oY<&ah1+=El6RyYR6I@OP6$aTNSmWY_>pPtauoAY zZ;}}F^Il0t;Q{I$vipSIHb1-&Y__T=zuoHf=FwpYCj~!;0$LC866E&~sDt7wGxnba zhQE_7p33;erjgcR`XQ*A2&rQ#q1_Rj&SOaD!q@Vg!;!D0Wxvq7&duN6K3 z_33BH5BTDoPJM$ns-N997UCyY8otnqETyh=Mnow7O)0_OK2b%ij0_+e^`?rN^0sa2 zR;G*i-K~8%^HAig7H4c~`aZP7d05p67Z;NiuV$?ae``GfyJ8H~(xnfw1R>x&3wl*# zGh7biGIvjBb_dwUd=U}-RgU>>roulP510g#Od;*He^x1{_-^KZbK)iShKKqg8&GGi zV|Pkql|S1oBM>I`72-2I?W*L-;2cl{=# zi4Df&_?3rJ)ch`eG`6I2!PM1PQSJbACS`#kD;==Djt3+Sf>ricki&Zf&V5zvBsYoxjLmO(-&~VG6&5FHghSfKr5*v7ff)AQtrfPrq zQ2DYteP`r2doT2j8DF$p>DXE*zdfehQB^g_Zg=WlkuU+&EASzWUSp#gDF=MpLCM^O z=rW_hEO7yicEI2@@=}lp@$|j!M@rSii@fiK7gI?^ru8ZxZiFARv3gL85XoYtMBU*M zIYkrCQlS;gprY1250YaL^w_PFPud~qKuWZ!&o&ebH*Si6n=^#EF%$GO(NHG_52lX7 zQ((Cieu^bOWqsgZP3+&A74WbEPnw!+OYBeZX&vvm%)bAF{mK0W4yIERii3#{IlLWs zZy*}%H8|EEy*+!ZI(=^2$G*qPz4-ixdoz>>Ct;FW7Js!ZXuNZJ!PDFbtg4?5I_n+3 z0I`Y^(pVb`G~jLwUl2OB-C(2~8FF{PBcfr#;eyd} zcK^Jws@V&fYaAe?B^WYY|9g4WaoCd5%MDRT`HQ>m!Dd#0CP9Ud^VVK04+GQ zy_JwpHwp&-0UNWo^3k3ZU$mOiPdVrLK7oHN0`j|mSE5#gZqu8+N`)z`MZtJh#_72n zQ`B_#T`|roTepOkT;W{Okt$v)CG=hJS5bcnhhEE;De1Xv3tH-(h)YE1+9)Ami+*wm z#&t%d8vzDgTZ=2`&$iUTF{{fXpo0`G@6IV+iVC4qu()otZ1(r>GzPss@%Bq*d}QdD z)~h@uD`g>q6u0JQxgFIDGWQb{lVSlYi%E@{tlbBKtNWply;?T!mmG7GO}umD2NBxt zLs8bCn*hz@`SW1I(wK*&Tk-8osd|3+B9e6MRx_vM zd`M(N*Eu6yi6Dl3MaxUFE!r3N07NOxgn)~*FMbF4&0iq%H3{aZ^I(AkVk`j9MjeBC z`^Lsb^{8Y3j=7>4wF0HCf6#DwhcC}amGrUK4?l&zj!-k8c;jM1ms1SXcc&C7B$)_1 z@gE>Pc*RAL`n4GF7W&Y6*4mb6S>+aSzYdqbGot3;E~cN>Ip+i}Arpo02O=e~e9ydDT76lE@ z&mSD`vaM?cdmTZd$w7&<`879KSWu0Ep%75KKsAs~LE(nxqXY|D=m<7Llx}*(7@ZkB zfN6Sw6QLNIF|)|~B%BY$ODIBxu4EPe$6$9xH|f^QFOp^xhjezdB!@BL~+Q?`?}5zE7jmc0%wwx$SOe zZ;x2z=o%=_sqKpSyi@nv?*@99Gst3MBeLbMc>nUS%VdJJBi`z-$PPcZyIMV)fs zRzC8h0Mo>bhAqOEHT}5-lQ0F&m2r0LZ&!Yw;YaC_O%pmg_GmlmMd~4}Pd3m+bd$o7 zdN?tx@Wj&im;NW@94l|ro+uS{WIbI~?~<;C0_WYg`0~|oL4;`C2{M$=?u|dlfvKmh z$pnx%%a@R}%h!Jz*7xb@1$hdQQVXE#6yXxtXOZU(xon$MGGwL>d!;kS0 zEj}JSdNgC7H2~4Zw#mo=tB}eQJ%bZ!5JmPm_KVg*n5o9Gm_51_DapD2g(Ty$fUYrA zpk8qKNC5B?@hMmdNGJ>2iCeBG$@7d5QQhnj2~vnF2F)XuNuo;X%9W;Q`VIdYQ;XZB&%*HK@u(G$!(Cm2VNOY4yujmzfyjl0 zCX1M3*c$)AUhUy zjg}_c$ATT`=zzKEo98nioi9igbe6Wk=IqBI;Rxeg61P{6@HUfx`gA$r`u z(ncDdc=L$OSGSws6v6m?!BUzR1U=QZpVsz-o2QHzyc1f)qJOwxc80vz-U*?}V10sV zLJun~v8~VLeRH=cOo1lhd+5jmoXr2yy*M9D%RgRU{LcPXXHFyFZXo2x;hc1fk^5!0 zr)!QU_ZVAT(+B8*=YP9{0K4StUlX!AqE=wc;HFF2CRKv#52%#Vino#%Kxh~*43jB4 zI|>esAVh`txI?zumOv{zu~4+ZpW1Gs&A4$#WMXC0_yT-9%(Ph*F1fJ0{_cj>&BRaV zR97C2ryE>tET6mPo<^4f!n885hhaVe=ovGWxTLcKWe2X7z^elNxgW4L+$QN)da^RE zrfT(P&L34Rvck#Ye{kM7g$zsHmiM@F z9PQf(-)uTeeK4FgYk&CL?A$;#Fs5PD7)Hw?pvMD#aRvQBqko{UXB~L$&s(qZ>--c6yVU!jb_z3~bFe z3R_VG;8q6{SCr62R-0U9%7+l9_JU+rPVt>$tUhu-oGLJEy8;A@ zPeLN(=ka>H)$Q9A#0iIi>0ZvO+)b3PFmt_sos1Y(2g? zh7jD-L73Pj%z*Kq^tF1revi%#FK?q;qhO_8t`r;oL8Pu!+5X+xUZ|J_C^=jSJ7Ask zM);mzG=tF+0H8Rnj5lol-L+omfAl-Q)xE@;S~N4Dr>E{=6v83m_gT1xyQbB~>aBE5 z6w1y*p#H_4Q3kKB#cPd~_Inlg|GdV(Jd}tzfLIQV*#As&FY1*5M&hZR1#0=g@{gM_ zT(^n-2|5u!+zn=09S0+5%Zqa~v$-7enk{1EIc zb+!p7ui3QJ{~%FC{-^3y1UL}DEGSSr-}o%vx{2Jc#?gHjus)mfpeAfxV35|oI;?%V zgP=9LWg*%Ao{2#^96)_F$>6f*ifms2y z#FNMEWX>Vwo8VDl_SK|=sdl`5qVwIAv=Q%VvYHD`w5=e zhe`O_`CyVBX<0sIb^`z z(sV}tflKVBB5fs-M_x0PE}2+cH;o6_$h?wac(rYw zu0p6k69Y!<)8gsK0hM#DrmlbD_SnpBf=4tIzXSIg*+N6xt%yIdJKeGR(Am_Y30M8h z?kq}|NoGx;TVMQRgFd$xzK*m)n|LkJd{X!OnNy!b0p_%GqG_cN%5a5+x}uz}9}m1) z>N$<+C@_#czL!CI^l;fD*8Ee$VN-F@G;IXofX41T@j9!31q zM_D=eiE?v&p6oNL85gof_DGx|8~uUR8iAt-A^`sscRv^pVY^AD=}lZ19+ZX!^A_?O z32zieG=Caj02v6zw0B{{mAk?u0fA<%+&Aj|9gkxjfPf_mU+Y)+T7=MxKq;#g^uqxU z$%Agv!*_VlmCeu2!r28xR$cLGK9@qnKrcNXMyW*!nM0+#4Biv*fxp;;#xuV}U&uka zGVr>?0WY^uXb*Q{ZOu_?W$RyYpeG>bb#o3^+(!*~Z0K=5{reZlnO6P8nYwWEZo(*H zEbSI@f5-LT_~oPri2&ENg3c(7R@pRcp6TK86x6|k8$juWEb>vMfWdDNox+U#LGPCx z`JFSiuRGLTBGVZR6`q$9SAV6)9DN#aHH`E5CUEZm{XU}zBq!*rL4;epu;P;rUlnAT z6kRPXxFHynJLqqm3dG1mLNfD?MCG5gum4HafxVz+Nx2|G>%5N3BeHpHN3`H zy2d+{2cvjnUt9vrD z%3GZ7yD-!<|L3ovl4!rZZYeTmBkds2KR7tyRp7i}XF$)3r9Q$8G)AoM^1YP6uHn(c zCcbvAKuiAE;A1rD%AdHg^!vOfnbzTS(B*_w<#=*e&oyjXY4C{l7p~j zC3jFo$i??8B?*U}Y)NDF4V;9T8wq7GQOHN8rZp>tpy`2ZDI?<@+<~#k!x$_1dgBXE z%Jhg15wxD#I`O5 zr4lFrxsUv#8+g*=lfb~ky_cWR_zZkPTL;E$hyLfxsw+O>IsiW)4)hgx)-|&R(9>BC zvF(?rxC4e(&&xj=#xFHCYUT3tJMc4{VHLM^l8t^=5?eRs((?W=-pEx`Jc5l>`to)B zB!_3&ep!-SM}I_(Lr z0q^sc3+VA5S*e~l**TLuRQa*_h#xSVx_OayT=YOa% zhMba)JS{zqK1V5+z=*m&etbE`b7yn#U;khOs3vrSimtJmY3u3zThA$h=!JeUJw;}O z8So@lRKxY>+Kvtsxnp1Hr)|&$1yB+qP?s6~rGs1#iCtN*v<1d)ZaUjVPKJ7}`!1aF z$K1|*;a6sYdk`ueWNDjCpk`n)*i%!3S;qpkAke4L@e#Y*gAif~1 zg&}qtladn|TlhDyU>h+&cjB_QfQ_>dD%LQP1NYBW*u~Rxu<4aG2S;fodhhr>VL&hgsyVm90|F$2NXuPf90f5G~L^%BA zy!+;=zpG7b7xRr0#oT;0oEQ@Jccq2t;Q}wgtJySIn1i1G77j;OU;B!b#99ar>WGSto z^v>lc|J1nCA2pozbDR56gEgYt>8J3$^Y#OQyo@^~S2 zt4X{o*LCGITkG;(mihL~H{*7Ioz|dgfbcb;Mf!6r7sHT);dk<9A}SW4k`|6%cIwecffBhqiiMSuh-6rFcu`L5Yd4-h zNVtNscDpIYV7OzHP?vm4!~I)Y)$%ud5~kcgvk)xn>w$T2J_J!=px=RzJRC&e4J3}W_MbwiX!s`OeR`H0@`$ftSFf$a( z(>lStL39n|=b7EStiHOYWBnPky-OI;h$o8Mvzc%PEL+*yE;UBWL4@GG4Rm{Od>9<` z1mpF*-SZ(efVQ^FN!*(Zf55t=S^rI&0 zUe?IHn`3(z#(DWkdb+y5Er#J}^!hOO$5`qktUV@?LSetzksb*a?I^uGJ0L6CT-D=H zhFApj9Q{`IDZ0r0kSFVd+8fwa+S(#vxa@==Z|;(y%Zn~i;jcizH`kkEd9)T*He^+< z0ylW8!CaEji$KkWk|C%xlODnutPq=1_4%+q0PpFjCP-A)k9#g1r;icYNI$Jtv|}hW z+Us5Y)9ZoSb+^{NjIdz>IxAhv*c-NOH<&nr&$e=~Ps!rNMb(W#b%})$iZ~z`BWP)T zPh(d;GE;K*bj3dQ!X;oPyiweNdcL`C6v+~*iIySo4RA>SgIXPL_y`Ps1ATpcxDQG_ zP`}Yndwg+jdGqhc$cbt}=Ysx?LC@G|4Enm%$H39Zb9uU?@uDnT5^Tt+{Z=sb2i0&V zQ`6Tp$n9nyEPuLz-0zJYW`b{Iy}NGBr1Tw{5p=o|9f1v~?g#(g>|^+2_h~WmwTj$_ zgQ~VXYYYr4svOE7ySpz8qQpn(e{@RLpdzUQcX*(`z*(DF63PprqBehCO}Oe}fQo^y zg_zFI2F{uL!i!(`H)S0?qME%k6}vOlo}vkn50DI;qa3S8`EW^36`Cl$ug_&IInFsj zwqFN)jFp6zSO3cG^7`hS^8VI-i=i>xOi-iqc*fQ6AaubPpciAImOL^%#_K8?Ae zABv;kC>hz=>j?t?Ue$FlJUY>e!dL5Q-t|icA~v;8b%F7 zIb-3p2Bhr2HG|#Dhm;sfTlODps%*IZp1;sWZUycy%SKN6-cc2~a5vo_Pw6yWCntzf zV8BJYwR`YM&LzGX(*x%aE!W32 zV|IiY5Q0juP(MyOEz?`vPeN;qUkZNj33_}v*G5;E!!9qX|Amp_C65!50ZT~W( z#-$6qBa3uEy7}lFNyx%B*b9*EH-^7Ag00k?6$b&$^s~3>}t9_?bZpd1?(ruhW7n?yGKX{Hi!}<4^pwe zPFB16G8vKyBJ6yhkC+Zwl^1v)D^a`Gv%Gp(`z+=|pgKSD)2?czv5f#Q?k%rVZFy`*nC2<^oSPKs+!}s1Yp*qo};*lY!qY z6P5TW4f4<5jCn0c(63jGPcVu7bjOetA2mbJ*v;YL>YGem6YQT)+l6PV{&5e`&KjHJ9!)e zkWz3W1q*b1edpw&yfEu(%Xoc5!(YG<3v^-OOMr6acFsx)@B;q?3`#6hhes**+aM7ki>pLe*k5X$4 zV_t2zy%(*h!Hp9$`Mn7I3<1aVb2+`aWK$ZH9{0_5Y+jm=HBMpi1>?79W(Y#GltKtf?CGKxAB z9-m}lirnQxSy-yn}q9e|ZtJ zOLU7o`+N2ZEuN1AE&!_fzr*8~P6Dq!|GDORw3Zv;Mc6z}#V>nQWD9i@E?o-Ui$8YDqH11vec? z>YYYFjoAoNSQ=&aesg2_(|!>w?8v2$`T~yuRu4|V*FWZVo!Cu6_!*-PC#5TaE^@eO zyt%d)kYggl99h~&;Wtx5bk^wB@t3wTZ!$&!UjdfEp2g9ZHbu#0_Oyoq zK!gCom#@`NLAF+J<_7>|2nHv(QxIxuXk-NZnu>HHbp2;F#Nm{!@!tT=_neCqGU)VA zZ-(S`uh9#deY=9U%17&%uwJnbc)^b2@sZQ;A&9YcpY;LC59jNx6L~sdy#bf-CHGpE zH|3BXejq+YiU^_Z>&O1Wdl0Ge`t25~rsKCw-E%+}UQ976d*KiuK_2q&Skiz8gq!VP z8N|I;5lHF+SU*UP3ljGy#cw2&sFvTG(f;nXHP--SH*f~7ROxbNRTwl8UYLY>F;?m` z2K$gw#--WdVY`ueSU3=k%mn#%KzI!J8I zegb&^*WFFPq~%k+@s>r3I}{MMq+l$iSa#{jrvp3ude~aYI0IjS=#v#U=|B!fAAv2P zptE%7bQ386zMI!UAkD+z&92{O(Y#~SrHQLAAdrp-B1Wuasaizee|H-r66hg()&F?4 z7@%3@euqF_+)l4wxJ1ai*-H-iOa_1l}NU)=XBqv&188}@hD`7C0u9ujPRiMGeBAyJ7=e}Ccu9>DRCS|a}NcFQ0?L-+VZgg{NFj3((nF?&6?Nt2D|7bNy3Yn#z|tu|`Y^{y9B)WS}>S`*rf$?rAiyT+U45iL*#SjP%X=?&6(JB|Y6dor zp*r;pq3L+;&9~>zK{}oR2c@7P!zVyV0nQGTq`a0uGJD7%k<&I}AVR9(o=-tdR#s+m zBOO};LI=%Xpv@lZ=UP@x6JeJBTjdww97N*=XtI|Tuh9eR>sfQ2=O9vn8@8wiA(3>+t4E3BLvK7UVQq5-5`P|#VH_Z)Gbe)MP$=?pfN=?yoU zZN1`KSN;90t!F-TG&h6bG(mo#G6#Ct(ML~V&!U$1j&J$-3S5o2^aN6 zD)yJnG6jL^^Bch<>--1|6YprF&z?@+jqv(e`t(qH`&e zAwN~w_Gb8b+zG}u-->~PT`a--Z{6h}7Tl22hlud;vdd9l+-fiRkU_96+b1D}zH9tj zq2{Z#&#r85gSRZH6|lQY*S^z|@pjt4eYl8GFr}=5 znVz0P(7Qi;LU*A)wDq+ZFdVen!7f00c`)b(Ys^9Bq%90v|&00c&h2${=pFoL> zuPbk_2xgwqo_#*~_QXj+TPT(U_ri51&YZW+jZ1g8KAP^2kZMuQn9S77eT>01P2SP< zU%N;Cd0L&{0C~xd;%xC>3NRN*_6Z{96&i@Mk?ie_nFOi{EF_qzY$}wY$PM!?pXXXf zR7K;O@y7+pj8+`3WR%$pm`Y8!$$ecjQ*IS|mU%uTS3r1s=}%Cw=kqM8S3-k3CR z#Fg}hKezgI-uV^Rd|zhJFbB?Nz#RlX*@^*csYg-^`*Okn{q-YL5eN#Pq=g7)%%a$B zYCPyHd>hJEEDLy+PMC1NZ0?hvzx=$FhjLMBNM_8C{s@Id-S?lrf}+=!;CGWQO5Qp_ zIso#|F(S3^W0sCaVCo-V^$^ykf?(-D$9&jvLVC(0vP7mQkQX2jyuOjcOua#uMVm`1 z6b`$9!oa)vIrB@TSq=(p=sGxxuusy2(AwSg$%#R=WfgvD(PLA4APogL<`L;RKQ%no z#PEiGbYG!aW`!)A11G-ZVe?{^1nbIf&E|Fz2uoYXSB!craGa!n&{o(L8s9Lp z$`WE?5TB|mG>}w&P@RJnGa3<>F})xA$?hpusR_S!6mQd?{FCU!PEyA=6hbQrx zC;33c0>qBWUUq}QqAOEHQDkB69E9!M*-wK%qEN_osC+g3(o4&uhqkQJ9MzS377o&DiS1d9kWJy&Ts^2jUn+ai)Ze(K#OhD(s3EC zy?ynn1@UGu2Q@?Xmp=XYu|iJ^ja^NZRFy|!U|WGe7D`CMB%g9chPkZFIm}eHpl=eN zk0n)BHBBB{azf&8MM`}*Ig)F%#QRFjSS+IgLQVdqaoxmnVR&i8zjp=~5Mk5IkLH$& z{GmrfiboWC|GgI6&sP2M19{qIc(VwEGOkjQm0#L+gw6bG%~d?H=>P8a zdg%by4_h4W4Ze}}5|a14^Om@!jt$d9&u<4j1A>NkCZ*Ilg6fBD=u@%44Z&RE$ognrrM2Qt(6Vhup z-C{RJK>AB(VwXT-0ZtH|;b%=<-$|ho(F41&OT6nc0!Em;xL3m-$D(pYc8?L56Tv05 zW-`>0ALHEdAt9luJ1wW?fQ@*&c@U_Pm9^d&d(I2iOFIh+4vh{dwq3AKfRYO3Q(i*R z7Vizv{<)sZZ8!L>nK=T3uK<|3HDyP_B{tys!J6qiRNtZvnp=sjAkKog2=)D} zAsKr3$dvo}Z8$fH0jxA;=wdBK=(XxIRn1?=|60%vW=6=f*qYdg6bN?pYM@U!)dH#>c;~(|}T#)$hID`C4 zgYiECd{=W;zdadt7>%Y!C#TyN7bkn7TBed`CvmSq;+K&<(yfaL77IB(UboR}Qvq{h` z1d*UQHEd|3ZfL8(G=$>UnHr8{$Qd`}&#NAbOyIWSvUP&$~l> z54-MR9@P|^xhvZ1v{l@VtgG;leN&Lam2x#yMx&6hGM%$`RLO$?-4qz;T9w-axF03h zLCu6|?-HQUzpBU66~n#f2hVFKl$fAmMA1}ekwMGj-%UkvcV0WcFjKc-S5{_Mp`#co zoa}-bbpKX(fq5_OQf!G&7%iU&7J_K%v7)bQ?q;K|NgIY&R4DWq-+GQoJx8qqmD8>5k*Nw z!A@#Rk5EpCN5w10HXtk%Fr_N6gm;zvA=P6MJQkJ+-n2owLgfUmn`)eTJnDuCrRS-1 zskl=7SKBz2xp{QVDN3@;Ezx;9q)Fy_(7Km|P!?o`F}n??iJolDQkq1b7dj6fsU>?6 zRFH<62YIZ~kj>6_HDRTl$(?2{-o_@ncK0mcZ(oz_3WX*mFW^^l`|=RvHdWT7yjhqG zg3k*FP~`4<-RL6fNDb6%Avq+i5@UnBLF5dJ=+;U>Pagxb$MsA9IH+CIWxu*VE5H=$&j!bOW;bMC`X+FJnERRyMq+ho~iYHo3*suG-VD9|9{m+O@Wfoc9 z`b_T7_(ldD|uzmce_c37)?rc;9b0Y#P(J$P+UcaJYoOHAdQFq%-MfD z@(#N)Og0>$9NzZ#+Na>e>M>M!p;(!C&NvE|hN*O9LWqjP!!JJ`5-1rC%qyTuVf*?y z)r<`u;pvb;+EF)f9HKG4@DV?-)*@9qis7al8VoCEDU^~kyQM8Vm-aw5TiOhTK2Mye z!9Gic{PkO->puA`+U@((3TTe}X7mUtIzYGlGP<=d*Yz#C8%jCBroIdzuY(~_t*YjQ zZij8tKkEDH(*hexYJurs(h@>K_lx!mx5MaKzG5SIl83%x8IqE9e24asUStLnm7^Ur zIou6s8ava=rfzB|8+H}0?>SudEW10u$5KQ5C7GD=mffe8+PD~0e$G$aviRG98j$1A zSrn?Ijp0ZwxvPUog}Y6$sukvG2bT+0TN~kpdHL~_(KRU%qIMlqOd^%;9hH?7et0Fd zL?;viSoa}PPMJ1ghwYkyZhiNy0^qjYRWt02^wbfGTx4mu%8%E*yz5$E%SpMsV-*kx z<6RZB{Qd3F8}e)v{T}l^h|mOY9r7pSyrJBo&TIHvt|w?kL|>LrVSJ0IU@o@-&6Igs z0*d^$vPWTj`&F^;IU;`$D)Qz#+%*DpuAp0%i-f*p3E<5 zvxr@el1@Z34Rdg-yi?m%iKolHVXJ6=#4dy*PoXp5xX@)IpO*Ms2Xo3upaR3nNiQry zYMBq+aCOt;3N&40-c=c=Vo35d9ScL=YGE|dVHk7!NuwbhCP1cpKB1lTTblsFOuIyd ziuWf=mp(L|Sitc0*bJ~#7;BPTd5ff#m zw2nzdIokfgt zChrpYXEf_tvb;?ZJJi;;FxSNabwFl`xx%M|2sgevttI7kp~jX|EK-=4Nj5)z$(CMQ z%DoM7^BlKPYqHW+OLIFBZ5^6reK6*hGOl6$8y#6)412(>tg60@?9d#^P6;R@H6R## zwr-Mhy-@V7Uxn>*vYOe|r=7R+9|Zm}rqGGM@zVPt=@f`2vslt9ys#K*kV=l8w~_BB zZ=_J*Q77ojNSGRei93|c5F7KXvtP4JKI?$olU^~Y^T|#6zr`_ES8|!gxvJ~p*E`M1 zs9*}pOSR@~B1~6e72K|^rz#*0p|5*dRw2yZn-gh5u6|^=2q{^-uN8gFYu62|Z=1!m zfb1#As#3yA0%r&BB@MU&(=hY~y^d|y+aa0h1iaL8NZsaJ?goe0 z19M$NFNc=GQsI9>kkKN8e3C)UldvHYVWR1D3nVAfYt_0y(T?C5YIrGByjE~BB&#?rom+8kx<8q z3_8^uFBUnMQ8V|Ny0c)cJy4={5=&O_mhg765RkwielOnRf|=n;%;mxrdhu(DqsNO04?FXiv(d3rzjc zpvQ*L^kJ2yTpZ`{?;Azrj`#AeUVCBwY+!?nLDZpT2jO-rN=<-H+*aPKjNAtoF;K`u zkvpgh>(G6YDW5fER>pmq(B`jFwgglT!+|~!q=0l{^!TnibMQIsky_c^YSP7>s-ieR4Z+iI~Pn z36ech;L({g%|EM7%Nw)J82A{rghY|ZhTt7r_CHp_Q$`Xj?c8VesfO+sP1I1NNH3MY z|D;LZTlTQUmCe*4vn9f^W9L47Okgk+7O>6uPr$ld&6En$vlnZebD*?tMqL{gZbHqM`1x%NyEf5#F=377x%F8kI&8IOQW$Dt5QzX07= zgAa;myHefr+C9DjE1MHs47Q$~9kF6+OfOni#jEAQ-ZyEw{Pa&WSKUr zbQ!X4yAL(!?m9)W&$g{D2Pf{0@n*7<4Mz^7L?qRiW3nn`Rq31Jc?N@wq{cKf@|0`n zcZWX;s-abmCf;B6)u4~bW~a7RyRJDADgaZN?B#^zZ|9DBEg_i2vxU+sfBvpfGWJp{ zof4@7aF-AItEPHSkF<(Xo($n4hNaS!4&Nb$%xFH3 zm#>gmdUd-=S2ZJbjmgk-8|6`0>U&U07jATC4F29$BK+qbwNaY{Bmb#SqZY<}1T z6%B3lqtQ3M_er+8Tx?efr?>4dG57XGJE7?B$7N1c@-yLhG6%v$ds`@)TH0in34a%boSNodEZQzK126TTGycYI2VJ4bzYW$D_>{n4AWj{TFPQqGs;I zXDHNn^?ggkk2H^~y%t6KYDEwHbSFb&?Ncc^Jf6o|ucI7qkvH7mz}~|7q8QvH*=^Y~ zo7X(9n^w(gq~At~c#&;9hDqXmksbm%xKNdszeX67^uHLbWo620=tTk=D9K^gwr5y6 z$tMn{`$;*E9gT6{*a67EoeV47=8I_`wMxoNHn-%%b0me1i?U)XDP$U5m#OQyTLaJp zc|?tQ$BpG_UwzFlWK++|CaM1fQ-{HZxykmSukAX9^IeXAaVOeDQ+1A;M|JXM3^>9) z*1y;`3Hx-WNu|+LXoSvG3ug8eRHlh@b;^jRA}gCho3!JSY+gIIFlppn4nq{a{F$nO zxL>TOmU)T!)HbFAqt7~}%q&-arP!_m<`%!@Yt^P-cqPpS7aIAXER4qTj@Q)rmG1Rs!(+Mr~}~YQr#EE`JPUVyqVW z^U+Of<9Du8OB+|FR_L%q-a0v>wNL1<6~RN9pceCEDpWMd#tNghw^`cDX;p?uL34D7 z-{*%JD@ZY^05v+U4g1={EJy`T788&<5+tXzt^PY?7&_LM?7F+5+xQ1<|P<*RgFDRwgy6 zI#@Fd^tQ6NPYY`@7q|1`QL=A>t7M+b2>Ue)wj#tr+YV3!)HYIC)^Tt-kr77pBc?5-bQv@b8Dd-CHhD~f=mDf9bu8^`zAtiv>mD)% zr##FGG9z6%j*W0VWoqte?DRLdU&d4$%W$n}mcGS+A~)zUFt0 z_oUZJgaC!*cPTk;OpJydjyj5_Q_W)2pD|O;BGO4->+5`3JQ0%h(zPv9?<_#*s&2IV z4yvAi%gsR0r_m^@h1n_%Fv&o(w+p!o;~QBnk>Rq&a;3xT5}4l z7deOhN#M=TqmUW1dKNZ|QbkiVI2oOw@mryTceHs-&joK#0D|T%0aY|ay!s~buql0hvs z1{i=4a>pI%Vb2~kJQfb(-zkn!OL<8KTrwt=>#ts9!&b4l(Z{@cXuin^OY_}bF5B_A zM?mu)-F`Z%Xb(2=H?DP>t}U!$pH$k{)n-c8Z1FC>ZD?{rg#H(huTdt!j(?4gI_d6L zJAHLH06rH&MOG-cUP(y2R7YZ-6LqC&H_02!Q0g9IE2@hiaSYOozW2U~r(IjLAV|wJg0{;_ZYju#{Hz;9LBB%nR)7bdl!JjDt^uBjk=2>D!wv%_Iv6Nj5_l z*(ae%CnK@N3Iw$Of||CJBTVu8yq&<-`?vF*9OWz!7q>L)utWY|zH=kij%1I2{-_rm zY|gYeh^Q0ZhG4Oq*08x^ubDKcrqp{1ZpscL+3Z!QVb&Fx_^KIpCs&db5)5mZ;X_V8 zRqCUUYQCf&`rTx~Aund|DOodYSzA#}Dx!VV-vOrRxx1QQuPkEBXPiF&!~p`?&h6O{ zY6;S=SjpVHaSP*QqxMNqds@r_mcX^v3@UHqz7S6FFzFoRZu*g$e~_oSc~+++1@BOF z@5T1FOVFu2eELJEV{7w2@YqN=hjQ!Fueuzzt*D+q0(w>_T-r}Lv?EHQ{=?aOsN74o zA5Tw6IZEhnljE3IxV6w?h&32>bWTT{6X+{&%xkU}e(|pmv2)nWr&m&$sItp{z&2!m z8K_r3!sV0vGZMJB*7Md^I64Sq96=AZCmN`tUyO@V`*^9RJ%0-9DL`bI%`t%U)2|SG z8!96{d4lCqFXPk%Pkpc5gHbwvVj00lrsWKC{MW8o_xy1f^H`wHl4B&nyiA;?cMSbs zZCUpUwi%gY&gve2*znFCe!B9~=A^wDG@AA5KU_Xl_PY`xp!o3<(N;W5idht%1Mc|U z8Vmpx>3`MS>&*OFt?wY#IOtL`@2V9W{VrBV+AL{cpGH~dfxnV z+Wk9wV%k@LMHXBdkd6n02h1@Qk{JS9`h+j9nN`tGC$-0bh_KpQRPgw(E}PQjlv};N za`x56FT>T47@La%&(T>PZLTj$+*gN&8(oM+1$XpS-p8u8H{(ed(|O>`((2fN;iuNt z0utIe|0>h9pw95wjs#BoyDpOc-gztY%axs$%q}=)vL*3V$F@bv9 z`?~6!_6r-6KG=V~nkgKc({l2-AzO5+Pn3ceB3YGKv=5AZ$x_vh)|e^xk1&$a)oNszsZz+)C%&GqNPh>Hcd z%l?0lJl8~@?cDjxx!M#R(Ri@9r+MN_d=k=pZ|^hN^*@tmC$F-VH;>W#LP2{?9C>6q zb0;fJe$9H5zzHXMoM_*HK-dzW{2f2zx^53YzH|EFY?2$EWjtDFd3!H-KIqrlXor8; zzsD8`+l(6Pft?+-tA;RFtL`HizYZqUPQrCZr%5tdPM}rL6suD zpW1vH97l&-evB6%LDxE+1EzRzEu>b>Q#0e(p`-c)YDFlN)b!8)tAzYjd`YXuKiglh z^o9SvU?Mm$#fJL7J)0N~CPyL~gi_87H~#Bf$o9YtuRng88M8EZgih{PhC}yT@0>WK zga@v82d>yYK@eXH!IICN^&8*x5afsmf4&UUW?#3JFF4pvI^2v%3Cdw`1y%!;Jv^@~ z*^`vwreH(eansph(~QmgoHa7ydV{0}ofe<%4fpy+bskJo&>PZKqB-OZspMrkR65EQ z=xK#k(mtU>en0x%o~uITH-3nKQu{vRK4tp%Wevv3=vVzd-ydTV6UGps0d3k&pbCQP z&^zTZ-R>9Wuzaol_!TNRGxdwOzoP%XlIc*@*kdg>R2a7OnUjVs!`;xvT`8W6l0Iz3 z55I2&hyEIoU6q38PkTGxDUJKr=`N@pOX($U0(ZF~EM2W~O!@Yn)?EZKa$~=oiP#1R t%k8r};;?`u(l9P*@GqH%in;>X4{|0_Dn=QVzBS?_Dhe9ta+FEP{{ez;#8dzP literal 117460 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 From e3773cad837aaddd31315e47ad067907f81e0569 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 25 Aug 2024 02:50:47 -0400 Subject: [PATCH 083/355] workaround for Qt 5.12 which is lacking some color constants --- tools/lammps-gui/highlighter.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tools/lammps-gui/highlighter.cpp b/tools/lammps-gui/highlighter.cpp index 9846011836..ec196438d9 100644 --- a/tools/lammps-gui/highlighter.cpp +++ b/tools/lammps-gui/highlighter.cpp @@ -13,6 +13,27 @@ #include "highlighter.h" #include "helpers.h" +#include + +// workaround for Qt-5.12 +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) +namespace QColorConstants { +const QColor Red = QColor::fromRgb(0xff, 0x00, 0x00); +const QColor Green = QColor::fromRgb(0x00, 0xff, 0x00); +const QColor Blue = QColor::fromRgb(0x00, 0x00, 0xff); +const QColor Cyan = QColor::fromRgb(0x00, 0xff, 0xff); +const QColor Magenta = QColor::fromRgb(0xff, 0x00, 0xff); +const QColor Yellow = QColor::fromRgb(0xff, 0xff, 0x00); +namespace Svg { +const QColor dodgerblue = QColor::fromRgb(0x1e, 0x90, 0xff); +const QColor indianred = QColor::fromRgb(0xcd, 0x5c, 0x5c); +const QColor lightcoral = QColor::fromRgb(0xf0, 0x80, 0x80); +const QColor lightgray = QColor::fromRgb(0xd3, 0xd3, 0xd3); +const QColor lightgreen = QColor::fromRgb(0x90, 0xee, 0x90); +const QColor lightskyblue = QColor::fromRgb(0x87, 0xce, 0xfa); +} // namespace Svg +} // namespace QColorConstants +#endif Highlighter::Highlighter(QTextDocument *parent) : QSyntaxHighlighter(parent), From 86975151b2e44c49cf56b3a9db9562c6dfae8cc7 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 25 Aug 2024 07:27:37 -0400 Subject: [PATCH 084/355] step LAMMPS-GUI version --- tools/lammps-gui/CMakeLists.txt | 2 +- tools/lammps-gui/lammps-gui.appdata.xml | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/lammps-gui/CMakeLists.txt b/tools/lammps-gui/CMakeLists.txt index 1732169bba..1dfd8f451d 100644 --- a/tools/lammps-gui/CMakeLists.txt +++ b/tools/lammps-gui/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -project(lammps-gui VERSION 1.6.9 LANGUAGES CXX) +project(lammps-gui VERSION 1.6.10 LANGUAGES CXX) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) diff --git a/tools/lammps-gui/lammps-gui.appdata.xml b/tools/lammps-gui/lammps-gui.appdata.xml index 2139cc6134..312abc66cf 100644 --- a/tools/lammps-gui/lammps-gui.appdata.xml +++ b/tools/lammps-gui/lammps-gui.appdata.xml @@ -54,6 +54,11 @@ + + + + + Added search and replace functionality From 586e5db7578a52151d7705593e954a020a05b94f Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 25 Aug 2024 15:49:42 -0400 Subject: [PATCH 085/355] completely hide inactive atom size field instead of only disabling them --- tools/lammps-gui/imageviewer.cpp | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/tools/lammps-gui/imageviewer.cpp b/tools/lammps-gui/imageviewer.cpp index b1c4207962..0b3c58abd3 100644 --- a/tools/lammps-gui/imageviewer.cpp +++ b/tools/lammps-gui/imageviewer.cpp @@ -170,7 +170,9 @@ ImageViewer::ImageViewer(const QString &fileName, LammpsWrapper *_lammps, QWidge auto *valid = new QDoubleValidator(1.0e-10, 1.0e10, 10, asize); asize->setValidator(valid); asize->setObjectName("atomSize"); + asize->setToolTip("Set Atom size"); asize->setEnabled(false); + asize->hide(); settings.beginGroup("snapshot"); auto *xval = new QSpinBox; xval->setRange(100, 10000); @@ -260,6 +262,9 @@ ImageViewer::ImageViewer(const QString &fileName, LammpsWrapper *_lammps, QWidge menuLayout->addWidget(renderstatus); menuLayout->addWidget(new QLabel(" Atom Size: ")); menuLayout->addWidget(asize); + // hide item initially + menuLayout->itemAt(2)->widget()->setObjectName("AtomLabel"); + menuLayout->itemAt(2)->widget()->hide(); menuLayout->addWidget(new QLabel(" Width: ")); menuLayout->addWidget(xval); menuLayout->addWidget(new QLabel(" Height: ")); @@ -579,15 +584,31 @@ void ImageViewer::createImage() auto *button = findChild("vdw"); if (button) button->setEnabled(true); auto *edit = findChild("atomSize"); - if (edit) edit->setEnabled(false); + if (edit) { + edit->setEnabled(false); + edit->hide(); + } + auto *label = findChild("AtomLabel"); + if (label) { + label->setEnabled(false); + label->hide(); + } + } else { adiams.clear(); auto *button = findChild("vdw"); if (button) button->setEnabled(false); + + auto *label = findChild("AtomLabel"); + if (label) { + label->setEnabled(true); + label->show(); + } auto *edit = findChild("atomSize"); if (edit) { if (!edit->isEnabled()) { edit->setEnabled(true); + edit->show(); // initialize with lattice spacing auto *xlattice = (const double *)lammps->extract_global("xlattice"); if (xlattice) atomSize = *xlattice; From cfcbad9e388068d6935846d40fa2bd3add0b49ae Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 25 Aug 2024 16:07:22 -0400 Subject: [PATCH 086/355] back to 5 sigma --- tools/lammps-gui/chartviewer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/lammps-gui/chartviewer.cpp b/tools/lammps-gui/chartviewer.cpp index 4ea5449f3f..eb5444de8e 100644 --- a/tools/lammps-gui/chartviewer.cpp +++ b/tools/lammps-gui/chartviewer.cpp @@ -442,7 +442,7 @@ void ChartViewer::add_data(int step, double data) const double num = count - first; const double avg = ysum / num; const double avgsq = ysumsq / num; - if (fabs(data - avg) > (4.0 * sqrt(avgsq - (avg * avg)))) return; + if (fabs(data - avg) > (5.0 * sqrt(avgsq - (avg * avg)))) return; } series->append(step, data); From d59d31b44508938963a58f9c78f319df95e990a2 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 25 Aug 2024 16:14:36 -0400 Subject: [PATCH 087/355] remove accidental commit --- check-downloads.py | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100755 check-downloads.py diff --git a/check-downloads.py b/check-downloads.py deleted file mode 100755 index 57b4af6a19..0000000000 --- a/check-downloads.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python3 - -import subprocess -import json - -try: - output = subprocess.run('gh api https://api.github.com/repos/lammps/lammps/releases', - shell=True, capture_output=True) -except subprocess.CalledProcessError as e: - print("API call failed with:", e.output) - -releases = json.loads(output.stdout) -print("Recent releases: ", len(releases)) -for rel in releases: - if len(rel['assets']) > 0: - print("Release: ", rel['name']) - for asset in rel['assets']: - print("Asset: %s Downloads: %d" % (asset['name'], asset['download_count'])) From 9e5e1af125728bf761a64f11d788a2c10fcb0552 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 25 Aug 2024 16:25:42 -0400 Subject: [PATCH 088/355] update TODO list for LAMMPS-GUI --- tools/lammps-gui/TODO.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tools/lammps-gui/TODO.md b/tools/lammps-gui/TODO.md index 2c190dc907..b36f0a4ce8 100644 --- a/tools/lammps-gui/TODO.md +++ b/tools/lammps-gui/TODO.md @@ -2,13 +2,21 @@ LAMMPS-GUI TODO list: # Short term goals (v1.x) -- figure out how widgets can be resized to fraction of available screen size. -- figure out stacking order of frames and whether it can be more flexible - implement a timed "Auto-Save" feature that saves after some idle time. set timeout in Editor preferences. +- add a "Filter data" checkbox to the "Charts" window to select whether data should be dropped. +- add a "Charts tab" to the preferences with the following (default) settings: + - default filter data yes/no + - default smooth parameters + - default plot colors + - enable "raw" or "smooth" or "both" +- add QLineEdit field to enter plot title - add a "Colors" menu to the image viewer to adjust color settings for the current image (unlike the defaults in the perferences) including assigning colors to individual atom types. - Support color by property (e.g. scan computes or fixes with per-atom data), define colormaps etc. +- Add a "Diameters" dialog where diamaters can by specified by atom type +- figure out how widgets can be resized to fraction of available screen size. +- figure out stacking order of frames and whether it can be more flexible - implement indenting regions for (nested) loops? - implement data file manager GUI with the following features: From b8b103df283c5cfa9a240e3ca0dd3a6ad828f1e4 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 25 Aug 2024 16:37:25 -0400 Subject: [PATCH 089/355] update version strings --- doc/lammps.1 | 4 ++-- src/version.h | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/lammps.1 b/doc/lammps.1 index 00d8a4f61f..75581bd008 100644 --- a/doc/lammps.1 +++ b/doc/lammps.1 @@ -1,7 +1,7 @@ -.TH LAMMPS "1" "27 June 2024" "2024-06-27" +.TH LAMMPS "1" "29 August 2024" "2024-08-29" .SH NAME .B LAMMPS -\- Molecular Dynamics Simulator. Version 27 June 2024 +\- Molecular Dynamics Simulator. Version 29 August 2024 .SH SYNOPSIS .B lmp diff --git a/src/version.h b/src/version.h index af7e87b61f..7ef4ade45e 100644 --- a/src/version.h +++ b/src/version.h @@ -1,2 +1 @@ -#define LAMMPS_VERSION "27 Jun 2024" -#define LAMMPS_UPDATE "Development" +#define LAMMPS_VERSION "29 Aug 2024" From c68981c7fc141164f180da6335dc5c2eca93ff27 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 25 Aug 2024 16:37:49 -0400 Subject: [PATCH 090/355] build LAMMPS-GUI flatpack from release branch of the official repo --- tools/lammps-gui/org.lammps.lammps-gui.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/lammps-gui/org.lammps.lammps-gui.yml b/tools/lammps-gui/org.lammps.lammps-gui.yml index 26f35531ce..548c4ce93e 100644 --- a/tools/lammps-gui/org.lammps.lammps-gui.yml +++ b/tools/lammps-gui/org.lammps.lammps-gui.yml @@ -108,5 +108,5 @@ modules: - -D BUILD_TOOLS=yes sources: - type: git - url: https://github.com/akohlmey/lammps.git - branch: collected-small-fixes + url: https://github.com/lammps/lammps.git + branch: release From fa373eb6852f96366de109bac7679d7fe9cea547 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 25 Aug 2024 16:39:13 -0400 Subject: [PATCH 091/355] update pending version tags with planned release date --- doc/src/Build_settings.rst | 2 +- doc/src/Commands_removed.rst | 2 +- doc/src/Packages_details.rst | 2 +- doc/src/Run_options.rst | 2 +- doc/src/bond_rheo_shell.rst | 2 +- doc/src/compute_rheo_property_atom.rst | 2 +- doc/src/fix_meso_move.rst | 2 +- doc/src/fix_mvv_dpd.rst | 2 +- doc/src/fix_rheo.rst | 2 +- doc/src/fix_rheo_oxidation.rst | 2 +- doc/src/fix_rheo_pressure.rst | 2 +- doc/src/fix_rheo_thermal.rst | 2 +- doc/src/fix_rheo_viscosity.rst | 2 +- doc/src/fix_rigid_meso.rst | 2 +- doc/src/fix_shake.rst | 2 +- doc/src/fix_smd_integrate_tlsph.rst | 2 +- doc/src/fix_smd_integrate_ulsph.rst | 2 +- doc/src/geturl.rst | 2 +- doc/src/neigh_modify.rst | 2 +- doc/src/pair_rheo.rst | 2 +- doc/src/pair_rheo_solid.rst | 2 +- doc/src/variable.rst | 2 +- python/lammps/core.py | 4 ++-- src/library.cpp | 6 +++--- 24 files changed, 27 insertions(+), 27 deletions(-) diff --git a/doc/src/Build_settings.rst b/doc/src/Build_settings.rst index dc79cc3ed9..9d31f6b431 100644 --- a/doc/src/Build_settings.rst +++ b/doc/src/Build_settings.rst @@ -492,7 +492,7 @@ during a run. Support for downloading files ----------------------------- -.. versionadded:: TBD +.. versionadded:: 29Aug2024 The :doc:`geturl command ` command uses the `the libcurl library `_ to download files. This requires that diff --git a/doc/src/Commands_removed.rst b/doc/src/Commands_removed.rst index f902a61515..ea8b3d4b03 100644 --- a/doc/src/Commands_removed.rst +++ b/doc/src/Commands_removed.rst @@ -171,7 +171,7 @@ instructions to install i-PI from PyPI via pip are provided. LAMMPS shell ------------ -.. versionchanged:: TBD +.. versionchanged:: 29Aug2024 The LAMMPS shell has been removed from the LAMMPS distribution. Users are encouraged to use the :ref:`LAMMPS-GUI ` tool instead. diff --git a/doc/src/Packages_details.rst b/doc/src/Packages_details.rst index 4483601a88..e87610fb31 100644 --- a/doc/src/Packages_details.rst +++ b/doc/src/Packages_details.rst @@ -2642,7 +2642,7 @@ This package has :ref:`specific installation instructions ` on the :doc:`B **Authors:** Joel T. Clemmer (Sandia National Labs), Thomas C. O'Connor (Carnegie Mellon University) -.. versionadded:: TBD +.. versionadded:: 29Aug2024 **Supporting info:** diff --git a/doc/src/Run_options.rst b/doc/src/Run_options.rst index 86e8e47626..4f7021cd53 100644 --- a/doc/src/Run_options.rst +++ b/doc/src/Run_options.rst @@ -508,7 +508,7 @@ e.g. the *nfile* and *fileper* keywords. See the **-restart2info restartfile keyword ...** -.. versionadded:: TBD +.. versionadded:: 29Aug2024 Write out some info about the restart file and and immediately exit. This is the same operation as if the following 2-line input script were diff --git a/doc/src/bond_rheo_shell.rst b/doc/src/bond_rheo_shell.rst index 992917c104..090f5ab7aa 100644 --- a/doc/src/bond_rheo_shell.rst +++ b/doc/src/bond_rheo_shell.rst @@ -38,7 +38,7 @@ Examples Description """"""""""" -.. versionadded:: TBD +.. versionadded:: 29Aug2024 The *rheo/shell* bond style is designed to work with :doc:`fix rheo/oxidation ` which creates candidate diff --git a/doc/src/compute_rheo_property_atom.rst b/doc/src/compute_rheo_property_atom.rst index f34b2225f5..2e905b97be 100644 --- a/doc/src/compute_rheo_property_atom.rst +++ b/doc/src/compute_rheo_property_atom.rst @@ -55,7 +55,7 @@ Examples Description """"""""""" -.. versionadded:: TBD +.. versionadded:: 29Aug2024 Define a computation that stores atom attributes specific to the RHEO package for each atom in the group. This is useful so that the values diff --git a/doc/src/fix_meso_move.rst b/doc/src/fix_meso_move.rst index 64b451b7f1..d5e1bba446 100644 --- a/doc/src/fix_meso_move.rst +++ b/doc/src/fix_meso_move.rst @@ -247,7 +247,7 @@ defined by the :doc:`atom_style sph ` command. All particles in the group must be mesoscopic SPH/SDPD particles. -.. versionchanged:: TBD +.. versionchanged:: 29Aug2024 This fix is incompatible with deformation controls that remap velocity, for instance the *remap v* option of :doc:`fix deform `. diff --git a/doc/src/fix_mvv_dpd.rst b/doc/src/fix_mvv_dpd.rst index e64a162bf4..44883e92ad 100644 --- a/doc/src/fix_mvv_dpd.rst +++ b/doc/src/fix_mvv_dpd.rst @@ -97,7 +97,7 @@ These fixes are part of the DPD-MESO package. They are only enabled if LAMMPS was built with that package. See the :doc:`Build package ` page for more info. -.. versionchanged:: TBD +.. versionchanged:: 29Aug2024 This fix is incompatible with deformation controls that remap velocity, for instance the *remap v* option of :doc:`fix deform `. diff --git a/doc/src/fix_rheo.rst b/doc/src/fix_rheo.rst index 3c7f8449c7..2977662238 100644 --- a/doc/src/fix_rheo.rst +++ b/doc/src/fix_rheo.rst @@ -44,7 +44,7 @@ Examples Description """"""""""" -.. versionadded:: TBD +.. versionadded:: 29Aug2024 Perform time integration for RHEO particles, updating positions, velocities, and densities. For an overview of other features available in the RHEO package, diff --git a/doc/src/fix_rheo_oxidation.rst b/doc/src/fix_rheo_oxidation.rst index ba3ead3f1f..dc22ed304a 100644 --- a/doc/src/fix_rheo_oxidation.rst +++ b/doc/src/fix_rheo_oxidation.rst @@ -27,7 +27,7 @@ Examples Description """"""""""" -.. versionadded:: TBD +.. versionadded:: 29Aug2024 This fix dynamically creates bonds on the surface of fluids to represent physical processes such as oxidation. It is intended diff --git a/doc/src/fix_rheo_pressure.rst b/doc/src/fix_rheo_pressure.rst index 40d623ae07..2a714b298b 100644 --- a/doc/src/fix_rheo_pressure.rst +++ b/doc/src/fix_rheo_pressure.rst @@ -33,7 +33,7 @@ Examples Description """"""""""" -.. versionadded:: TBD +.. versionadded:: 29Aug2024 This fix defines a pressure equation of state for RHEO particles. One can define different equations of state for different atom types. An equation diff --git a/doc/src/fix_rheo_thermal.rst b/doc/src/fix_rheo_thermal.rst index cf245cbdca..214bc1db86 100644 --- a/doc/src/fix_rheo_thermal.rst +++ b/doc/src/fix_rheo_thermal.rst @@ -48,7 +48,7 @@ Examples Description """"""""""" -.. versionadded:: TBD +.. versionadded:: 29Aug2024 This fix performs time integration of temperature for atom style rheo/thermal. In addition, it defines multiple thermal properties of particles and handles diff --git a/doc/src/fix_rheo_viscosity.rst b/doc/src/fix_rheo_viscosity.rst index 5bc1b2a210..804059e6f8 100644 --- a/doc/src/fix_rheo_viscosity.rst +++ b/doc/src/fix_rheo_viscosity.rst @@ -38,7 +38,7 @@ Examples Description """"""""""" -.. versionadded:: TBD +.. versionadded:: 29Aug2024 This fix defines a viscosity for RHEO particles. One can define different viscosities for different atom types, but a viscosity must be specified for diff --git a/doc/src/fix_rigid_meso.rst b/doc/src/fix_rigid_meso.rst index 3f734e3fef..6a9a85a865 100644 --- a/doc/src/fix_rigid_meso.rst +++ b/doc/src/fix_rigid_meso.rst @@ -353,7 +353,7 @@ defined by the :doc:`atom_style sph ` command. All particles in the group must be mesoscopic SPH/SDPD particles. -.. versionchanged:: TBD +.. versionchanged:: 29Aug2024 This fix is incompatible with deformation controls that remap velocity, for instance the *remap v* option of :doc:`fix deform `. diff --git a/doc/src/fix_shake.rst b/doc/src/fix_shake.rst index 339d0fd68c..434415eecf 100644 --- a/doc/src/fix_shake.rst +++ b/doc/src/fix_shake.rst @@ -137,7 +137,7 @@ constrained (within a fudge factor of MASSDELTA specified in both bonds in the angle are constrained then the angle will also be constrained if its type is in the list. -.. versionchanged:: TBD +.. versionchanged:: 29Aug2024 The types may be given as type labels *only* if there is no atom, bond, or angle type label named *b*, *a*, *t*, or *m* defined in the diff --git a/doc/src/fix_smd_integrate_tlsph.rst b/doc/src/fix_smd_integrate_tlsph.rst index 44d4bab3a5..fce4c057c3 100644 --- a/doc/src/fix_smd_integrate_tlsph.rst +++ b/doc/src/fix_smd_integrate_tlsph.rst @@ -53,7 +53,7 @@ Restrictions This fix is part of the MACHDYN package. It is only enabled if LAMMPS was built with that package. See the :doc:`Build package ` page for more info. -.. versionchanged:: TBD +.. versionchanged:: 29Aug2024 This fix is incompatible with deformation controls that remap velocity, for instance the *remap v* option of :doc:`fix deform `. diff --git a/doc/src/fix_smd_integrate_ulsph.rst b/doc/src/fix_smd_integrate_ulsph.rst index 6b1e070763..60dfb06a0e 100644 --- a/doc/src/fix_smd_integrate_ulsph.rst +++ b/doc/src/fix_smd_integrate_ulsph.rst @@ -61,7 +61,7 @@ Restrictions This fix is part of the MACHDYN package. It is only enabled if LAMMPS was built with that package. See the :doc:`Build package ` page for more info. -.. versionchanged:: TBD +.. versionchanged:: 29Aug2024 This fix is incompatible with deformation controls that remap velocity, for instance the *remap v* option of :doc:`fix deform `. diff --git a/doc/src/geturl.rst b/doc/src/geturl.rst index 0ca0ce0cd3..acf1e21a3e 100644 --- a/doc/src/geturl.rst +++ b/doc/src/geturl.rst @@ -32,7 +32,7 @@ Examples Description """"""""""" -.. versionadded:: TBD +.. versionadded:: 29Aug2024 Download a file from an URL to the local disk. This is implemented with the `libcurl library `_ which supports a diff --git a/doc/src/neigh_modify.rst b/doc/src/neigh_modify.rst index 753990c93f..bf87c8452c 100644 --- a/doc/src/neigh_modify.rst +++ b/doc/src/neigh_modify.rst @@ -159,7 +159,7 @@ sample scenarios where this is useful: * When one or more rigid bodies are specified, interactions within each body can be turned off to save needless computation. See the :doc:`fix rigid ` command for more details. -.. versionchanged:: TBD +.. versionchanged:: 29Aug2024 Support for type labels was added. diff --git a/doc/src/pair_rheo.rst b/doc/src/pair_rheo.rst index 993ec3cee3..927d2f0266 100644 --- a/doc/src/pair_rheo.rst +++ b/doc/src/pair_rheo.rst @@ -31,7 +31,7 @@ Examples Description """"""""""" -.. versionadded:: TBD +.. versionadded:: 29Aug2024 Pair style *rheo* computes pressure and viscous forces between particles in the :doc:`rheo package `. If thermal evolution is turned diff --git a/doc/src/pair_rheo_solid.rst b/doc/src/pair_rheo_solid.rst index 0b1ed47fb8..f71cba7bdb 100644 --- a/doc/src/pair_rheo_solid.rst +++ b/doc/src/pair_rheo_solid.rst @@ -21,7 +21,7 @@ Examples Description """"""""""" -.. versionadded:: TBD +.. versionadded:: 29Aug2024 Style *rheo/solid* is effectively a copy of pair style :doc:`bpm/spring ` except it only applies forces diff --git a/doc/src/variable.rst b/doc/src/variable.rst index 330e44139e..1867532efa 100644 --- a/doc/src/variable.rst +++ b/doc/src/variable.rst @@ -1042,7 +1042,7 @@ label2type(), but returns 1 if the type label has been assigned, otherwise it returns 0. This function can be used to check if a particular type label already exists in the simulation. -.. versionadded:: TBD +.. versionadded:: 29Aug2024 The is_timeout() function returns 1 when the :doc:`timer timeout ` has expired otherwise it returns 0. This function can be used diff --git a/python/lammps/core.py b/python/lammps/core.py index 4fcda37d5d..9e6329fe3c 100644 --- a/python/lammps/core.py +++ b/python/lammps/core.py @@ -941,7 +941,7 @@ class lammps(object): def extract_pair_dimension(self, name): """Retrieve pair style property dimensionality from LAMMPS - .. versionadded:: TBD + .. versionadded:: 29Aug2024 This is a wrapper around the :cpp:func:`lammps_extract_pair_dimension` function of the C-library interface. The list of supported keywords @@ -970,7 +970,7 @@ class lammps(object): def extract_pair(self, name): """Extract pair style data from LAMMPS. - .. versionadded:: TBD + .. versionadded:: 29Aug2024 This is a wrapper around the :cpp:func:`lammps_extract_pair` function of the C-library interface. Since there are no pointers in Python, this diff --git a/src/library.cpp b/src/library.cpp index 097cffd68a..324bfc5b9f 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -1972,7 +1972,7 @@ void *lammps_extract_global(void *handle, const char *name) * \verbatim embed:rst -.. versionadded:: TBD +.. versionadded:: 29Aug2024 This function returns an integer that specified the dimensionality of the data that can be extracted from the current pair style with ``Pair::extract()``. @@ -2005,7 +2005,7 @@ int lammps_extract_pair_dimension(void * handle, const char *name) * \verbatim embed:rst -.. versionadded:: TBD +.. versionadded:: 29Aug2024 This function returns a pointer to data available from the current pair style with ``Pair::extract()``. The dimensionality of the returned @@ -5859,7 +5859,7 @@ int lammps_config_has_ffmpeg_support() { \verbatim embed:rst -.. versionadded::TBD +.. versionadded::29Aug2024 The LAMMPS :doc:`geturl command ` supports downloading files through using `the libcurl library `_. From 04400e10a8f3c27a57089598806c8c356415d586 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Sun, 25 Aug 2024 16:02:16 -0500 Subject: [PATCH 092/355] Updated the regression tester run_tests.py to handle list of input scripts --- .github/workflows/full-regression.yml | 6 +- .github/workflows/quick-regression.yml | 2 +- tools/regression-tests/get-quick-list.py | 24 +--- tools/regression-tests/run_tests.py | 153 ++++++++++++++++------- 4 files changed, 119 insertions(+), 66 deletions(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index d550f5b728..5e11fbbdd4 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -67,7 +67,7 @@ jobs: cmake --build build ccache -s - - name: Analyze top-level examples folder, split into 8 seperate subfolder lists + - name: Analyze top-level examples folder, split into 8 seperate lists of input scripts shell: bash run: | source linuxenv/bin/activate @@ -86,10 +86,10 @@ jobs: strategy: max-parallel: 2 matrix: - idx: [ 0, 1, 2, 3, 4, 5, 6, 7 ] + idx: [ 0, 1 ] steps: - - name: Run regression tests with 8 workers each processing a list of subfolders + - name: Run regression tests run: | source linuxenv/bin/activate python3 tools/regression-tests/run_tests.py \ diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index 2726e70650..89da0bfb0a 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -78,7 +78,7 @@ jobs: python3 tools/regression-tests/run_tests.py \ --lmp-bin=build/lmp \ --config-file=tools/regression-tests/config_serial.yaml \ - --list-input=folder_list.txt + --list-input=input_list.txt tar -cvf quick-regression-test.tar run.log progress.yaml - name: Upload artifacts diff --git a/tools/regression-tests/get-quick-list.py b/tools/regression-tests/get-quick-list.py index c9ec9e3971..672aa10f3a 100644 --- a/tools/regression-tests/get-quick-list.py +++ b/tools/regression-tests/get-quick-list.py @@ -259,28 +259,12 @@ if __name__ == "__main__": if regex: inputs = get_examples_using_styles(regex, os.path.join(LAMMPS_DIR,'examples')) - # TODO: modify the regression tester tool to process the raw list of input scripts - folder_list = [] print("Suggested inputs for testing:") - for inp in inputs: - print(inp) - - # get the folder that contains the input script - full_path = str(inp) - folder = full_path.rsplit('/', 1)[0] - # add unique folders in the list - if folder not in folder_list: - folder_list.append(folder) - # input_list.txt is used for the regression tester tool - # that lists the individual subfolders and the number of input scripts therein - with open('folder_list.txt', 'w') as f: - for folder in folder_list: - cmd_str = f"ls {folder}/in.* | wc -l" - p = subprocess.run(cmd_str, shell=True, text=True, capture_output=True) - num_input = p.stdout.split('\n')[0] - f.write(folder + ' ' + num_input + '\n') - + with open('input_list.txt', 'w') as f: + for inp in inputs: + print(inp) + f.write(inp + '\n') print("Found changes to the following styles:") print("Commands: ", styles['command']) diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index 4d9fdfaa22..3051355eb8 100644 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -9,6 +9,7 @@ UPDATE: August 13, 2024: With the current features, users can: + specify which LAMMPS binary version to test (e.g., the version from a commit, or those from `lammps-testing`) + specify the examples subfolders (thus the reference log files) seperately (e.g. from other LAMMPS versions or commits) + + specify the list of examples input scripts to test + specify tolerances for individual quantities for any input script to override the global values + launch tests with `mpirun` with all supported command line features (multiple procs, multiple paritions, and suffices) + skip certain input files (whose names match specified patterns) if not interested, or packaged not installed, or no reference log file exists @@ -43,21 +44,33 @@ Example usage: python3 run_tests.py --lmp-bin=/path/to/lmp_binary --config-file=/path/to/config/file/config.yaml \ --example-folders="/path/to/examples/folder1;/path/to/examples/folder2" - The example folders can also be loaded from a text file list_subfolders1.txt: + The example subfolders can also be loaded from a text file list_subfolders1.txt: python3 run_tests.py --lmp-bin=/path/to/lmp_binary --config-file=/path/to/config/file/config.yaml \ - --list-input=list_subfolders1.txt --output-file=output1.txt --progress-file=progress1.yaml \ + --list-subfolders=list_subfolders1.txt --output-file=output1.txt --progress-file=progress1.yaml \ + --log-file=run1.log + + 4) Specify a list of example input scripts + python3 run_tests.py --lmp-bin=/path/to/lmp_binary --config-file=/path/to/config/file/config.yaml \ + --list-input=input-list-1.txt --output-file=output1.txt --progress-file=progress1.yaml \ + --log-file=run1.log + + The example subfolders can also be loaded from a text file list_subfolders1.txt: + python3 run_tests.py --lmp-bin=/path/to/lmp_binary --config-file=/path/to/config/file/config.yaml \ + --list-subfolders=list_subfolders1.txt --output-file=output1.txt --progress-file=progress1.yaml \ --log-file=run1.log - 4) Test a LAMMPS binary with the whole top-level /examples folder in a LAMMPS source tree + 5) Test a LAMMPS binary with the whole top-level /examples folder in a LAMMPS source tree python3 run_tests.py --lmp-bin=/path/to/lmp_binary --examples-top-level=/path/to/lammps/examples - 5) Analyze the LAMMPS binary annd whole top-level /examples folder in a LAMMPS source tree + 6) Analyze the LAMMPS binary annd whole top-level /examples folder in a LAMMPS source tree and generate separate input lists for 8 workers: python3 run_tests.py --lmp-bin=/path/to/lmp_binary --examples-top-level=/path/to/lammps/examples \ --analyze --num-workers=8 - This is used for splitting the subfolders into separate input lists and launching different instances - of run_tests.py simultaneously. + The output of this run is 8 files folder-list-[0-7].txt that lists the subfolders + and 8 files input-list-[0-7].txt that lists the input scripts under the top-level example folders. + With these lists, one can launch multiple instances of run_tests.py simultaneously + each with a list of example subfolders (Case 3), or with a list of input scripts (Case 4). ''' from argparse import ArgumentParser @@ -825,6 +838,7 @@ if __name__ == "__main__": lmp_binary = "" configFileName = "config.yaml" example_subfolders = [] + example_inputs = [] example_toplevel = "" genref = False verbose = False @@ -832,6 +846,7 @@ if __name__ == "__main__": progress_file = "progress.yaml" log_file = "run.log" list_input = "" + list_subfolders = "" analyze = False # distribute the total number of input scripts over the workers @@ -844,7 +859,8 @@ if __name__ == "__main__": help="Configuration YAML file") parser.add_argument("--examples-top-level", dest="example_toplevel", default="", help="Examples top-level") parser.add_argument("--example-folders", dest="example_folders", default="", help="Example subfolders") - parser.add_argument("--list-input", dest="list_input", default="", help="File that lists the subfolders") + parser.add_argument("--list-input", dest="list_input", default="", help="File that lists the input scripts") + parser.add_argument("--list-subfolders", dest="list_subfolders", default="", help="File that lists the subfolders") parser.add_argument("--num-workers", dest="num_workers", default=1, help="Number of workers") parser.add_argument("--gen-ref",dest="genref", action='store_true', default=False, help="Generating reference data") @@ -866,6 +882,7 @@ if __name__ == "__main__": if int(args.num_workers) > 0: num_workers = int(args.num_workers) list_input = args.list_input + list_subfolders = args.list_subfolders # example_toplevel is where all the examples subfolders reside if args.example_toplevel != "": @@ -884,33 +901,6 @@ if __name__ == "__main__": logger = logging.getLogger(__name__) logging.basicConfig(filename=log_file, level=logging.INFO, filemode="w") - # read in the configuration of the tests - with open(configFileName, 'r') as f: - config = yaml.load(f, Loader=Loader) - absolute_path = os.path.abspath(configFileName) - print(f"\nRegression tests with the settings defined in the configuration file:\n {absolute_path}") - f.close() - - # check if lmp_binary is specified in the config yaml - if lmp_binary == "": - if config['lmp_binary'] == "": - print("Needs a valid LAMMPS binary") - quit() - else: - lmp_binary = os.path.abspath(config['lmp_binary']) - - # print out the binary info - packages, operating_system, GitInfo, compile_flags = get_lammps_build_configuration(lmp_binary) - print("\nLAMMPS build info:") - print(f" - {operating_system}") - print(f" - {GitInfo}") - print(f" - Active compile flags: {compile_flags}") - print(f" - List of {len(packages)} installed packages:") - all_pkgs = "" - for p in packages: - all_pkgs += p + " " - print(all_pkgs) - if len(example_subfolders) > 0: print("\nExample folders to test:") print(*example_subfolders, sep='\n') @@ -926,7 +916,7 @@ if __name__ == "__main__": # then use the path from --example-top-folder, or from the input-list read from a text file if len(example_subfolders) == 0: - # need top level specified + # if the top level is specified if len(example_toplevel) != 0: # getting the list of all the input files because there are subfolders (e.g. PACKAGES) under the top level cmd_str = f"find {example_toplevel} -name \"in.*\" " @@ -953,7 +943,7 @@ if __name__ == "__main__": # write each chunk to a file idx = 0 for list_input in sublists: - filename = f"input-list-{idx}.txt" + filename = f"folder-list-{idx}.txt" with open(filename, "w") as f: for folder in list_input: # count the number of input scripts in each folder @@ -967,14 +957,28 @@ if __name__ == "__main__": # working on all the folders for now example_subfolders = folder_list - # if a list of subfolders are provided from a text file (list_input from the command-line argument) - elif len(list_input) != 0: + # divide the list of input scripts into num_workers chunks + sublists = divide_into_N(input_list, num_workers) + + # write each chunk to a file + idx = 0 + for list_input in sublists: + filename = f"input-list-{idx}.txt" + with open(filename, "w") as f: + for inp in list_input: + f.write(inp + '\n') + f.close() + idx = idx + 1 + + # if a list of subfolders is provided from a text file (list_subfolders from the command-line argument) + elif len(list_subfolders) != 0: num_inputscripts = 0 - with open(list_input, "r") as f: + with open(list_subfolders, "r") as f: all_subfolders = f.read().splitlines() f.close() for line in all_subfolders: if len(line) > 0: + # skip subfolders if line[0] == '#': continue folder = line.split()[0] @@ -983,6 +987,33 @@ if __name__ == "__main__": msg = f"\nThere are {len(example_subfolders)} folders with {num_inputscripts} input scripts in total listed in {list_input}." print(msg) logger.info(msg) + + # if a list of input scripts is provided from a text file (list_input from the command-line argument) + elif len(list_input) != 0: + num_inputscripts = 0 + folder_list = [] + with open(list_input, "r") as f: + all_inputs = f.read().splitlines() + f.close() + + for line in all_inputs: + if len(line) > 0: + # skip input scripts + if line[0] == '#': + continue + input = line.split()[0] + folder = input.rsplit('/', 1)[0] + # unique folders in the list + if folder not in folder_list: + folder_list.append(folder) + example_inputs.append(input) + num_inputscripts += 1 + + example_subfolders = folder_list + msg = f"\nThere are {num_inputscripts} input scripts listed in {list_input}." + print(msg) + logger.info(msg) + else: inplace_input = False @@ -990,6 +1021,33 @@ if __name__ == "__main__": if analyze == True: quit() + # read in the configuration of the tests + with open(configFileName, 'r') as f: + config = yaml.load(f, Loader=Loader) + absolute_path = os.path.abspath(configFileName) + print(f"\nRegression test configuration file:\n {absolute_path}") + f.close() + + # check if lmp_binary is specified in the config yaml + if lmp_binary == "": + if config['lmp_binary'] == "": + print("Needs a valid LAMMPS binary") + quit() + else: + lmp_binary = os.path.abspath(config['lmp_binary']) + + # print out the binary info + packages, operating_system, GitInfo, compile_flags = get_lammps_build_configuration(lmp_binary) + print("\nLAMMPS build info:") + print(f" - {operating_system}") + print(f" - {GitInfo}") + print(f" - Active compile flags: {compile_flags}") + print(f" - List of {len(packages)} installed packages:") + all_pkgs = "" + for p in packages: + all_pkgs += p + " " + print(all_pkgs) + all_results = [] # save current working dir @@ -1044,10 +1102,21 @@ if __name__ == "__main__": cmd_str = "ls in.*" p = subprocess.run(cmd_str, shell=True, text=True, capture_output=True) - input_list = p.stdout.split('\n') - input_list.remove('') + all_input_list = p.stdout.split('\n') + all_input_list.remove('') - print(f"{len(input_list)} input script(s): {input_list}") + # if the list of example input scripts is provided + # if an input script is not in the list, then remove it from input_list + input_list = [] + if len(example_inputs) > 0: + for inp in all_input_list: + full_path = directory + "/" + inp + if full_path in example_inputs: + input_list.append(inp) + else: + input_list = all_input_list + + print(f"{len(input_list)} input script(s) to be tested: {input_list}") total_tests += len(input_list) # iterate through the input scripts From 9f20e5b7f7ad49b3460e7f3cf8c6f535fd509742 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Sun, 25 Aug 2024 16:19:52 -0500 Subject: [PATCH 093/355] put a timeout for a run, specified in the config file --- tools/regression-tests/config_serial.yaml | 1 + tools/regression-tests/run_tests.py | 20 ++++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/tools/regression-tests/config_serial.yaml b/tools/regression-tests/config_serial.yaml index 1fe3f48353..746e74f226 100644 --- a/tools/regression-tests/config_serial.yaml +++ b/tools/regression-tests/config_serial.yaml @@ -36,6 +36,7 @@ in.bucky-plus-cnt*, ] + timeout: 60 nugget: 1.0 epsilon: 1e-16 diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index 3051355eb8..f2d41af9de 100644 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -319,7 +319,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file # if there is no ERROR in the output, but there is no Total wall time printed out if "Total wall time" not in output: - logger.info(f" ERROR: no Total wall time in the output.\n") + logger.info(f" ERROR: no Total wall time in the output.\n") logger.info(f"\n{input_test}:") logger.info(f"\n Output:\n{output}") logger.info(f"\n Error:\n{error}") @@ -720,7 +720,6 @@ def get_lammps_build_configuration(lmp_binary): launch LAMMPS using the configuration defined in the dictionary config with an input file TODO: - generate new reference values if needed - - wrap subprocess with try/catch to handle exceptions ''' def execute(lmp_binary, config, input_file_name, generate_ref_yaml=False): cmd_str = "" @@ -728,9 +727,22 @@ def execute(lmp_binary, config, input_file_name, generate_ref_yaml=False): cmd_str += config['mpiexec'] + " " + config['mpiexec_numproc_flag'] + " " + config['nprocs'] + " " cmd_str += lmp_binary + " -in " + input_file_name + " " + config['args'] logger.info(f" Executing: {cmd_str}") - p = subprocess.run(cmd_str, shell=True, text=True, capture_output=True) + # set a timeout (in seconds) for each run + timeout = 60 + if 'timeout' in config: + if config['timeout'] != "": + timeout = int(config['timeout']) - return cmd_str, p.stdout, p.stderr, p.returncode + try: + p = subprocess.run(cmd_str, shell=True, text=True, capture_output=True, timeout=timeout) + return cmd_str, p.stdout, p.stderr, p.returncode + + except subprocess.TimeoutExpired: + msg = f" Timeout for {cmd_str} ({timeout} s) expired" + logger.info(msg) + print(msg) + + return cmd_str, "", "", -1 ''' split a list into a list of N sublists From 1148f5f5c83af07b912c69c38850f9c4580935e5 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Sun, 25 Aug 2024 16:41:26 -0500 Subject: [PATCH 094/355] cast into PosixPath to str before writing to file --- .github/workflows/full-regression.yml | 2 -- tools/regression-tests/get-quick-list.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index 5e11fbbdd4..1dcccfcdcc 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -81,8 +81,6 @@ jobs: # restrict to official LAMMPS repository if: ${{ github.repository == 'lammps/lammps' }} runs-on: ubuntu-latest - env: - CCACHE_DIR: ${{ github.workspace }}/.ccache strategy: max-parallel: 2 matrix: diff --git a/tools/regression-tests/get-quick-list.py b/tools/regression-tests/get-quick-list.py index 672aa10f3a..9af91b139c 100644 --- a/tools/regression-tests/get-quick-list.py +++ b/tools/regression-tests/get-quick-list.py @@ -264,7 +264,7 @@ if __name__ == "__main__": with open('input_list.txt', 'w') as f: for inp in inputs: print(inp) - f.write(inp + '\n') + f.write(str(inp) + '\n') print("Found changes to the following styles:") print("Commands: ", styles['command']) From d9a2fd9f3601ff2590ab61a7ef8ebfd19ab2e939 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Fri, 23 Aug 2024 14:49:47 -0600 Subject: [PATCH 095/355] kokkos: move #endif to correct location --- src/KOKKOS/fft3d_kokkos.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/KOKKOS/fft3d_kokkos.cpp b/src/KOKKOS/fft3d_kokkos.cpp index 202d46e788..9d5347f173 100644 --- a/src/KOKKOS/fft3d_kokkos.cpp +++ b/src/KOKKOS/fft3d_kokkos.cpp @@ -43,7 +43,6 @@ FFT3dKokkos::FFT3dKokkos(LAMMPS *lmp, MPI_Comm comm, int nfast, int #if defined(LMP_KOKKOS_GPU) int ngpus = lmp->kokkos->ngpus; ExecutionSpace execution_space = ExecutionSpaceFromDevice::space; -#endif #if defined(FFT_KOKKOS_MKL) if (ngpus > 0 && execution_space == Device) @@ -69,6 +68,8 @@ FFT3dKokkos::FFT3dKokkos(LAMMPS *lmp, MPI_Comm comm, int nfast, int if (stack_size < 2048) cudaDeviceSetLimit(cudaLimitStackSize,2048); #endif +#endif + #endif plan = fft_3d_create_plan_kokkos(comm,nfast,nmid,nslow, From ac90dca5a1661a92c8632988f6067f8612878873 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 26 Aug 2024 11:38:26 -0400 Subject: [PATCH 096/355] whitespace --- doc/src/compute_temp.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/compute_temp.rst b/doc/src/compute_temp.rst index 4c7a46b822..2638a1ae11 100644 --- a/doc/src/compute_temp.rst +++ b/doc/src/compute_temp.rst @@ -59,7 +59,7 @@ factor, these tensor components are twice those of the traditional kinetic energy tensor. The six components of the vector are ordered :math:`xx`, :math:`yy`, :math:`zz`, :math:`xy`, :math:`xz`, :math:`yz`. - + The number of atoms contributing to the temperature is assumed to be constant for the duration of the run; use the *dynamic* option of the :doc:`compute_modify ` command if this is not the case. From a59ac7ec86587a5420e21f74ee178545f0b15442 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Mon, 26 Aug 2024 17:43:36 -0500 Subject: [PATCH 097/355] Reduced the timeout for quick reg tests --- tools/regression-tests/config_serial.yaml | 2 +- tools/regression-tests/run_tests.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/regression-tests/config_serial.yaml b/tools/regression-tests/config_serial.yaml index 746e74f226..ce984bb2b8 100644 --- a/tools/regression-tests/config_serial.yaml +++ b/tools/regression-tests/config_serial.yaml @@ -36,7 +36,7 @@ in.bucky-plus-cnt*, ] - timeout: 60 + timeout: 10 nugget: 1.0 epsilon: 1e-16 diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index f2d41af9de..d2a4559010 100644 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -163,6 +163,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file matched_pattern = False for skipped_files in config['skip']: if '*' in skipped_files: + # check input script name e.g. in.*_imd* if fnmatch.fnmatch(input, skipped_files): matched_pattern = True break From 2ebe1f019fb370251e4c932dd1d06ba7c4d705a2 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 26 Aug 2024 21:14:44 -0400 Subject: [PATCH 098/355] avoid uninitialized data access in case there are no atoms owned by a process --- src/REACTION/fix_bond_react.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/REACTION/fix_bond_react.cpp b/src/REACTION/fix_bond_react.cpp index b80231cf44..8ed21ce2ba 100644 --- a/src/REACTION/fix_bond_react.cpp +++ b/src/REACTION/fix_bond_react.cpp @@ -3714,7 +3714,7 @@ int FixBondReact::insert_atoms_setup(tagint **my_update_mega_glove, int iupdate) tagint *molecule = atom->molecule; int nlocal = atom->nlocal; - tagint maxmol_all; + tagint maxmol_all = 0;; for (int i = 0; i < nlocal; i++) maxmol_all = MAX(maxmol_all,molecule[i]); MPI_Allreduce(MPI_IN_PLACE,&maxmol_all,1,MPI_LMP_TAGINT,MPI_MAX,world); From 5fbdf155e5ab725a82dc8cdf4cb6c9d907c991fa Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 27 Aug 2024 08:57:42 -0400 Subject: [PATCH 099/355] remove unused variables --- src/REACTION/fix_bond_react.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/REACTION/fix_bond_react.cpp b/src/REACTION/fix_bond_react.cpp index 8ed21ce2ba..1e4ae33ee6 100644 --- a/src/REACTION/fix_bond_react.cpp +++ b/src/REACTION/fix_bond_react.cpp @@ -3693,7 +3693,6 @@ int FixBondReact::insert_atoms_setup(tagint **my_update_mega_glove, int iupdate) imageint *imageflags; double **coords,lamda[3],rotmat[3][3]; double *newcoord; - double **v = atom->v; double t,delx,dely,delz,rsq; memory->create(coords,twomol->natoms,3,"bond/react:coords"); @@ -3709,7 +3708,6 @@ int FixBondReact::insert_atoms_setup(tagint **my_update_mega_glove, int iupdate) } // find current max atom and molecule IDs - tagint *tag = atom->tag; double **x = atom->x; tagint *molecule = atom->molecule; int nlocal = atom->nlocal; From d8b9679b8702940b22c5f7e48640af6cc5a2c470 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 27 Aug 2024 10:10:29 -0400 Subject: [PATCH 100/355] fix cut-n-paste issue --- tools/lammps-gui/lammpswrapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/lammps-gui/lammpswrapper.cpp b/tools/lammps-gui/lammpswrapper.cpp index ed2bde1c9f..70d271f547 100644 --- a/tools/lammps-gui/lammpswrapper.cpp +++ b/tools/lammps-gui/lammpswrapper.cpp @@ -115,7 +115,7 @@ double LammpsWrapper::extract_variable(const char *keyword) } double val = *((double *)ptr); #if defined(LAMMPS_GUI_USE_PLUGIN) - ptr = ((liblammpsplugin_t *)plugin_handle)->free(ptr); + ((liblammpsplugin_t *)plugin_handle)->free(ptr); #else lammps_free(ptr); #endif From 83aab0f425f3988b49ec3eea40fdb2be3e952601 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 27 Aug 2024 10:40:24 -0400 Subject: [PATCH 101/355] add option to set plugin path from the command line --- tools/lammps-gui/main.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tools/lammps-gui/main.cpp b/tools/lammps-gui/main.cpp index 736a37d58b..abd5f6d099 100644 --- a/tools/lammps-gui/main.cpp +++ b/tools/lammps-gui/main.cpp @@ -17,6 +17,9 @@ #include #include #include +#include +#include +#include #include #include @@ -40,11 +43,27 @@ int main(int argc, char *argv[]) "\nA graphical editor for LAMMPS input files with syntax highlighting and\n" "auto-completion that can run LAMMPS directly. It has built-in capabilities\n" "for monitoring, visualization, plotting, and capturing console output."); +#if defined(LAMMPS_GUI_USE_PLUGIN) + QCommandLineOption plugindir(QStringList() << "p" + << "pluginpath", + "Path to LAMMPS shared library", "path"); + parser.addOption(plugindir); +#endif + parser.addHelpOption(); parser.addVersionOption(); parser.addPositionalArgument("file", "The LAMMPS input file to open (optional)."); parser.process(app); // this removes known arguments +#if defined(LAMMPS_GUI_USE_PLUGIN) + if (parser.isSet(plugindir)) { + QString pluginpath = parser.value(plugindir); + QSettings settings; + settings.setValue("plugin_path", pluginpath); + settings.sync(); + } +#endif + const char *infile = nullptr; if (argc > 1) infile = argv[1]; LammpsGui w(nullptr, infile); From 4060de6a9cd6a70f32f58b98a9d0b7be17075d6d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 27 Aug 2024 14:27:39 -0400 Subject: [PATCH 102/355] make handling of plugin path and recent files more consistent --- tools/lammps-gui/lammpsgui.cpp | 28 +++++++++++++++------------- tools/lammps-gui/lammpsgui.h | 4 ++-- tools/lammps-gui/main.cpp | 17 ++++++++++------- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/tools/lammps-gui/lammpsgui.cpp b/tools/lammps-gui/lammpsgui.cpp index d75f97da7a..e695d44c56 100644 --- a/tools/lammps-gui/lammpsgui.cpp +++ b/tools/lammps-gui/lammpsgui.cpp @@ -69,7 +69,7 @@ static const QString blank(" "); static constexpr int BUFLEN = 256; -LammpsGui::LammpsGui(QWidget *parent, const char *filename) : +LammpsGui::LammpsGui(QWidget *parent, const QString &filename) : QMainWindow(parent), ui(new Ui::LammpsGui), highlighter(nullptr), capturer(nullptr), status(nullptr), logwindow(nullptr), imagewindow(nullptr), chartwindow(nullptr), slideshow(nullptr), logupdater(nullptr), dirstatus(nullptr), progress(nullptr), @@ -98,21 +98,21 @@ LammpsGui::LammpsGui(QWidget *parent, const char *filename) : #if defined(LAMMPS_GUI_USE_PLUGIN) plugin_path.clear(); - std::string deffile = settings.value("plugin_path", "liblammps.so").toString().toStdString(); - for (const char *libfile : {deffile.c_str(), "./liblammps.so", "liblammps.dylib", + QString deffile = settings.value("plugin_path", "liblammps.so").toString(); + for (const char *libfile : {deffile.toStdString().c_str(), "./liblammps.so", "liblammps.dylib", "./liblammps.dylib", "liblammps.dll"}) { if (lammps.load_lib(libfile)) { - auto canonical = QFileInfo(libfile).canonicalFilePath(); - plugin_path = canonical.toStdString(); - settings.setValue("plugin_path", canonical); + plugin_path = QFileInfo(libfile).canonicalFilePath(); + settings.setValue("plugin_path", plugin_path); break; } } - if (plugin_path.empty()) { + if (plugin_path.isEmpty()) { // none of the plugin paths could load, remove key settings.remove("plugin_path"); - QMessageBox::critical(this, "Error", "Cannot open LAMMPS shared library file"); + QMessageBox::critical(this, "Error", "Cannot open LAMMPS shared library file.\n" + "Use -p command line flag to specify a path to the library."); exit(1); } #endif @@ -281,7 +281,7 @@ LammpsGui::LammpsGui(QWidget *parent, const char *filename) : dirstatus->show(); ui->statusbar->addWidget(progress); - if (filename) { + if (filename.size() > 0) { open_file(filename); } else { setWindowTitle("LAMMPS-GUI - Editor - *unknown*"); @@ -505,7 +505,8 @@ void LammpsGui::start_exe() void LammpsGui::update_recents(const QString &filename) { QSettings settings; - recent = settings.value("recent").value>(); + if (settings.contains("recent")) + recent = settings.value("recent").value>(); for (int i = 0; i < recent.size(); ++i) { QFileInfo fi(recent[i]); @@ -517,7 +518,8 @@ void LammpsGui::update_recents(const QString &filename) if (!filename.isEmpty() && !recent.contains(filename)) recent.prepend(filename); if (recent.size() > 5) recent.removeLast(); - settings.setValue("recent", QVariant::fromValue(recent)); + if (recent.size() > 0) settings.setValue("recent", QVariant::fromValue(recent)); + else settings.remove("recent"); ui->action_1->setVisible(false); if ((recent.size() > 0) && !recent[0].isEmpty()) { @@ -1438,9 +1440,9 @@ void LammpsGui::about() version += " using dark theme\n"; if (lammps.has_plugin()) { version += "LAMMPS library loaded as plugin"; - if (!plugin_path.empty()) { + if (!plugin_path.isEmpty()) { version += " from file "; - version += plugin_path; + version += plugin_path.toStdString(); } } else { version += "LAMMPS library linked to executable"; diff --git a/tools/lammps-gui/lammpsgui.h b/tools/lammps-gui/lammpsgui.h index 9185b7a535..a269e1a384 100644 --- a/tools/lammps-gui/lammpsgui.h +++ b/tools/lammps-gui/lammpsgui.h @@ -68,7 +68,7 @@ class LammpsGui : public QMainWindow { friend class Tutorial2Wizard; public: - LammpsGui(QWidget *parent = nullptr, const char *filename = nullptr); + LammpsGui(QWidget *parent = nullptr, const QString &filename = QString()); ~LammpsGui() override; protected: @@ -172,7 +172,7 @@ private: LammpsWrapper lammps; LammpsRunner *runner; QString docver; - std::string plugin_path; + QString plugin_path; bool is_running; int run_counter; std::vector lammps_args; diff --git a/tools/lammps-gui/main.cpp b/tools/lammps-gui/main.cpp index abd5f6d099..badee254f1 100644 --- a/tools/lammps-gui/main.cpp +++ b/tools/lammps-gui/main.cpp @@ -53,19 +53,22 @@ int main(int argc, char *argv[]) parser.addHelpOption(); parser.addVersionOption(); parser.addPositionalArgument("file", "The LAMMPS input file to open (optional)."); - parser.process(app); // this removes known arguments + parser.process(app); #if defined(LAMMPS_GUI_USE_PLUGIN) if (parser.isSet(plugindir)) { - QString pluginpath = parser.value(plugindir); - QSettings settings; - settings.setValue("plugin_path", pluginpath); - settings.sync(); + QStringList pluginpath = parser.values(plugindir); + if (pluginpath.length() > 0) { + QSettings settings; + settings.setValue("plugin_path", QString(pluginpath.at(0))); + settings.sync(); + } } #endif - const char *infile = nullptr; - if (argc > 1) infile = argv[1]; + QString infile; + QStringList args = parser.positionalArguments(); + if (args.size() > 0) infile = args[0]; LammpsGui w(nullptr, infile); w.show(); return app.exec(); From 49d664583a9c314b762e6249d28e40cc9a530e9c Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 27 Aug 2024 17:00:42 -0400 Subject: [PATCH 103/355] correct define --- examples/COUPLE/plugin/liblammpsplugin.c | 1 - src/KOKKOS/pair_uf3_kokkos.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/COUPLE/plugin/liblammpsplugin.c b/examples/COUPLE/plugin/liblammpsplugin.c index 5d27a0a64b..0549f3ab8c 100644 --- a/examples/COUPLE/plugin/liblammpsplugin.c +++ b/examples/COUPLE/plugin/liblammpsplugin.c @@ -41,7 +41,6 @@ #include - liblammpsplugin_t *liblammpsplugin_load(const char *lib) { liblammpsplugin_t *lmp; diff --git a/src/KOKKOS/pair_uf3_kokkos.cpp b/src/KOKKOS/pair_uf3_kokkos.cpp index 59112ddab0..da7660d0d0 100644 --- a/src/KOKKOS/pair_uf3_kokkos.cpp +++ b/src/KOKKOS/pair_uf3_kokkos.cpp @@ -1655,7 +1655,7 @@ double PairUF3Kokkos::single(int /*i*/, int /*j*/, int itype, int jt namespace LAMMPS_NS { template class PairUF3Kokkos; -#ifdef KOKKOS_ENABLE_GPU +#ifdef LMP_KOKKOS_GPU template class PairUF3Kokkos; #endif } // namespace LAMMPS_NS From c01585e8b23f1ae51a55a09448d32ed94a13a542 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 27 Aug 2024 17:02:17 -0400 Subject: [PATCH 104/355] revise plugin loading logic --- tools/lammps-gui/lammpsgui.cpp | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/tools/lammps-gui/lammpsgui.cpp b/tools/lammps-gui/lammpsgui.cpp index e695d44c56..cf6f2e32fd 100644 --- a/tools/lammps-gui/lammpsgui.cpp +++ b/tools/lammps-gui/lammpsgui.cpp @@ -97,21 +97,26 @@ LammpsGui::LammpsGui(QWidget *parent, const QString &filename) : QSettings settings; #if defined(LAMMPS_GUI_USE_PLUGIN) - plugin_path.clear(); - QString deffile = settings.value("plugin_path", "liblammps.so").toString(); - for (const char *libfile : {deffile.toStdString().c_str(), "./liblammps.so", "liblammps.dylib", - "./liblammps.dylib", "liblammps.dll"}) { - if (lammps.load_lib(libfile)) { - plugin_path = QFileInfo(libfile).canonicalFilePath(); - settings.setValue("plugin_path", plugin_path); - break; + plugin_path = settings.value("plugin_path", "liblammps.so").toString(); + if (!lammps.load_lib(plugin_path.toStdString().c_str())) { + // fall back to defaults + for (const char *libfile : + {"./liblammps.so", "liblammps.dylib", "./liblammps.dylib", "liblammps.dll"}) { + if (lammps.load_lib(libfile)) { + plugin_path = QFileInfo(libfile).canonicalFilePath(); + settings.setValue("plugin_path", plugin_path); + break; + } else { + plugin_path.clear(); + } } } if (plugin_path.isEmpty()) { // none of the plugin paths could load, remove key settings.remove("plugin_path"); - QMessageBox::critical(this, "Error", "Cannot open LAMMPS shared library file.\n" + QMessageBox::critical(this, "Error", + "Cannot open LAMMPS shared library file.\n" "Use -p command line flag to specify a path to the library."); exit(1); } @@ -505,8 +510,7 @@ void LammpsGui::start_exe() void LammpsGui::update_recents(const QString &filename) { QSettings settings; - if (settings.contains("recent")) - recent = settings.value("recent").value>(); + if (settings.contains("recent")) recent = settings.value("recent").value>(); for (int i = 0; i < recent.size(); ++i) { QFileInfo fi(recent[i]); @@ -518,8 +522,10 @@ void LammpsGui::update_recents(const QString &filename) if (!filename.isEmpty() && !recent.contains(filename)) recent.prepend(filename); if (recent.size() > 5) recent.removeLast(); - if (recent.size() > 0) settings.setValue("recent", QVariant::fromValue(recent)); - else settings.remove("recent"); + if (recent.size() > 0) + settings.setValue("recent", QVariant::fromValue(recent)); + else + settings.remove("recent"); ui->action_1->setVisible(false); if ((recent.size() > 0) && !recent[0].isEmpty()) { From 98b4771ed67eddc54920804a5ff9776e7e3b5792 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 27 Aug 2024 17:18:01 -0400 Subject: [PATCH 105/355] sanity check --- examples/COUPLE/plugin/liblammpsplugin.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/COUPLE/plugin/liblammpsplugin.c b/examples/COUPLE/plugin/liblammpsplugin.c index 0549f3ab8c..50ad2f5192 100644 --- a/examples/COUPLE/plugin/liblammpsplugin.c +++ b/examples/COUPLE/plugin/liblammpsplugin.c @@ -190,6 +190,9 @@ liblammpsplugin_t *liblammpsplugin_load(const char *lib) ADDSYM(is_running); ADDSYM(force_timeout); + // symbol not present + if (!lmp->config_has_exceptions) return NULL; + lmp->has_exceptions = lmp->config_has_exceptions(); if (lmp->has_exceptions) { ADDSYM(has_error); From 99e5b062790362c8030a0ec05f1c88f983c717c2 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 27 Aug 2024 17:18:11 -0400 Subject: [PATCH 106/355] make plugin path canonical --- tools/lammps-gui/lammpsgui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/lammps-gui/lammpsgui.cpp b/tools/lammps-gui/lammpsgui.cpp index cf6f2e32fd..4826b14719 100644 --- a/tools/lammps-gui/lammpsgui.cpp +++ b/tools/lammps-gui/lammpsgui.cpp @@ -97,7 +97,8 @@ LammpsGui::LammpsGui(QWidget *parent, const QString &filename) : QSettings settings; #if defined(LAMMPS_GUI_USE_PLUGIN) - plugin_path = settings.value("plugin_path", "liblammps.so").toString(); + plugin_path = + QFileInfo(settings.value("plugin_path", "liblammps.so").toString()).canonicalFilePath(); if (!lammps.load_lib(plugin_path.toStdString().c_str())) { // fall back to defaults for (const char *libfile : From 932eaf864a3176980f8bf8f32d871f46c39c84c7 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 27 Aug 2024 21:19:09 -0400 Subject: [PATCH 107/355] must register variant for QList before using QSettings --- tools/lammps-gui/lammpsgui.cpp | 5 ----- tools/lammps-gui/main.cpp | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/lammps-gui/lammpsgui.cpp b/tools/lammps-gui/lammpsgui.cpp index 4826b14719..fe6b8c5391 100644 --- a/tools/lammps-gui/lammpsgui.cpp +++ b/tools/lammps-gui/lammpsgui.cpp @@ -76,11 +76,6 @@ LammpsGui::LammpsGui(QWidget *parent, const QString &filename) : prefdialog(nullptr), lammpsstatus(nullptr), varwindow(nullptr), wizard(nullptr), runner(nullptr), is_running(false), run_counter(0) { -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - // register QList only needed for Qt5 - qRegisterMetaTypeStreamOperators>("QList"); -#endif - docver = ""; ui->setupUi(this); this->setCentralWidget(ui->textEdit); diff --git a/tools/lammps-gui/main.cpp b/tools/lammps-gui/main.cpp index badee254f1..53bdaca8fd 100644 --- a/tools/lammps-gui/main.cpp +++ b/tools/lammps-gui/main.cpp @@ -30,6 +30,11 @@ int main(int argc, char *argv[]) { Q_INIT_RESOURCE(lammpsgui); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + // register QList only needed for Qt5 + qRegisterMetaTypeStreamOperators>("QList"); +#endif + QApplication app(argc, argv); // enforce using the plain ASCII C locale within the GUI. QLocale::setDefault(QLocale::c()); From 78310bddcff1e01b615f02c7134997279b2ad52a Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 27 Aug 2024 21:43:00 -0400 Subject: [PATCH 108/355] update docs for LAMMPS-GUI plugin changes and document flatpak build --- doc/src/Tools.rst | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/doc/src/Tools.rst b/doc/src/Tools.rst index 6c12baf967..9f9f63f46a 100644 --- a/doc/src/Tools.rst +++ b/doc/src/Tools.rst @@ -590,20 +590,31 @@ and the LAMMPS library, via ``-D LAMMPS_SOURCE_DIR=/path/to/lammps/src``. CMake will try to guess a build folder with the LAMMPS library from that path, but it can also be set with ``-D LAMMPS_LIB_DIR=/path/to/lammps/lib``. +Plugin version +"""""""""""""" + Rather than linking to the LAMMPS library during compilation, it is also -possible to compile the GUI with a plugin loader that will load -the LAMMPS library dynamically at runtime during the start of the GUI -from a shared library; e.g. ``liblammps.so`` or ``liblammps.dylib`` or +possible to compile the GUI with a plugin loader that will load the +LAMMPS library dynamically at runtime during the start of the GUI from a +shared library; e.g. ``liblammps.so`` or ``liblammps.dylib`` or ``liblammps.dll`` (depending on the operating system). This has the advantage that the LAMMPS library can be built from updated or modified LAMMPS source without having to recompile the GUI. The ABI of the LAMMPS C-library interface is very stable and generally backward -compatible. This feature is enabled by setting -``-D LAMMPS_GUI_USE_PLUGIN=on`` and then ``-D +compatible. This feature is enabled by setting ``-D +LAMMPS_GUI_USE_PLUGIN=on`` and then ``-D LAMMPS_PLUGINLIB_DIR=/path/to/lammps/plugin/loader``. Typically, this would be the ``examples/COUPLE/plugin`` folder of the LAMMPS distribution. +When compiling LAMMPS-GUI with plugin support, there is an additional +command line flag (``-p `` or ``--pluginpath ``) which +allows to override the path to LAMMPS shared library used by the GUI. +This is usually auto-detected on the first run and can be changed in the +LAMMPS-GUI *Preferences* dialog. The command line flag allows to reset +this path to a valid value in case the original setting has become +invalid. An empty path ("") as argument restores the default setting. + Platform notes ^^^^^^^^^^^^^^ @@ -671,6 +682,15 @@ folder> --target tgz`` or ``make tgz`` to build a ``LAMMPS-Linux-amd64.tar.gz`` file with the executables and their support libraries. +It is also possible to build a `flatpak bundle +`_ which is +a way to distribute applications in a way that is compatible with most +Linux distributions. Use the "flatpak" target to trigger a compile +(``cmake --build --target flatpak`` or ``make flatpak``). +Please note that this will not build from the local sources but from the +repository and branch listed in the ``org.lammps.lammps-gui.yml`` +LAMMPS-GUI source folder. + ---------- .. _arc: From 5c11c5ead8de623f00d205bca549380df5b8f47f Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Wed, 28 Aug 2024 10:48:12 +0200 Subject: [PATCH 109/355] include variable definitions in region_plane.h --- src/region_plane.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/region_plane.h b/src/region_plane.h index 2025586a7c..0e4ecda6d4 100644 --- a/src/region_plane.h +++ b/src/region_plane.h @@ -28,13 +28,23 @@ class RegPlane : public Region { public: RegPlane(class LAMMPS *, int, char **); ~RegPlane() override; + void init() override; int inside(double, double, double) override; int surface_interior(double *, double) override; int surface_exterior(double *, double) override; + void shape_update() override; private: double xp, yp, zp; double normal[3]; + + int xstyle, xvar; + int ystyle, yvar; + int zstyle, zvar; + char *xstr, *ystr, *zstr; + + void variable_check(); + }; } // namespace LAMMPS_NS From 40cd70465cba86d7ea631cfe2c675c15fad90b39 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Wed, 28 Aug 2024 10:57:02 +0200 Subject: [PATCH 110/355] Code for variable point definition in region_plane.cpp --- src/region_plane.cpp | 99 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 4 deletions(-) diff --git a/src/region_plane.cpp b/src/region_plane.cpp index 154b072633..d8c09fa3cb 100644 --- a/src/region_plane.cpp +++ b/src/region_plane.cpp @@ -14,20 +14,58 @@ #include "region_plane.h" #include "error.h" +#include "input.h" +#include "update.h" +#include "variable.h" #include using namespace LAMMPS_NS; +enum { CONSTANT, VARIABLE }; + /* ---------------------------------------------------------------------- */ -RegPlane::RegPlane(LAMMPS *lmp, int narg, char **arg) : Region(lmp, narg, arg) +RegPlane::RegPlane(LAMMPS *lmp, int narg, char **arg) : Region(lmp, narg, arg), + xstr(nullptr), ystr(nullptr), zstr(nullptr) { options(narg - 8, &arg[8]); - xp = xscale * utils::numeric(FLERR, arg[2], false, lmp); - yp = yscale * utils::numeric(FLERR, arg[3], false, lmp); - zp = zscale * utils::numeric(FLERR, arg[4], false, lmp); + if (utils::strmatch(arg[2], "^v_")) { + xstr = utils::strdup(arg[2] + 2); + xp = 0.0; + xstyle = VARIABLE; + varshape = 1; + } else { + xp = xscale * utils::numeric(FLERR, arg[2], false, lmp); + xstyle = CONSTANT; + } + + if (utils::strmatch(arg[3], "^v_")) { + ystr = utils::strdup(arg[3] + 2); + yp = 0.0; + ystyle = VARIABLE; + varshape = 1; + } else { + yp = yscale * utils::numeric(FLERR, arg[3], false, lmp); + ystyle = CONSTANT; + } + + if (utils::strmatch(arg[4], "^v_")) { + zstr = utils::strdup(arg[4] + 2); + zp = 0.0; + zstyle = VARIABLE; + varshape = 1; + } else { + zp = zscale * utils::numeric(FLERR, arg[4], false, lmp); + zstyle = CONSTANT; + } + + if (varshape) { + variable_check(); + RegPlane::shape_update(); + } + normal[0] = xscale * utils::numeric(FLERR, arg[5], false, lmp); normal[1] = yscale * utils::numeric(FLERR, arg[6], false, lmp); normal[2] = zscale * utils::numeric(FLERR, arg[7], false, lmp); @@ -54,9 +92,20 @@ RegPlane::RegPlane(LAMMPS *lmp, int narg, char **arg) : Region(lmp, narg, arg) RegPlane::~RegPlane() { + delete[] xstr; + delete[] ystr; + delete[] zstr; delete[] contact; } +/* ---------------------------------------------------------------------- */ + +void RegPlane::init() +{ + Region::init(); + if (varshape) variable_check(); +} + /* ---------------------------------------------------------------------- inside = 1 if x,y,z is on normal side of plane or on plane inside = 0 if x,y,z is on non-normal side of plane and not on plane @@ -113,3 +162,45 @@ int RegPlane::surface_exterior(double *x, double cutoff) } return 0; } + +/* ---------------------------------------------------------------------- + change region shape via variable evaluation +------------------------------------------------------------------------- */ + +void RegPlane::shape_update() +{ + if (xstyle == VARIABLE) xp = xscale * input->variable->compute_equal(xvar); + + if (ystyle == VARIABLE) yp = yscale * input->variable->compute_equal(yvar); + + if (zstyle == VARIABLE) zp = zscale * input->variable->compute_equal(zvar); +} + +/* ---------------------------------------------------------------------- + error check on existence of variable +------------------------------------------------------------------------- */ + +void RegPlane::variable_check() +{ + if (xstyle == VARIABLE) { + xvar = input->variable->find(xstr); + if (xvar < 0) error->all(FLERR, "Variable {} for region plane does not exist", xstr); + if (!input->variable->equalstyle(xvar)) + error->all(FLERR, "Variable {} for region plane is invalid style", xstr); + } + + if (ystyle == VARIABLE) { + yvar = input->variable->find(ystr); + if (yvar < 0) error->all(FLERR, "Variable {} for region plane does not exist", ystr); + if (!input->variable->equalstyle(yvar)) + error->all(FLERR, "Variable {} for region plane is invalid style", ystr); + } + + if (zstyle == VARIABLE) { + zvar = input->variable->find(zstr); + if (zvar < 0) error->all(FLERR, "Variable {} for region plane does not exist", zstr); + if (!input->variable->equalstyle(zvar)) + error->all(FLERR, "Variable {} for region plane is invalid style", zstr); + } +} + From 709ab8fbe92a1815382db0491c9965c16512b4dd Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Wed, 28 Aug 2024 11:04:47 +0200 Subject: [PATCH 111/355] Update region.rst --- doc/src/region.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/src/region.rst b/doc/src/region.rst index 9d2af01de1..7e3c95362c 100644 --- a/doc/src/region.rst +++ b/doc/src/region.rst @@ -34,10 +34,11 @@ Syntax *ellipsoid* args = x y z a b c x,y,z = center of ellipsoid (distance units) a,b,c = half the length of the principal axes of the ellipsoid (distance units) - x,y,z,a,b and c can be a variable (see below) + x,y,z,a,b and c can be a variable (see below) *plane* args = px py pz nx ny nz px,py,pz = point on the plane (distance units) nx,ny,nz = direction normal to plane (distance units) + px,py,pz can be a variable *prism* args = xlo xhi ylo yhi zlo zhi xy xz yz xlo,xhi,ylo,yhi,zlo,zhi = bounds of untilted prism (distance units) xy = distance to tilt y in x direction (distance units) @@ -46,7 +47,7 @@ Syntax *sphere* args = x y z radius x,y,z = center of sphere (distance units) radius = radius of sphere (distance units) - x,y,z, and radius can be a variable (see below) + x,y,z, and radius can be a variable (see below) *union* args = N reg-ID1 reg-ID2 ... N = # of regions to follow, must be 2 or greater reg-ID1,reg-ID2, ... = IDs of regions to join together @@ -203,12 +204,13 @@ and with radius as its radius. The *radius* value for styles *sphere* and *cylinder*, and the parameters a,b,c for style *ellipsoid*, can each be specified as an -equal-style :doc:`variable `. Likewise, for style *sphere* +equal-style :doc:`variable `. Likewise, for style *sphere* and *ellipsoid* the x-, y-, and z- coordinates of the center of the -sphere/ellipsoid can be specified as an equal-style variable. And for +sphere/ellipsoid can be specified as an equal-style variable. And for style *cylinder* the two center positions c1 and c2 for the location of the cylinder axes can be specified as a equal-style variable. For style *cone* -all properties can be defined via equal-style variables. +all properties can be defined via equal-style variables. For style *plane* +the point can be defined via equal-style variables. If the value is a variable, it should be specified as v_name, where name is the variable name. In this case, the variable will be From f2102b76f212512d6d40246397296f8d544abd86 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 28 Aug 2024 11:09:47 -0400 Subject: [PATCH 112/355] cosmetic --- src/CLASS2/angle_class2.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/CLASS2/angle_class2.cpp b/src/CLASS2/angle_class2.cpp index 1dbaaf0568..118179ad91 100644 --- a/src/CLASS2/angle_class2.cpp +++ b/src/CLASS2/angle_class2.cpp @@ -18,17 +18,17 @@ #include "angle_class2.h" -#include -#include #include "atom.h" -#include "neighbor.h" -#include "domain.h" #include "comm.h" +#include "domain.h" +#include "error.h" #include "force.h" #include "math_const.h" #include "memory.h" -#include "error.h" +#include "neighbor.h" +#include +#include using namespace LAMMPS_NS; using namespace MathConst; From e3119155e138c3d7d4b3da070b7134b484de4d5e Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Wed, 28 Aug 2024 18:20:18 +0200 Subject: [PATCH 113/355] Update doc/src/region.rst Co-authored-by: Axel Kohlmeyer --- doc/src/region.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/region.rst b/doc/src/region.rst index 7e3c95362c..a6a9469381 100644 --- a/doc/src/region.rst +++ b/doc/src/region.rst @@ -38,7 +38,7 @@ Syntax *plane* args = px py pz nx ny nz px,py,pz = point on the plane (distance units) nx,ny,nz = direction normal to plane (distance units) - px,py,pz can be a variable + px,py,pz can be a variable *prism* args = xlo xhi ylo yhi zlo zhi xy xz yz xlo,xhi,ylo,yhi,zlo,zhi = bounds of untilted prism (distance units) xy = distance to tilt y in x direction (distance units) From f5ffb28a1f3d20b7a51ddcc093a9e4e7b1bd15f4 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Wed, 28 Aug 2024 12:57:58 -0500 Subject: [PATCH 114/355] use the log file with 1 proc in serial runs, removed examples/bpm/impact/brokenDump --- examples/bpm/impact/brokenDump | 3914 --------------------------- tools/regression-tests/run_tests.py | 85 +- 2 files changed, 54 insertions(+), 3945 deletions(-) delete mode 100644 examples/bpm/impact/brokenDump diff --git a/examples/bpm/impact/brokenDump b/examples/bpm/impact/brokenDump deleted file mode 100644 index 0a2316cd5e..0000000000 --- a/examples/bpm/impact/brokenDump +++ /dev/null @@ -1,3914 +0,0 @@ -599 1817 5468 -600 1808 1815 -554 1938 5471 -548 5471 5591 -554 1938 5471 -571 5471 5590 -572 5471 5589 -599 1817 5468 -548 5471 5591 -571 5471 5590 -572 5471 5589 -608 1814 1815 -616 1815 1926 -622 5231 5350 -622 1818 1935 -635 1818 1929 -638 5468 5469 -648 1806 1923 -648 1818 1937 -649 1811 1812 -650 1822 5468 -650 1822 1935 -652 5469 5472 -655 1822 5589 -660 1812 1813 -662 1816 1929 -666 1810 1923 -672 1935 1937 -676 1810 1812 -676 1822 1938 -677 1806 1917 -677 1822 1937 -679 1812 1816 -683 1819 1937 -683 1816 1931 -684 1816 1937 -686 1810 1931 -695 1583 1584 -699 1821 1822 -700 1812 1929 -616 1815 1926 -635 1818 1929 -648 1806 1923 -650 1822 1935 -660 1923 1930 -662 1816 1929 -666 1810 1923 -671 1935 1936 -672 1935 1937 -676 1822 1938 -677 1806 1917 -683 1816 1931 -686 1810 1931 -687 2052 5709 -692 1923 2040 -692 2169 5709 -694 2052 5588 -696 1936 1940 -700 1812 1929 -605 5477 5596 -610 5467 5468 -611 5237 5350 -622 5231 5350 -628 5467 5474 -629 5473 5474 -635 5467 5472 -637 5473 5480 -638 5468 5469 -640 5474 5475 -646 5354 5467 -647 5467 5475 -650 1822 5468 -652 5347 5354 -652 5469 5472 -653 5479 5480 -658 5473 5478 -661 5472 5475 -661 5474 5478 -663 5480 5481 -665 5358 5478 -668 5227 5234 -669 5480 5593 -670 5347 5355 -671 5234 5347 -672 5479 5486 -674 5352 5354 -674 5475 5478 -677 5471 5472 -678 5478 5593 -679 5478 5481 -681 5352 5355 -682 5352 5475 -682 5468 5471 -687 5352 5357 -687 5486 5599 -689 5232 5234 -689 5481 5601 -690 5476 5478 -691 5480 5484 -693 5484 5599 -694 5471 5477 -700 5479 5484 -655 1822 5589 -669 5480 5593 -678 5478 5593 -687 5486 5599 -687 2052 5709 -692 2169 5709 -693 5484 5599 -693 5594 5600 -694 2052 5588 -695 5600 5714 -699 5828 5829 -700 5951 5958 -704 1806 1925 -706 1810 1925 -711 1816 1932 -711 1937 1940 -712 1800 1917 -716 1926 1931 -720 1457 1464 -721 1804 1917 -722 5469 5470 -722 1803 1920 -728 1820 5471 -729 1802 1809 -730 1925 1928 -734 1353 1469 -735 1923 1925 -737 5230 5231 -738 1804 1925 -739 1810 1926 -739 1816 1821 -740 1911 1918 -743 1822 5471 -745 1821 1937 -746 1458 1574 -755 1586 5231 -756 1469 4994 -756 1463 1470 -756 1467 1577 -756 1815 1931 -757 1804 1919 -758 1683 1793 -758 1800 1919 -760 1809 1810 -762 1692 1808 -763 5469 5471 -763 1932 1937 -765 1808 1810 -766 1914 1915 -769 1804 1806 -769 1821 1938 -771 1806 1809 -774 1926 1933 -776 1459 1574 -776 1568 1575 -776 1573 1574 -779 4994 5110 -779 5111 5229 -779 5229 5230 -781 1804 1807 -783 1797 1908 -783 1800 1806 -784 5112 5229 -787 1799 1804 -788 1799 1805 -789 1464 1580 -789 1802 1807 -795 1807 1810 -800 1674 1790 -800 1790 1797 -703 2057 5709 -704 5952 6071 -711 1816 1932 -711 1937 1940 -711 2052 5710 -712 1800 1917 -716 1926 1931 -717 2169 5829 -719 2277 2278 -721 1804 1917 -722 1803 1920 -724 2037 2038 -727 1940 2052 -730 1925 1928 -730 1940 2054 -731 1928 1930 -734 2169 5830 -735 1923 1925 -736 1928 1931 -736 1924 2040 -736 2044 2154 -736 2057 5710 -740 1911 1918 -745 1928 1934 -746 1923 1928 -746 1923 1931 -748 1921 2031 -749 1939 1940 -750 2170 2286 -751 1928 2048 -754 1928 2040 -755 1931 1933 -756 1917 1919 -756 1815 1931 -757 1804 1919 -758 1800 1919 -758 1933 2043 -759 1930 2040 -761 2057 5830 -762 1921 2038 -762 2283 2284 -763 1932 1937 -764 2287 2402 -766 1914 1915 -766 2040 2047 -769 1821 1938 -771 2161 2271 -774 1926 1933 -774 2406 2513 -778 2040 2042 -782 1930 1931 -784 2174 5829 -785 1912 2028 -788 2043 2048 -788 2041 2157 -790 1923 1924 -793 1940 2055 -795 2038 2039 -796 2174 5830 -799 1931 1934 -799 1937 1938 -799 2286 2287 -702 5114 5233 -702 5484 5601 -703 5470 5472 -704 5116 5117 -704 5255 5368 -707 5352 5470 -707 5495 5608 -710 5232 5237 -711 5232 5355 -713 5483 5602 -714 5478 5483 -716 5476 5477 -717 5232 5235 -718 5470 5471 -720 5227 5235 -721 5230 5232 -721 5599 5607 -722 5469 5470 -722 5482 5483 -725 5364 5484 -726 5123 5236 -728 1820 5471 -729 5366 5485 -729 5483 5601 -734 5350 5355 -737 5230 5231 -737 5350 5357 -739 5234 5235 -741 5374 5375 -742 5488 5495 -743 1822 5471 -745 5481 5484 -746 5114 5227 -749 5500 5501 -749 5606 5607 -751 5486 5605 -751 5604 5607 -751 5614 5615 -753 5479 5487 -760 5601 5602 -762 5113 5120 -763 5000 5110 -763 5469 5471 -763 5492 5605 -765 5108 5114 -767 5112 5227 -771 5472 5477 -773 5476 5596 -775 5381 5494 -778 5486 5487 -779 4994 5110 -779 5111 5229 -779 5229 5230 -779 5375 5488 -779 5470 5477 -781 5107 5108 -783 5116 5118 -784 5112 5229 -784 5242 5243 -784 5602 5604 -787 5368 5369 -787 5475 5477 -789 5233 5235 -791 5129 5242 -791 5602 5603 -792 5248 5249 -794 5485 5486 -795 5477 5478 -702 5484 5601 -703 2057 5709 -704 5952 6071 -708 5957 5964 -709 5593 5600 -711 2052 5710 -717 2169 5829 -718 5709 5829 -720 5736 5855 -721 5593 5601 -721 5599 5607 -722 5720 5721 -729 5483 5601 -730 5598 5600 -730 5828 5830 -733 5735 5742 -733 5709 5830 -733 5958 6077 -734 2169 5830 -736 2057 5710 -738 5714 5722 -740 5598 5601 -747 5605 5606 -749 5606 5607 -751 5486 5605 -751 5604 5607 -751 5614 5615 -751 5849 5856 -757 5841 5954 -759 5621 5741 -760 5601 5602 -761 5721 5722 -761 2057 5830 -761 5828 5833 -763 5492 5605 -769 5835 5948 -772 5721 5725 -776 5615 5735 -784 5602 5604 -784 2174 5829 -785 5833 5835 -786 5969 5976 -790 5726 5727 -791 5602 5603 -795 5831 5951 -796 2174 5830 -798 5605 5726 -799 5960 5961 -801 1568 1685 -801 1586 5229 -802 1463 1465 -802 1799 1800 -803 4991 5107 -803 1673 1680 -803 1685 1692 -803 5108 5112 -804 1799 1806 -804 1806 1807 -804 1911 1916 -805 1799 1802 -807 1807 1809 -808 1573 1575 -808 1586 1588 -808 5350 5351 -808 5351 5352 -810 1354 1469 -813 4994 5109 -813 1353 1468 -816 1580 1587 -816 1819 1821 -818 5351 5470 -819 1685 1690 -820 1689 1799 -821 1463 1468 -821 1911 1912 -823 1464 1574 -823 1575 1685 -823 1689 1806 -824 1471 5229 -824 1685 1687 -824 1799 1801 -825 1570 1575 -827 1583 1586 -827 1676 1677 -831 1463 1464 -833 1683 1801 -835 1805 1806 -836 5108 5109 -841 1586 1587 -841 1687 1807 -842 1686 1687 -843 1686 1802 -845 1681 1793 -847 1676 1681 -848 1471 5108 -849 1570 1690 -849 1687 1689 -849 1692 1802 -850 1684 1690 -851 1808 1809 -852 1676 1678 -853 1348 1465 -853 1464 1465 -854 1462 1464 -854 1464 1579 -854 1471 1583 -856 1348 1468 -857 1684 1687 -857 1687 1801 -857 1687 1799 -858 1570 1576 -859 1806 1810 -860 1585 5229 -860 1585 1588 -864 1462 1465 -865 1354 5109 -868 1681 1683 -870 1459 1576 -871 1573 1576 -872 1354 1468 -872 1683 1799 -873 1686 1801 -876 1585 1587 -877 1459 1579 -879 1687 1802 -882 1683 1686 -885 1584 1585 -885 1681 1801 -886 1466 5108 -886 1583 1585 -893 4995 5109 -896 1459 1464 -900 1354 1466 -900 1682 1684 -801 2045 2047 -802 2041 2158 -802 2396 2403 -803 1929 1931 -803 2039 2154 -804 1911 1916 -805 2045 2048 -805 2154 2159 -808 2040 2048 -812 1918 1919 -812 2045 2051 -812 2045 2157 -814 2040 2045 -814 2154 2161 -817 1928 1933 -818 1932 1938 -819 1932 1934 -819 2047 2050 -821 1911 1912 -821 2045 2165 -821 2156 2161 -822 2048 2050 -823 2286 5829 -825 2055 5710 -825 2047 2157 -828 2036 2038 -829 2156 2159 -829 2172 5830 -831 2161 2276 -831 2274 2281 -838 2167 2277 -839 1931 1932 -839 2022 2029 -841 1938 1939 -842 2159 2161 -842 2164 2280 -844 2276 2277 -848 2039 2153 -849 6191 6307 -852 2049 2050 -855 2050 2051 -856 2047 2165 -859 2161 2162 -861 5710 5712 -861 2157 2164 -863 5830 5832 -868 2162 2276 -869 2050 2165 -873 2631 6191 -874 5831 5832 -875 2276 2282 -876 2031 2036 -876 2290 2399 -879 2044 2161 -879 5712 5830 -881 2163 2165 -881 2161 2277 -884 2051 2165 -884 2507 2508 -885 2055 5591 -887 2051 2166 -889 2277 2279 -890 2160 2161 -890 2519 2520 -892 1933 1934 -896 2163 2164 -897 2393 2394 -897 2394 2501 -898 2282 2284 -898 2405 5952 -803 4991 5107 -803 5108 5112 -803 5118 5123 -803 5366 5487 -806 5477 5590 -808 5350 5351 -808 5351 5352 -812 5507 5620 -818 5351 5470 -822 5115 5116 -822 5122 5123 -822 5483 5596 -822 5484 5602 -824 5247 5248 -825 5365 5372 -825 5364 5487 -826 5129 5247 -829 5255 5373 -829 5364 5370 -832 5477 5595 -832 5483 5484 -832 5484 5486 -832 5611 5612 -834 5123 5241 -837 5124 5129 -840 5373 5374 -841 5000 5115 -842 5250 5255 -843 5130 5247 -843 5349 5350 -844 5122 5124 -846 5366 5370 -848 5248 5250 -848 5363 5483 -850 5249 5250 -853 5499 5500 -856 5365 5370 -859 5118 5121 -859 5235 5238 -859 5481 5482 -862 5124 5127 -865 5230 5237 -865 5256 5373 -865 5367 5370 -869 4995 5110 -869 5476 5483 -874 5350 5352 -875 5250 5256 -876 5374 5376 -877 5364 5482 -877 5376 5381 -879 5130 5250 -884 5250 5253 -884 5365 5373 -888 5001 5115 -891 5364 5369 -891 5381 5499 -891 5481 5483 -893 4995 5109 -894 4990 4997 -897 4997 5113 -898 5245 5250 -899 4995 5115 -900 5245 5247 -900 5371 5378 -803 5604 5609 -803 5970 6089 -805 5620 5627 -806 5477 5590 -810 5606 5610 -811 6196 6203 -812 5507 5620 -813 5612 5726 -815 6197 6313 -816 5856 5975 -816 6083 6090 -823 2286 5829 -825 2055 5710 -829 2172 5830 -832 5477 5595 -832 5611 5612 -832 5854 5855 -833 6084 6202 -833 6197 6307 -836 5971 5976 -837 5835 5955 -840 5971 6089 -840 6084 6090 -841 5732 5733 -843 5969 5974 -847 5737 5742 -847 5958 6071 -848 6075 6187 -849 6201 6202 -849 6085 6202 -849 6191 6307 -850 5727 5840 -852 5954 5955 -853 5855 5862 -855 5609 5723 -856 5610 5726 -858 6201 6203 -859 5740 5741 -861 5710 5712 -862 5604 5728 -862 5955 6074 -862 5955 5959 -863 5830 5832 -863 5954 5956 -864 5737 5855 -865 5955 5956 -865 5953 6071 -865 6088 6089 -866 5970 6088 -866 6083 6085 -866 6197 6312 -867 5835 5956 -867 5974 5976 -867 6083 6088 -868 5961 5967 -869 6085 6090 -871 5610 5728 -871 5954 5959 -872 5606 5728 -872 5740 5742 -872 5954 6074 -873 5857 5862 -873 6088 6090 -874 5831 5832 -877 5857 5975 -877 5948 5956 -877 6068 6075 -878 6074 6076 -879 5712 5830 -880 5974 5975 -881 5833 5836 -882 6192 6307 -883 5955 6076 -885 2055 5591 -888 5954 5957 -888 6201 6204 -891 6198 6203 -893 5961 6074 -894 5609 5728 -895 6198 6313 -897 5740 5743 -898 5830 5833 -898 5959 5961 -900 6068 6076 -901 1683 1684 -904 1570 1682 -906 1815 1816 -907 1677 1793 -913 1570 1572 -916 1571 1573 -918 1572 1573 -925 1467 1583 -928 1459 1571 -931 1582 1585 -937 1459 1461 -938 1810 1815 -942 1580 1582 -943 1566 1683 -944 1465 1582 -945 1560 1670 -949 1821 1932 -953 1461 1462 -958 1821 1939 -962 1577 1585 -965 1676 1683 -967 1566 1682 -968 1460 1465 -978 1460 1462 -979 1813 1815 -984 1348 1460 -986 1350 1468 -989 1565 1572 -994 1455 1571 -902 2165 2171 -904 2284 2285 -905 2277 2284 -907 2279 2282 -908 6191 6306 -910 2164 2165 -910 2285 2399 -912 1933 2048 -912 2400 2513 -914 2282 2398 -915 2163 2168 -916 2162 2164 -918 2033 2036 -920 2274 2276 -921 2401 2513 -923 2166 2171 -923 2164 2274 -925 1915 1916 -925 2274 2279 -926 2289 2290 -928 2056 2166 -929 1916 2031 -929 5712 5713 -929 2156 2158 -929 2158 2276 -931 2151 2156 -931 2279 2281 -932 2151 2158 -933 2030 2036 -935 2285 2398 -936 2162 2168 -936 2168 2170 -938 2165 2168 -940 2513 2518 -941 2399 2404 -949 1821 1932 -949 2274 2275 -949 2518 2520 -954 2056 2173 -957 2166 2168 -958 1821 1939 -958 5711 5712 -958 2274 2282 -959 2520 6191 -960 2399 2405 -961 2281 2398 -964 1932 1939 -964 1933 1939 -965 2170 2280 -967 2151 2153 -969 2168 2171 -969 2171 2173 -969 2512 2513 -970 2396 2398 -970 2521 2631 -971 1933 2049 -974 5712 5832 -974 2151 2152 -974 5832 5833 -977 2158 2268 -977 6192 6306 -985 2519 2521 -987 2289 5832 -990 2035 2153 -991 2281 2390 -995 5829 5833 -996 5712 5831 -997 2512 2514 -999 2172 2173 -904 5123 5242 -904 5252 5253 -904 5250 5252 -905 5245 5248 -906 5376 5379 -911 5245 5253 -912 5501 5502 -913 5245 5252 -913 5371 5372 -916 5121 5124 -917 5113 5121 -919 5118 5124 -920 5252 5256 -921 5130 5245 -922 5246 5250 -925 5367 5369 -926 5120 5121 -926 5245 5246 -926 5371 5374 -929 5123 5124 -935 5367 5368 -938 5363 5482 -939 5119 5124 -943 5132 5245 -944 5126 5130 -944 5256 5371 -945 5382 5499 -946 5363 5369 -948 5126 5247 -948 5502 5620 -953 5119 5239 -956 5117 5230 -956 5362 5368 -956 5499 5502 -957 5232 5350 -960 4995 5107 -961 5502 5619 -963 5001 5113 -963 5119 5120 -965 5118 5233 -965 5378 5381 -968 5371 5379 -971 5126 5127 -972 5116 5123 -974 5378 5379 -992 5125 5126 -902 5737 5860 -904 5728 5729 -905 5726 5734 -906 5971 6094 -907 5723 5730 -908 5732 5852 -911 5831 5833 -912 5728 5734 -912 5852 5855 -915 5829 5830 -916 5974 5977 -921 5742 5855 -923 5833 5838 -925 5833 5956 -926 5953 5956 -926 6198 6318 -927 5620 5625 -927 5723 5843 -927 5953 5955 -929 5712 5713 -929 5857 5980 -930 5728 5730 -930 5974 5980 -930 6088 6094 -932 6085 6207 -933 5733 5734 -933 5961 6082 -934 6088 6207 -934 6085 6091 -935 5953 6076 -936 5965 6088 -937 5728 5731 -939 5833 5951 -939 6073 6075 -939 6198 6312 -940 5956 6076 -944 6088 6091 -946 5726 5731 -946 5733 5737 -948 5502 5620 -949 5621 5622 -949 5831 5838 -950 5857 5860 -951 5724 5843 -951 6192 6312 -952 5727 5731 -952 5959 5962 -954 5852 5860 -955 6080 6081 -957 5718 5838 -957 5730 5736 -958 5711 5712 -959 5729 5730 -961 5502 5619 -964 5836 5838 -966 5739 5852 -966 5951 5953 -967 5855 5860 -967 6192 6309 -968 5620 5622 -970 5731 5846 -971 5958 6076 -974 5739 5860 -974 5832 5833 -974 5953 5958 -975 5724 5844 -975 5837 5838 -977 5725 5730 -977 6192 6306 -979 5737 5852 -980 5731 5733 -985 5730 5849 -986 5725 5843 -987 6199 6207 -987 2289 5832 -988 5837 5844 -989 5619 5625 -993 5843 5849 -993 6193 6194 -995 5730 5731 -995 5837 5839 -995 5829 5833 -996 5712 5831 -999 5619 5622 -1000 6199 6204 -1004 1460 1461 -1006 1465 1577 -1020 1580 1581 -1026 1574 1581 -1027 1454 1461 -1028 1344 1460 -1033 1579 1580 -1043 1574 1579 -1047 1461 1465 -1047 1465 1579 -1049 1350 1460 -1056 1350 1466 -1057 1681 1684 -1062 1799 1807 -1085 1343 1460 -1086 1574 1576 -1093 1466 4991 -1002 2401 2404 -1004 2170 2288 -1005 2507 2514 -1006 2404 2406 -1007 2172 5832 -1008 2173 2174 -1010 2033 2035 -1010 2405 2406 -1014 5950 5951 -1019 2521 6189 -1021 2521 6191 -1024 2035 2145 -1024 2510 2517 -1028 2396 2401 -1032 2028 2145 -1032 2515 2518 -1034 2162 2282 -1034 2512 2515 -1034 2518 2521 -1039 5829 5950 -1039 2510 2515 -1042 2028 2033 -1047 2167 2168 -1048 2401 2403 -1051 2507 2509 -1057 2509 2512 -1058 2028 2029 -1059 1916 2030 -1060 2401 2407 -1061 2396 2397 -1061 2506 2507 -1062 2174 2289 -1062 2406 2407 -1071 2504 2507 -1073 2517 2518 -1074 5832 5950 -1077 2507 2512 -1080 2274 2277 -1081 2028 2030 -1085 2511 2512 -1086 2403 2510 -1088 2394 2506 -1091 2395 2507 -1092 2162 2277 -1092 2516 2517 -1092 6190 6191 -1093 2166 2173 -1100 2516 2518 -1003 5003 5113 -1006 5252 5365 -1008 5118 5120 -1010 5119 5126 -1014 5251 5252 -1017 5378 5382 -1025 5258 5371 -1037 5126 5245 -1040 5377 5378 -1046 5362 5369 -1047 5498 5502 -1049 5118 5238 -1064 5235 5237 -1073 5382 5497 -1075 5118 5241 -1080 4997 5001 -1088 5497 5502 -1091 4995 4997 -1094 5497 5505 -1095 5236 5237 -1001 5730 5850 -1001 5848 5849 -1004 5730 5848 -1005 6087 6207 -1006 5729 5731 -1007 5739 5858 -1007 5835 5838 -1007 5972 5975 -1008 5622 5740 -1009 5731 5848 -1009 5739 5743 -1010 5727 5730 -1010 5731 5849 -1013 5733 5854 -1014 5950 5951 -1017 5725 5848 -1017 5846 5849 -1019 5731 5736 -1019 5848 5854 -1019 5973 6094 -1019 2521 6189 -1020 5842 5843 -1020 5973 5977 -1020 6087 6091 -1021 5731 5851 -1021 6086 6091 -1023 6086 6094 -1024 5852 5858 -1025 5725 5845 -1026 5849 5854 -1026 5849 5850 -1028 5852 5859 -1029 5736 5854 -1031 5731 5854 -1031 5842 5844 -1034 5849 5851 -1037 5730 5843 -1038 6200 6204 -1039 6198 6310 -1048 5836 5839 -1049 5731 5734 -1049 5842 5845 -1049 5733 5852 -1049 5972 5977 -1053 6068 6073 -1056 5736 5737 -1059 5736 5849 -1059 5737 5854 -1060 5839 5842 -1064 5610 5731 -1067 5727 5848 -1068 5729 5849 -1069 5857 5859 -1071 5841 5842 -1072 5972 5980 -1073 5834 5839 -1074 6200 6318 -1076 5859 5972 -1077 6199 6205 -1078 6194 6312 -1079 5738 5743 -1083 5835 5839 -1084 5972 5978 -1086 6075 6195 -1089 5972 5979 -1089 6193 6310 -1092 5618 5622 -1092 5857 5972 -1092 6190 6191 -1115 1349 1466 -1131 4991 4995 -1133 1574 1575 -1143 1354 4991 -1150 1350 4991 -1154 4990 4991 -1157 4991 4992 -1158 1908 1913 -1167 1905 1906 -1171 1349 4991 -1179 1349 4874 -1180 1905 1910 -1184 4874 4991 -1184 5107 5109 -1186 1349 4992 -1194 1905 1907 -1194 1905 1908 -1197 1574 1580 -1199 4991 5109 -1102 2277 2282 -1105 2167 2282 -1107 1912 2030 -1107 2174 2288 -1107 2400 2507 -1108 5951 5952 -1108 2394 2507 -1110 2510 2511 -1115 2403 2518 -1119 1913 2030 -1120 2174 5950 -1120 2405 2519 -1121 2516 2521 -1122 2041 2159 -1126 5829 5948 -1129 1913 1915 -1133 2407 2518 -1137 1912 2022 -1137 2151 2159 -1138 2166 2283 -1138 2521 6188 -1146 6069 6073 -1152 1910 1912 -1158 1908 1913 -1165 6188 6306 -1167 1905 1906 -1168 1910 1913 -1180 1905 1910 -1181 2407 2519 -1181 6069 6187 -1186 2045 2159 -1187 2174 2286 -1188 2504 2511 -1192 2628 6188 -1194 1905 1907 -1198 2519 6072 -1200 1899 1906 -1200 2039 2159 -1113 5230 5235 -1114 5502 5617 -1117 4996 4997 -1130 5236 5238 -1131 4991 4995 -1136 5249 5368 -1138 5247 5250 -1154 4990 4991 -1155 5384 5497 -1156 5617 5622 -1166 4997 4998 -1171 5250 5367 -1184 5107 5109 -1184 5236 5243 -1185 5617 5625 -1199 5118 5236 -1103 6192 6304 -1108 5951 5952 -1109 6194 6310 -1114 6070 6071 -1114 6070 6076 -1115 5834 5841 -1115 6087 6205 -1119 5972 6092 -1122 5840 5842 -1124 5972 5974 -1126 5829 5948 -1126 5973 6092 -1128 5745 5858 -1130 5725 5840 -1135 5622 5738 -1145 5855 5857 -1146 6069 6073 -1153 5739 5745 -1153 5853 5972 -1156 5617 5622 -1157 6200 6310 -1159 5973 6093 -1160 6086 6093 -1165 5855 5856 -1167 5727 5846 -1174 5738 5745 -1181 6069 6187 -1185 5617 5625 -1186 6070 6073 -1188 5738 5746 -1198 2519 6072 -1200 5721 5840 -1200 5835 5954 -1205 1349 1354 -1214 1809 1920 -1217 1240 1349 -1229 1792 1907 -1229 1792 1908 -1235 1686 1796 -1236 1804 1809 -1244 5109 5112 -1245 1788 1907 -1246 5109 5110 -1246 1809 1925 -1251 1809 1926 -1253 1788 1899 -1254 1350 1354 -1254 1925 1926 -1256 1791 1792 -1278 1348 1350 -1280 1680 1797 -1280 1790 1792 -1288 1680 1796 -1290 1788 1791 -1300 1349 1350 -1205 6188 6304 -1208 2174 5832 -1211 2519 6189 -1214 1809 1920 -1215 2041 2045 -1229 1792 1907 -1243 1926 1927 -1245 1788 1907 -1251 1809 1926 -1253 1788 1899 -1254 1925 1926 -1258 5832 5838 -1259 2042 2045 -1262 2167 2283 -1263 2039 2044 -1267 2040 2041 -1274 1927 1928 -1278 2282 2283 -1282 2166 2167 -1215 5617 5624 -1228 5504 5617 -1231 5236 5241 -1243 5250 5365 -1244 5109 5112 -1246 5109 5110 -1268 5497 5504 -1205 6188 6304 -1211 2519 6189 -1212 6194 6304 -1214 5624 5738 -1215 5617 5624 -1224 6189 6191 -1228 6194 6311 -1258 5832 5838 -1259 6191 6192 -1261 5958 5959 -1269 5726 5728 -1276 6199 6206 -1301 1681 1796 -1305 1790 1795 -1315 1680 1795 -1319 1343 1350 -1323 1681 1795 -1329 1782 1899 -1333 1350 1351 -1354 1789 1795 -1354 1789 1792 -1354 5110 5111 -1355 1675 1795 -1359 1786 1788 -1367 1675 1792 -1372 1787 1790 -1378 1786 1899 -1380 1786 1907 -1390 1782 1893 -1393 1788 1789 -1306 1924 1928 -1309 2519 6191 -1310 1928 2043 -1312 5832 5951 -1313 2165 2167 -1320 2162 2167 -1321 1928 2042 -1329 1782 1899 -1378 1786 1899 -1380 1786 1907 -1316 5111 5112 -1354 5110 5111 -1382 5501 5619 -1384 5501 5620 -1309 2519 6191 -1312 5832 5951 -1317 6200 6316 -1338 5733 5846 -1364 5606 5726 -1368 5619 5620 -1372 5620 5621 -1378 5614 5620 -1382 5501 5619 -1384 5501 5620 -1396 5619 5621 -1403 1782 1901 -1405 1677 1681 -1410 1804 1920 -1415 1787 1789 -1427 1677 1678 -1445 1675 1677 -1447 1787 1788 -1450 5111 5230 -1451 1675 1787 -1472 1671 1787 -1477 1670 1677 -1495 1462 1463 -1499 1469 5110 -1410 1804 1920 -1416 2279 2284 -1425 2044 2159 -1445 2284 2393 -1458 2516 6187 -1472 2031 2038 -1484 2284 2398 -1486 2032 2038 -1488 2042 2043 -1495 1927 2043 -1404 5368 5370 -1430 5006 5116 -1446 5116 5121 -1450 5111 5230 -1479 4999 5116 -1489 5129 5248 -1499 1469 5110 -1416 5621 5740 -1420 5614 5621 -1435 5616 5621 -1439 5733 5853 -1458 2516 6187 -1516 1347 1463 -1520 1781 1788 -1557 1338 1448 -1558 1348 1463 -1580 1560 1678 -1593 1454 1455 -1594 1782 1788 -1598 1564 1678 -1504 2398 2399 -1510 2038 2148 -1512 2033 2038 -1512 6069 6189 -1516 2393 2399 -1521 2284 2399 -1543 2033 2153 -1556 2037 2044 -1567 2038 2154 -1568 2038 2153 -1598 2042 2044 -1598 2504 2509 -1508 5000 5116 -1516 5129 5130 -1530 5127 5130 -1538 5001 5116 -1550 4880 4996 -1567 5128 5129 -1568 5128 5135 -1574 5128 5130 -1589 5009 5125 -1512 6069 6189 -1574 6072 6073 -1606 1459 1462 -1610 1353 1463 -1612 1558 1560 -1618 1553 1670 -1626 1679 1684 -1630 1448 1456 -1631 1684 1686 -1635 1553 1554 -1637 1455 1456 -1642 1553 1558 -1644 1554 1664 -1645 1558 1561 -1654 1557 1673 -1661 1439 1446 -1661 1440 1446 -1664 1440 1556 -1666 1455 1459 -1669 1547 1548 -1671 1553 1555 -1675 1556 1557 -1676 1680 1790 -1678 1673 1674 -1680 1554 1555 -1684 1437 1547 -1685 1564 1679 -1690 1331 1332 -1690 1555 1558 -1690 1679 1685 -1694 1453 1456 -1699 1563 1564 -1620 2275 2390 -1630 2390 2391 -1630 2518 2519 -1630 2506 2509 -1631 2275 2279 -1632 2511 2616 -1637 2390 2392 -1637 2399 2400 -1638 2275 2391 -1640 2397 2504 -1647 2262 2269 -1648 2384 2391 -1649 2505 2509 -1662 2276 2279 -1662 2504 2512 -1664 2029 2030 -1675 2279 2392 -1677 2395 2506 -1677 2505 2616 -1678 2406 2519 -1681 2392 2395 -1683 2263 2378 -1686 2278 2279 -1693 2391 2395 -1698 2506 2508 -1699 2504 2616 -1700 2510 2518 -1603 5121 5123 -1604 5125 5127 -1620 5013 5125 -1628 5009 5013 -1633 5251 5258 -1650 5008 5009 -1655 5010 5013 -1660 5127 5128 -1660 5256 5374 -1669 5122 5128 -1670 5256 5379 -1672 5013 5127 -1673 5008 5010 -1676 5255 5374 -1678 5256 5261 -1679 5012 5128 -1680 4892 5008 -1682 5138 5257 -1682 5255 5261 -1684 5258 5379 -1691 5377 5382 -1693 5254 5255 -1694 5012 5127 -1697 5258 5377 -1700 5254 5256 -1607 6071 6072 -1702 1561 1563 -1704 1569 1679 -1709 1554 1672 -1710 1336 1338 -1715 1437 1555 -1717 1563 1569 -1719 1672 1673 -1720 1556 1561 -1720 1557 1558 -1722 1440 1441 -1723 1441 1555 -1723 1547 1554 -1723 1552 1664 -1724 1329 1445 -1726 1439 1441 -1734 1563 1679 -1741 1439 1444 -1742 1674 1675 -1744 1675 1790 -1745 1441 1556 -1747 1681 1686 -1748 1435 1441 -1749 1435 1437 -1751 1569 1686 -1755 1217 1218 -1756 1329 1444 -1763 1679 1686 -1766 1329 1335 -1770 1438 1441 -1771 1224 1331 -1773 1562 1563 -1776 1670 1671 -1781 1453 1459 -1783 1331 1338 -1791 1437 1438 -1792 1430 1547 -1793 1555 1556 -1794 1436 1438 -1800 1459 1573 -1800 1563 1678 -1701 2394 2395 -1701 2385 2498 -1701 2492 2499 -1703 2030 2033 -1703 2610 2616 -1705 2278 2394 -1710 2027 2029 -1711 2023 2139 -1711 2512 2518 -1714 2506 2618 -1714 2505 2618 -1715 2391 2504 -1715 2407 6189 -1719 2389 2391 -1719 2501 2508 -1722 2250 2257 -1730 2610 2617 -1736 2385 2500 -1737 2504 2506 -1739 2503 2508 -1740 2399 2406 -1742 2389 2506 -1743 2389 2501 -1744 2401 2518 -1745 2401 2406 -1746 2031 2033 -1748 2610 2618 -1752 2503 2618 -1753 2389 2498 -1756 2400 2406 -1757 2391 2392 -1757 2493 2604 -1758 2612 2618 -1759 2389 2500 -1761 2386 2500 -1762 2492 2500 -1762 2504 2505 -1762 2503 2613 -1763 2610 2615 -1764 2615 2618 -1765 2140 2256 -1766 2388 2389 -1767 2611 2721 -1771 2503 2615 -1772 2383 2500 -1772 2502 2508 -1779 2399 2401 -1781 2032 2033 -1784 2492 2497 -1786 2611 2615 -1787 2027 2147 -1787 2387 2388 -1787 2383 2389 -1789 2493 2497 -1790 2502 2613 -1791 2612 2615 -1792 2027 2139 -1792 2386 2388 -1793 2392 2393 -1797 2612 2613 -1800 2391 2506 -1703 5258 5262 -1704 5251 5259 -1710 4896 5008 -1714 5138 5259 -1714 5253 5254 -1715 5377 5384 -1718 5256 5259 -1719 5012 5129 -1720 4891 4898 -1722 5258 5259 -1727 5258 5264 -1730 5010 5011 -1731 5377 5385 -1732 5012 5013 -1733 4896 4898 -1736 5264 5385 -1738 5264 5383 -1739 5011 5012 -1743 5136 5259 -1744 5257 5264 -1747 4896 5016 -1752 5257 5258 -1757 5136 5253 -1758 5136 5254 -1761 5263 5270 -1762 5135 5248 -1765 5135 5253 -1769 5135 5254 -1774 5138 5142 -1774 5251 5256 -1776 5262 5385 -1777 5136 5141 -1780 5000 5001 -1788 5137 5257 -1790 5136 5142 -1795 4898 4899 -1715 2407 6189 -1736 6072 6189 -1756 5957 5958 -1761 6072 6190 -1801 1336 1456 -1811 1430 1431 -1814 1670 1672 -1816 1430 1437 -1816 1554 1558 -1818 1458 1459 -1822 1670 1675 -1823 1430 1435 -1829 1110 1217 -1829 1324 1444 -1829 1457 1458 -1834 1672 1675 -1842 1558 1672 -1846 1456 1459 -1849 1552 1554 -1851 1324 1436 -1854 1552 1558 -1861 1435 1438 -1863 1324 1326 -1866 1675 1789 -1874 1454 1459 -1877 880 887 -1877 1320 1436 -1877 1446 1563 -1878 1671 1675 -1881 1457 1459 -1887 1554 1670 -1888 862 869 -1888 874 881 -1888 1329 1330 -1892 1671 1789 -1893 1304 1311 -1894 1314 1424 -1894 1671 1672 -1897 868 875 -1899 1671 1674 -1802 2721 2723 -1807 2721 2722 -1808 2715 2722 -1817 2383 2386 -1818 2615 2723 -1819 2492 2495 -1821 2032 2147 -1822 2133 2140 -1822 2383 2385 -1825 2406 2518 -1826 2383 2388 -1826 2385 2492 -1827 2381 2386 -1829 2728 2829 -1837 2032 2148 -1837 2272 2381 -1841 2378 2383 -1842 2614 2615 -1844 2721 2726 -1851 2267 2383 -1852 2267 2381 -1853 2244 2251 -1858 2391 2498 -1859 2278 2393 -1862 2726 2728 -1871 2378 2379 -1871 2395 2504 -1875 2134 2250 -1884 2835 2836 -1885 2378 2380 -1886 2267 2380 -1891 2726 2729 -1894 2263 2380 -1898 2612 2614 -1900 2266 2267 -1803 4896 5010 -1806 5130 5135 -1809 5256 5258 -1835 5137 5144 -1836 5269 5270 -1837 5134 5141 -1842 5134 5136 -1844 5137 5142 -1845 5139 5141 -1847 5139 5142 -1848 5027 5144 -1851 5130 5133 -1852 4777 4784 -1853 5250 5373 -1860 4896 5011 -1860 5027 5143 -1863 5137 5140 -1864 5139 5140 -1866 5137 5145 -1877 5027 5145 -1880 5135 5136 -1883 5377 5379 -1886 5026 5033 -1888 4784 4891 -1895 5025 5145 -1898 4891 4899 -1901 1327 1444 -1902 1197 1310 -1902 1670 1678 -1904 1446 1556 -1906 1675 1678 -1908 780 886 -1910 1319 1320 -1913 1673 1675 -1916 1331 1339 -1917 863 971 -1919 683 785 -1920 868 869 -1921 1319 1324 -1921 1678 1680 -1921 1669 1671 -1925 1675 1680 -1928 880 881 -1928 1324 1327 -1935 1671 1781 -1936 1082 1089 -1938 1664 1671 -1941 779 786 -1941 1196 1203 -1942 867 869 -1942 868 870 -1944 1327 1329 -1946 880 882 -1946 1558 1678 -1951 965 972 -1951 1305 1421 -1951 1680 1681 -1955 879 881 -1956 768 874 -1957 864 869 -1957 1321 1327 -1961 1083 1196 -1964 689 785 -1965 1322 1327 -1966 874 876 -1966 1212 1319 -1966 1319 1321 -1969 874 875 -1971 880 885 -1974 780 885 -1975 873 875 -1978 864 971 -1979 689 791 -1983 978 1088 -1983 1205 1206 -1983 1210 1324 -1985 763 870 -1985 1328 1329 -1985 1673 1678 -1986 1415 1422 -1989 1103 1104 -1989 1679 1680 -1995 1210 1212 -1997 780 781 -1997 1304 1309 -1998 970 971 -1999 779 781 -2000 873 876 -1919 2265 2266 -1923 2516 6189 -1927 2931 2932 -1930 2611 2614 -1931 2808 2809 -1932 2263 2372 -1934 2264 2266 -1934 2815 2913 -1937 2261 2263 -1937 2829 2837 -1939 2251 2366 -1944 2259 2265 -1945 2472 2577 -1949 2465 2466 -1949 2502 2607 -1952 2261 2264 -1952 2925 2926 -1954 2352 2459 -1956 2583 2584 -1957 2821 2919 -1958 2257 2372 -1962 2345 2346 -1962 2695 2701 -1963 2257 2373 -1964 2229 2230 -1965 2701 2802 -1965 2829 2836 -1969 2257 2261 -1970 2810 2913 -1972 2256 2261 -1973 2366 2372 -1975 1995 1996 -1975 2144 2261 -1976 2919 2920 -1977 2694 2695 -1977 2726 2837 -1979 2809 2810 -1979 3034 3125 -1982 2257 2374 -1985 2113 2229 -1987 2924 2926 -1989 2257 2366 -1991 2149 2265 -1992 1879 1995 -1992 2230 2345 -1992 2467 2472 -1992 2807 2809 -1993 2913 2918 -1993 3131 3132 -1994 2112 2113 -1994 2577 2582 -1999 2465 2467 -1904 4782 4784 -1907 5031 5143 -1914 5025 5140 -1917 4896 4899 -1917 4896 4901 -1920 5024 5134 -1922 5027 5031 -1923 4784 4785 -1927 4782 4899 -1929 4465 4564 -1931 5024 5139 -1933 4805 4912 -1944 5033 5143 -1948 4452 4453 -1948 5031 5145 -1950 4916 5032 -1951 5024 5141 -1953 4459 4558 -1953 5019 5024 -1957 5033 5034 -1961 5025 5030 -1962 4895 5011 -1964 5024 5140 -1965 4704 4705 -1971 4471 4570 -1974 4894 4900 -1981 4440 4441 -1982 4894 4901 -1989 4680 4686 -1995 4894 4899 -1995 5032 5033 -1998 5024 5030 -2000 4337 4434 -1913 5956 5959 -1923 2516 6189 -1926 5957 5959 -1995 6080 6082 -1999 5965 6082 -2004 684 785 -2004 867 870 -2005 1197 1309 -2011 695 791 -2011 4330 4331 -2012 1643 1650 -2013 1199 1205 -2014 1210 1321 -2015 1532 1539 -2016 1650 1760 -2017 1197 1198 -2017 1206 1319 -2018 970 972 -2018 1533 1649 -2018 1878 1879 -2019 879 882 -2022 689 790 -2022 966 1076 -2023 688 695 -2024 694 4331 -2024 683 784 -2024 876 879 -2026 775 885 -2026 867 976 -2029 1760 1767 -2031 1091 1092 -2034 1210 1327 -2035 870 873 -2035 1761 1878 -2037 690 791 -2037 1206 1210 -2037 1415 1420 -2041 779 784 -2041 1210 1216 -2042 864 973 -2043 778 885 -2043 1070 1077 -2043 1210 1322 -2046 694 695 -2046 1304 1306 -2047 967 972 -2048 865 870 -2050 1215 1322 -2051 878 879 -2051 1760 1765 -2052 970 973 -2052 1305 1306 -2052 1645 1650 -2053 1235 1236 -2054 864 976 -2055 694 696 -2055 1422 1532 -2055 1533 1534 -2059 1532 1537 -2059 1877 1878 -2060 778 781 -2060 1209 1210 -2060 1305 1420 -2062 693 695 -2062 1122 1235 -2063 1196 1197 -2064 1760 1878 -2065 678 784 -2066 1235 1237 -2066 1417 1422 -2067 684 787 -2067 967 1076 -2068 1532 1649 -2069 689 695 -2071 1236 1240 -2071 1532 1534 -2072 684 790 -2072 1417 1537 -2073 1098 1205 -2075 877 879 -2075 1192 1198 -2076 684 784 -2080 4329 4331 -2081 1235 1240 -2082 690 790 -2084 866 870 -2084 1648 1649 -2085 1529 1532 -2085 1648 1650 -2085 1761 1762 -2085 1762 1767 -2086 680 784 -2088 684 782 -2088 690 695 -2089 1531 1534 -2090 1192 1309 -2096 1417 1420 -2097 690 796 -2099 1300 1420 -2099 1534 1648 -2002 2464 2465 -2002 2611 2723 -2004 2259 2264 -2004 2802 2807 -2005 2694 2696 -2006 2695 2696 -2007 2467 2470 -2009 2918 2920 -2010 2693 2694 -2011 2258 2261 -2011 2347 2352 -2011 2696 2802 -2012 2344 2345 -2013 2230 2231 -2013 2352 2464 -2014 2696 2701 -2014 2926 2927 -2015 2584 2688 -2016 3138 3225 -2017 1996 2112 -2017 2113 2114 -2017 2228 2229 -2018 1878 1879 -2018 2582 2584 -2019 2255 2366 -2020 2584 2694 -2020 2584 2585 -2022 2353 2464 -2027 2255 2257 -2028 2810 2912 -2029 1996 1997 -2030 2607 2612 -2031 2579 2582 -2032 2919 2926 -2036 1994 1995 -2036 2345 2347 -2038 2579 2585 -2039 2256 2259 -2039 2467 2582 -2041 2347 2353 -2042 2111 2112 -2046 2347 2350 -2046 2693 2695 -2050 2516 6188 -2051 2231 2344 -2052 2149 2259 -2054 2920 2921 -2055 2585 2693 -2057 1879 1880 -2059 1877 1878 -2061 2696 2804 -2062 2804 2807 -2064 2467 2469 -2065 2137 2247 -2065 2143 2149 -2065 2144 2259 -2066 1997 2111 -2068 2228 2231 -2069 2142 2149 -2070 2609 2612 -2070 2696 2801 -2073 2114 2228 -2074 2462 2467 -2079 2921 3021 -2081 1994 1997 -2082 2111 2114 -2083 1880 1994 -2085 2258 2260 -2086 1877 1880 -2092 2462 2464 -2094 2574 2582 -2096 2609 2611 -2098 2258 2259 -2001 4787 4894 -2005 5023 5025 -2011 4330 4331 -2014 4577 4680 -2015 4464 4465 -2019 5254 5374 -2020 5031 5033 -2023 5031 5151 -2025 4780 4781 -2029 4349 4446 -2034 5023 5024 -2035 4782 4787 -2039 5254 5261 -2040 4783 4785 -2041 4440 4447 -2041 5025 5028 -2043 4672 4783 -2051 4361 4458 -2055 4785 4788 -2060 4686 4687 -2061 4459 4564 -2063 5027 5028 -2066 4687 4693 -2066 5022 5023 -2073 5020 5028 -2089 4459 4563 -2091 5261 5374 -2092 5034 5037 -2094 4676 4783 -2094 5254 5259 -2096 4915 4922 -2099 4907 5017 -2100 4440 4442 -2012 5962 5964 -2042 5964 5965 -2049 5963 5964 -2050 2516 6188 -2102 1760 1762 -2102 1762 1877 -2104 1121 1122 -2104 1207 1210 -2105 693 696 -2105 1075 1076 -2105 1126 1235 -2106 1208 1215 -2107 1240 4874 -2108 1205 1207 -2109 967 1081 -2109 1645 1765 -2112 775 877 -2112 1648 1651 -2114 1195 1196 -2115 1300 1306 -2117 1195 1198 -2118 1759 1762 -2119 1192 1306 -2120 872 873 -2120 968 971 -2120 1096 1207 -2121 1417 1419 -2123 696 4329 -2125 866 976 -2125 1092 1207 -2125 1195 1201 -2125 1645 1651 -2128 690 788 -2129 1092 1199 -2129 1205 1208 -2129 1208 1209 -2130 1092 1205 -2130 1303 1306 -2131 1208 1210 -2132 686 790 -2133 686 782 -2133 871 876 -2133 1207 1208 -2134 1092 1096 -2144 1085 1199 -2147 968 973 -2149 1096 1205 -2152 1101 1214 -2152 1417 1529 -2153 1092 1093 -2154 1240 4875 -2155 1085 1086 -2156 4329 4332 -2159 1646 1648 -2162 975 1086 -2162 1090 1092 -2163 1208 1213 -2164 968 976 -2164 1095 1208 -2166 690 692 -2168 776 784 -2168 1085 1090 -2169 1095 1096 -2170 1530 1534 -2171 975 1079 -2171 981 1085 -2172 1086 1087 -2172 1529 1531 -2174 1122 1126 -2175 1205 1210 -2181 1090 1093 -2181 1100 1107 -2181 1647 1648 -2189 776 781 -2190 1354 4992 -2194 1758 1762 -2195 1010 4648 -2195 1645 1647 -2199 1085 1087 -2102 1762 1877 -2105 2227 2231 -2107 2142 2147 -2108 2250 2255 -2108 2360 2367 -2108 2693 2696 -2111 2142 2259 -2113 2231 2342 -2113 2366 2368 -2114 2349 2464 -2116 2915 2918 -2118 2692 2696 -2120 2226 2231 -2120 2347 2349 -2121 2110 2114 -2123 2342 2350 -2128 2255 2258 -2128 2367 2368 -2131 2226 2227 -2132 2349 2456 -2132 2921 3020 -2133 2227 2342 -2133 2342 2347 -2134 1875 1877 -2134 2342 2349 -2134 2806 2807 -2135 2144 2149 -2135 2806 2810 -2136 2349 2350 -2136 2604 2609 -2137 2691 2693 -2138 2932 3027 -2139 2579 2581 -2142 2349 2353 -2143 1992 1994 -2143 2497 2607 -2145 2114 2226 -2146 1993 1997 -2146 2144 2264 -2148 2581 2693 -2152 2927 3027 -2154 2226 2234 -2157 2462 2463 -2159 2361 2474 -2159 2927 3026 -2160 2696 2799 -2161 2144 2146 -2161 2799 2807 -2162 2109 2114 -2162 2144 2147 -2162 3027 3032 -2165 2109 2226 -2165 2143 2144 -2166 2611 2715 -2169 2116 2226 -2169 2349 2462 -2169 2709 2716 -2170 2348 2349 -2172 2469 2574 -2174 2144 2256 -2175 2253 2258 -2175 2932 3034 -2176 1876 1880 -2178 2226 2233 -2179 3026 3027 -2180 2921 2926 -2182 2256 2258 -2182 3027 3034 -2183 2574 2581 -2185 2365 2367 -2185 2926 3027 -2187 2109 2110 -2189 2139 2144 -2189 2250 2251 -2190 3027 3029 -2192 2139 2142 -2192 2146 2256 -2195 2109 2111 -2195 2605 2715 -2197 2257 2258 -2104 4452 4454 -2106 4669 4774 -2107 4330 4332 -2108 4343 4446 -2108 4907 5022 -2110 5026 5027 -2117 4808 4921 -2118 5133 5136 -2123 5132 5133 -2127 4902 4907 -2128 4785 4787 -2131 4908 5022 -2140 4344 4446 -2140 4780 4787 -2141 4678 4789 -2147 4908 5020 -2149 5374 5379 -2150 4662 4663 -2154 4337 4439 -2154 4900 4901 -2156 4329 4332 -2156 4451 4453 -2156 4678 4684 -2157 5019 5136 -2162 4439 4440 -2163 4332 4337 -2165 4904 4908 -2168 4338 4439 -2169 4335 4439 -2169 4446 4451 -2169 4910 5026 -2170 4337 4440 -2173 4905 5022 -2174 4332 4335 -2175 4454 4459 -2176 4337 4338 -2178 4786 4787 -2182 4900 4902 -2182 4910 5028 -2185 4780 4782 -2190 4902 4905 -2191 4335 4337 -2192 4343 4445 -2193 4437 4440 -2194 4440 4445 -2198 5136 5139 -2109 5965 6083 -2110 5963 6083 -2129 5970 6083 -2140 5979 6092 -2143 5751 5864 -2147 6205 6206 -2150 5741 5742 -2159 6098 6099 -2160 5873 5880 -2169 5744 5745 -2174 5977 6092 -2174 6120 6238 -2177 6202 6203 -2180 6098 6217 -2187 6202 6209 -2188 5870 5871 -2191 5622 5743 -2192 6089 6090 -2192 6092 6100 -2192 6452 6459 -2195 5977 6097 -2198 5745 5746 -2198 6099 6100 -2198 6090 6202 -2200 5743 5746 -2200 6105 6217 -2201 1085 1092 -2201 1240 4992 -2202 1412 1420 -2203 1080 1187 -2208 777 885 -2209 975 1087 -2210 1083 1084 -2211 968 969 -2212 691 696 -2223 1456 1458 -2225 1757 1765 -2226 777 781 -2234 1302 1420 -2239 1084 1195 -2240 777 883 -2252 777 877 -2256 1302 1306 -2256 1757 1762 -2257 1530 1646 -2259 1082 1087 -2260 969 973 -2261 865 872 -2262 686 788 -2264 979 1087 -2264 1529 1530 -2270 969 970 -2271 1758 1875 -2272 692 693 -2273 1640 1646 -2280 1082 1084 -2289 978 1082 -2294 776 783 -2295 871 872 -2202 2605 2609 -2203 2810 2910 -2207 2226 2228 -2208 2257 2263 -2212 2256 2264 -2215 2606 2609 -2219 2143 2258 -2220 2365 2474 -2224 1993 2109 -2225 2910 2918 -2225 3027 3028 -2228 2142 2144 -2233 2251 2252 -2236 2252 2255 -2236 2605 2717 -2238 2031 2032 -2240 2139 2140 -2248 2917 2918 -2249 2139 2141 -2251 2917 2921 -2254 2709 2717 -2258 2025 2031 -2258 2581 2685 -2261 1915 2031 -2266 1875 1876 -2271 1758 1875 -2274 2251 2255 -2275 2805 2806 -2276 2474 2476 -2276 2691 2692 -2280 2921 3018 -2283 2368 2371 -2287 2030 2031 -2288 2921 3026 -2289 1992 1993 -2294 2468 2475 -2294 2698 2799 -2295 1876 1992 -2202 4336 4337 -2206 4338 4445 -2206 4904 4905 -2210 4678 4791 -2211 4676 4791 -2212 4678 4783 -2213 4344 4445 -2216 4899 4901 -2224 5374 5381 -2228 4897 4905 -2229 4678 4682 -2230 4343 4440 -2233 4338 4440 -2235 4344 4448 -2237 4779 4780 -2237 4788 4902 -2241 4330 4337 -2247 4899 4902 -2249 4454 4457 -2253 4336 4440 -2253 4677 4684 -2258 4338 4437 -2260 4448 4451 -2266 4327 4332 -2266 4451 4454 -2266 4669 4779 -2274 4450 4451 -2275 4903 5020 -2281 4443 4451 -2282 4785 4786 -2284 4677 4682 -2291 4910 5020 -2296 5635 5636 -2202 5977 6094 -2202 6440 6447 -2203 5976 6089 -2204 5757 5870 -2205 6446 6453 -2209 6202 6207 -2212 5975 5976 -2212 6452 6453 -2214 6097 6100 -2220 6548 6555 -2221 6099 6103 -2224 6451 6453 -2225 5741 5743 -2226 5744 5749 -2232 6316 6317 -2234 5746 5749 -2235 6103 6217 -2235 6202 6204 -2240 5744 5751 -2240 5749 5752 -2241 6452 6454 -2244 5746 5748 -2247 6232 6239 -2248 6316 6318 -2250 5747 5749 -2255 5741 5748 -2258 5751 5752 -2258 6448 6453 -2260 6344 6458 -2261 6103 6219 -2265 6217 6225 -2266 5766 5885 -2267 6219 6225 -2268 5750 5751 -2268 6005 6012 -2271 5627 5748 -2275 6343 6458 -2277 6094 6095 -2281 5747 5748 -2282 5630 5750 -2283 6095 6097 -2283 6080 6085 -2289 6223 6224 -2290 6224 6225 -2290 6343 6350 -2293 5892 6005 -2294 5885 5886 -2296 5635 5636 -2296 6119 6126 -2296 6343 6349 -2298 6653 6660 -2300 5750 5755 -2301 4762 4763 -2302 1081 1084 -2302 1640 1647 -2305 1757 1764 -2307 1647 1757 -2312 973 975 -2318 866 974 -2318 1126 1237 -2328 1193 1195 -2330 680 782 -2333 685 692 -2334 1301 1306 -2338 1301 1309 -2345 4328 4329 -2346 771 877 -2347 973 976 -2349 973 1087 -2351 1007 1014 -2352 691 692 -2352 1080 1084 -2360 1124 4651 -2363 1007 1008 -2363 1096 1213 -2364 973 978 -2365 1194 1195 -2367 1192 1194 -2374 971 972 -2380 1101 1213 -2383 1007 1009 -2386 971 973 -2391 1013 1014 -2394 1011 1121 -2398 691 4328 -2400 686 692 -2305 2812 2910 -2309 2365 2482 -2310 2495 2502 -2311 2143 2259 -2312 2255 2368 -2331 2586 2593 -2353 2254 2255 -2358 2368 2369 -2361 2513 2520 -2363 2916 2917 -2370 2253 2255 -2374 2253 2260 -2374 2917 2923 -2393 2110 2226 -2398 2513 2625 -2301 4762 4763 -2303 4903 4904 -2306 4344 4443 -2310 4340 4445 -2310 5629 5636 -2313 4332 4334 -2324 4460 4564 -2326 4334 4439 -2326 5141 5254 -2329 4683 4690 -2334 4768 4769 -2340 4899 4900 -2345 4328 4329 -2347 4568 4677 -2347 4657 4762 -2348 4897 4902 -2357 4775 4882 -2368 4460 4566 -2369 5638 5645 -2371 4460 4563 -2375 4897 4898 -2376 4679 4682 -2376 4676 4786 -2378 4779 4781 -2386 4465 4570 -2386 4664 4774 -2394 4327 4328 -2397 4465 4569 -2398 691 4328 -2399 4449 4454 -2302 5752 5755 -2303 6097 6102 -2306 5752 5754 -2306 6090 6207 -2309 5747 5754 -2310 5629 5636 -2310 6222 6225 -2310 6233 6349 -2313 6345 6458 -2315 6006 6125 -2315 6090 6208 -2316 6345 6350 -2319 6224 6228 -2323 5645 5759 -2323 5757 5878 -2324 6094 6096 -2325 6653 6654 -2328 6090 6091 -2330 6102 6219 -2341 5976 6095 -2343 6334 6335 -2348 5752 5753 -2350 5765 5766 -2352 6219 6220 -2353 6348 6350 -2355 6348 6349 -2359 5755 5758 -2362 6457 6458 -2365 5755 5757 -2365 6234 6349 -2366 6340 6341 -2367 6457 6459 -2369 5638 5645 -2370 6224 6340 -2376 6234 6239 -2379 5884 5885 -2386 6096 6097 -2391 6340 6342 -2396 6237 6239 -2397 5640 5645 -2409 1080 1193 -2426 1079 1084 -2433 1121 1123 -2434 1079 1087 -2437 4650 4651 -2451 1413 1529 -2461 1192 1301 -2469 1079 1086 -2469 1641 1647 -2476 1412 1419 -2494 1354 4994 -2496 968 975 -2422 2917 3018 -2425 2474 2475 -2426 2254 2369 -2459 2253 2369 -2467 2368 2370 -2467 2513 2514 -2473 2233 2342 -2486 2365 2370 -2404 4774 4779 -2412 4782 4785 -2413 4460 4569 -2421 4897 4904 -2425 4346 4443 -2426 4466 4570 -2427 5525 5638 -2429 4776 4779 -2434 4779 4782 -2437 4650 4651 -2437 4663 4774 -2440 4327 4431 -2443 4340 4443 -2447 5635 5642 -2448 4768 4770 -2449 4683 4684 -2452 4684 4685 -2455 5635 5640 -2459 4466 4572 -2461 4460 4561 -2464 4773 4779 -2465 4683 4688 -2471 4768 4775 -2477 4664 4773 -2478 4778 4779 -2481 4466 4569 -2485 4663 4773 -2489 4658 4664 -2491 4334 4431 -2492 4663 4664 -2493 4340 4437 -2493 4544 4653 -2494 4462 4569 -2497 4682 4685 -2500 4658 4773 -2405 6218 6328 -2406 6228 6342 -2408 6237 6238 -2408 6345 6463 -2409 6220 6222 -2423 6097 6214 -2423 6121 6238 -2424 6121 6126 -2426 6348 6463 -2438 5766 5767 -2438 6340 6345 -2441 5876 5883 -2442 5976 5977 -2444 6457 6463 -2447 5635 5642 -2455 5635 5640 -2462 6089 6094 -2469 5636 5637 -2472 6455 6456 -2477 5633 5753 -2480 6234 6354 -2485 6228 6345 -2485 6340 6347 -2487 6005 6010 -2490 6124 6126 -2520 1123 1126 -2553 1187 1194 -2561 1010 4538 -2569 1188 1194 -2579 1010 4649 -2588 1301 1303 -2589 1015 4648 -2589 1188 1301 -2591 1302 1418 -2600 978 1087 -2502 2476 2479 -2507 2475 2476 -2519 2476 2588 -2530 2473 2475 -2534 2025 2032 -2537 2365 2477 -2543 2469 2580 -2549 2030 2032 -2557 2473 2580 -2558 2476 2477 -2572 2027 2032 -2575 3018 3020 -2578 2473 2588 -2579 2607 2613 -2584 2032 2142 -2590 2580 2582 -2593 2476 2478 -2503 4567 4569 -2507 4456 4563 -2508 4334 4437 -2512 4462 4567 -2512 4661 4663 -2515 4570 4575 -2516 4771 4779 -2517 4567 4570 -2519 4663 4768 -2519 4664 4771 -2520 4657 4767 -2522 4462 4568 -2524 4561 4569 -2524 4685 4688 -2524 4777 4782 -2531 4767 4769 -2531 4777 4778 -2532 4572 4575 -2537 4660 4664 -2544 4575 4577 -2547 4658 4768 -2552 4657 4768 -2554 4567 4572 -2556 4454 4456 -2556 4658 4661 -2561 1010 4538 -2564 4685 4687 -2569 4660 4773 -2571 4676 4681 -2585 4666 4778 -2589 4666 4771 -2596 4658 4767 -2598 4686 4688 -2599 4780 4786 -2501 6005 6007 -2505 5876 5881 -2507 5882 5883 -2517 5886 5887 -2518 6124 6125 -2519 6006 6007 -2520 6348 6354 -2524 5761 5878 -2524 5876 5878 -2541 5976 6094 -2546 6348 6351 -2554 5742 5861 -2555 6237 6240 -2557 5883 5884 -2568 5742 5743 -2570 5753 5755 -2573 5767 5885 -2575 6347 6348 -2578 6004 6007 -2581 5887 5892 -2583 5881 5884 -2583 6446 6447 -2599 6001 6007 -2605 1096 1208 -2613 1015 1121 -2615 1101 1208 -2628 1015 4761 -2628 1093 1095 -2646 1908 1915 -2654 1240 4877 -2659 1301 1302 -2667 1301 1418 -2688 1295 1301 -2696 1085 1088 -2607 2254 2370 -2617 2469 2582 -2619 2613 2614 -2621 2581 2582 -2646 1908 1915 -2655 2473 2478 -2655 2615 2724 -2662 1909 1915 -2665 2473 2583 -2676 2582 2585 -2685 2364 2477 -2690 1915 2025 -2700 2363 2370 -2611 4655 4767 -2616 4572 4577 -2616 4679 4685 -2620 4449 4450 -2624 4683 4691 -2626 5522 5641 -2628 1015 4761 -2636 4576 4577 -2644 4455 4561 -2644 4570 4577 -2649 4680 4685 -2662 4462 4561 -2670 4573 4580 -2675 4652 4657 -2677 5521 5528 -2679 4658 4765 -2683 4659 4660 -2692 4686 4693 -2694 4675 4786 -2601 6124 6243 -2602 5885 5887 -2604 6124 6127 -2606 6121 6243 -2618 6457 6460 -2619 5887 6010 -2619 5889 6002 -2619 6085 6088 -2626 5522 5641 -2626 5755 5760 -2637 6345 6347 -2641 5753 5873 -2641 5760 5878 -2645 5883 5887 -2648 5753 5754 -2648 5887 6002 -2654 5767 5772 -2656 5887 5890 -2660 5887 5893 -2661 5767 5890 -2663 5765 5772 -2667 6102 6214 -2669 5887 6004 -2687 5651 5771 -2688 5761 5879 -2689 5760 5873 -2717 871 878 -2730 1352 4992 -2738 1015 4649 -2744 901 4538 -2752 906 4538 -2762 1012 4649 -2763 906 4649 -2768 1087 1088 -2769 1015 4651 -2797 1013 4649 -2797 1125 1126 -2704 2025 2030 -2705 1910 2030 -2710 1910 1915 -2745 2027 2030 -2750 1915 2030 -2751 2022 2030 -2752 2473 2582 -2762 2604 2605 -2718 4660 4661 -2720 4572 4574 -2720 4692 4693 -2722 4572 4685 -2723 4456 4555 -2727 4679 4681 -2728 4677 4685 -2736 4456 4561 -2739 4691 4693 -2744 901 4538 -2749 4567 4568 -2749 4688 4691 -2750 4674 4681 -2752 906 4538 -2753 4679 4680 -2756 4555 4561 -2784 4692 4694 -2785 4690 4691 -2791 4691 4694 -2705 6095 6096 -2712 6220 6221 -2715 5889 6010 -2719 5879 5884 -2723 6089 6096 -2723 6096 6102 -2724 5884 5886 -2725 5766 5879 -2737 6214 6220 -2740 6007 6124 -2745 5760 5879 -2752 5889 6008 -2756 6003 6007 -2763 5895 6008 -2763 6122 6124 -2765 6002 6007 -2780 6002 6010 -2799 5893 6008 -2817 901 4432 -2818 1013 1015 -2819 1013 4651 -2820 1238 4875 -2827 1124 4761 -2832 1124 1126 -2832 1126 1238 -2833 4541 4649 -2837 901 4539 -2839 1088 1090 -2847 1012 1013 -2849 906 4539 -2852 1238 4877 -2858 1352 4877 -2872 1238 4763 -2872 1352 4994 -2808 3012 3019 -2812 2471 2478 -2848 3020 3023 -2849 2472 2582 -2854 3019 3020 -2880 3017 3019 -2810 4692 4699 -2814 4677 4683 -2817 901 4432 -2823 4698 4699 -2827 1124 4761 -2832 4694 4697 -2833 4541 4649 -2835 4697 4699 -2848 4574 4677 -2865 4689 4694 -2868 4658 4660 -2873 4705 4810 -2885 4689 4690 -2898 4699 4700 -2811 5893 6010 -2813 5893 6013 -2818 6014 6015 -2842 6002 6009 -2847 5766 5886 -2868 5887 6005 -2870 6003 6122 -2881 6008 6016 -2908 794 4432 -2911 971 976 -2938 1124 4763 -2940 971 978 -2946 869 971 -2958 799 4432 -2969 869 978 -2982 869 976 -2988 794 4431 -2990 799 4539 -2901 2620 2724 -2918 2724 2729 -2934 2472 2583 -2903 4680 4682 -2904 4700 4810 -2906 4689 4696 -2908 794 4432 -2921 4571 4680 -2942 4700 4809 -2958 799 4432 -2966 4680 4687 -2980 4810 4815 -2984 4653 4660 -2988 794 4431 -2990 4572 4680 -2902 5892 6012 -2904 5904 6023 -2907 6015 6016 -2913 5892 6010 -2918 6235 6238 -2922 6123 6127 -2928 6013 6016 -2928 6304 6305 -2929 6017 6024 -2937 6122 6123 -2949 5893 6011 -2950 6002 6122 -2957 6123 6243 -2960 6021 6134 -2973 5892 6011 -2974 6235 6243 -2980 6014 6019 -2983 5891 6011 -2984 5892 5898 -2995 5892 5893 -2995 5898 6011 -2999 6455 6463 -3000 6347 6455 -3014 794 4328 -3039 794 4433 -3073 696 4328 -3090 1353 4994 -3092 906 4541 -3021 2724 2731 -3040 2692 2799 -3014 794 4328 -3021 4654 4765 -3039 794 4433 -3039 4816 4817 -3054 4817 4823 -3073 696 4328 -3075 4650 4652 -3079 4654 4658 -3080 4696 4697 -3092 906 4541 -3002 5891 5898 -3004 6235 6240 -3005 5893 6016 -3006 5891 5892 -3010 5893 5898 -3021 6011 6016 -3025 6016 6018 -3027 6016 6019 -3028 5778 5891 -3028 5885 5891 -3029 6236 6240 -3030 5890 5892 -3030 6454 6459 -3040 6236 6354 -3045 5885 5890 -3052 5896 5898 -3052 6235 6241 -3058 6346 6354 -3068 5772 5885 -3071 6235 6242 -3075 6123 6241 -3075 6346 6351 -3083 6346 6347 -3093 5896 5897 -3096 6455 6460 -3107 904 4539 -3112 799 4433 -3116 906 1013 -3158 1090 1095 -3161 866 968 -3171 1088 1095 -3191 1088 1089 -3141 2729 2731 -3107 4652 4655 -3112 799 4433 -3164 4653 4654 -3188 4654 4655 -3102 5778 5897 -3106 5895 6016 -3118 6346 6352 -3120 6454 6566 -3132 6236 6352 -3135 6089 6091 -3141 6453 6454 -3150 5896 5899 -3155 6445 6447 -3157 6454 6571 -3170 6451 6565 -3184 6448 6560 -3187 5777 5784 -3198 6019 6021 -3213 1013 4650 -3225 1095 1207 -3233 1301 1308 -3213 1013 4650 -3202 6448 6565 -3206 6456 6460 -3209 6454 6565 -3213 6447 6560 -3227 6456 6462 -3233 6456 6571 -3243 6304 6306 -3245 5895 5899 -3246 6561 6671 -3250 6554 6560 -3251 6450 6565 -3267 6559 6560 -3271 6455 6462 -3273 5895 6014 -3278 6557 6560 -3284 6560 6561 -3290 6559 6562 -3294 6557 6565 -3297 6561 6562 -3371 4649 4651 -3383 4649 4652 -3394 1095 1202 -3325 2934 2941 -3337 2935 3036 -3340 2836 2940 -3345 2513 2515 -3327 4680 4681 -3338 4695 4702 -3363 4697 4700 -3369 4695 4696 -3371 5381 5500 -3383 4649 4652 -3305 6557 6562 -3311 6564 6565 -3313 6561 6670 -3316 6450 6557 -3320 6563 6564 -3326 6557 6564 -3327 6345 6348 -3329 6563 6565 -3333 6563 6571 -3334 5894 5899 -3334 6558 6562 -3336 6561 6665 -3353 6346 6461 -3355 6563 6568 -3357 6346 6353 -3363 6555 6665 -3367 6456 6569 -3368 6558 6564 -3379 5894 5901 -3382 6556 6665 -3464 696 4433 -3489 1302 1412 -3414 3029 3032 -3424 2728 2837 -3425 3029 3034 -3432 3125 3130 -3435 3130 3132 -3408 5381 5387 -3421 4680 4792 -3427 5379 5382 -3431 4695 4700 -3460 5381 5382 -3464 696 4433 -3482 5380 5381 -3494 5379 5380 -3417 6555 6666 -3418 6556 6670 -3466 6222 6227 -3476 6563 6570 -3477 6664 6665 -3489 6343 6345 -3500 6552 6662 -3508 1295 1412 -3509 1300 1302 -3529 1085 1093 -3531 799 4435 -3542 797 4433 -3546 975 1085 -3569 979 1085 -3512 3131 3133 -3527 3127 3130 -3574 2836 2840 -3583 2940 2942 -3590 2834 2837 -3524 4698 4700 -3528 5262 5379 -3531 799 4435 -3537 5261 5380 -3542 797 4433 -3544 5261 5381 -3549 4702 4703 -3552 5261 5379 -3560 4700 4703 -3563 4592 4701 -3581 4701 4703 -3595 4592 4598 -3598 4703 4706 -3505 6342 6343 -3515 5781 5900 -3518 6227 6342 -3521 6651 6752 -3526 6343 6344 -3528 5894 5902 -3528 6659 6666 -3540 5781 5902 -3540 6664 6666 -3555 6664 6667 -3560 6659 6660 -3561 5779 5902 -3561 6661 6666 -3563 6660 6666 -3577 6658 6660 -3585 5780 5787 -3588 6758 6759 -3598 6558 6670 -3601 1302 1303 -3608 3030 3037 -3623 2836 2837 -3625 2939 2941 -3625 2941 3036 -3642 2941 2942 -3653 2840 2942 -3663 2941 3044 -3676 3037 3134 -3680 3049 3140 -3685 3042 3043 -3687 2942 2945 -3611 4591 4598 -3618 4698 4705 -3630 4596 4701 -3639 4596 4598 -3646 4596 4706 -3674 4703 4705 -3676 4596 4709 -3694 4492 4591 -3602 5779 5897 -3606 5781 5785 -3614 6662 6664 -3615 6655 6660 -3617 6556 6662 -3623 5780 5785 -3632 6659 6661 -3642 5780 5786 -3647 5666 5786 -3647 6558 6668 -3655 5782 5785 -3665 5672 5786 -3673 5779 5784 -3691 5780 5788 -3694 5666 5788 -3696 5672 5792 -3702 1013 4541 -3739 3146 3147 -3748 3042 3044 -3755 3147 3240 -3771 3036 3038 -3775 3047 3049 -3781 2739 6420 -3787 3031 3128 -3796 2942 2943 -3702 1013 4541 -3711 4704 4706 -3713 4591 4599 -3721 4485 4492 -3737 4490 4492 -3748 4704 4709 -3771 4490 4599 -3774 4596 4599 -3776 4388 4492 -3782 4594 4599 -3785 4388 4491 -3800 4704 4711 -3701 5670 5786 -3718 5664 5788 -3722 6305 6419 -3726 5782 5784 -3726 6227 6337 -3737 5672 5794 -3748 6661 6667 -3750 5670 5788 -3769 6305 6309 -3775 5783 5785 -3781 2739 6420 -3797 5672 5676 -3810 2832 2837 -3815 3140 3148 -3817 2740 2847 -3822 3147 3151 -3824 2945 3044 -3834 3044 3050 -3836 3042 3047 -3839 3235 3322 -3844 3047 3050 -3845 3234 3241 -3849 3047 3148 -3850 3044 3047 -3852 3042 3050 -3852 3147 3148 -3853 3042 3049 -3853 3047 3052 -3855 3045 3050 -3856 3043 3047 -3857 2944 2945 -3857 3045 3047 -3858 2945 3042 -3858 3050 3052 -3863 2945 3045 -3865 3145 3148 -3866 3143 3148 -3874 2941 2947 -3875 2943 2944 -3879 2944 2950 -3880 2947 3042 -3880 3051 3052 -3887 2941 2945 -3889 2940 2941 -3892 3052 3053 -3895 2950 3045 -3895 3234 3242 -3898 3147 3242 -3801 4596 4601 -3803 4492 4493 -3803 4495 4599 -3820 4491 4493 -3822 4594 4601 -3827 5671 5676 -3829 4493 4496 -3850 5673 5794 -3856 4392 4491 -3858 4601 4704 -3863 4495 4496 -3865 4704 4710 -3871 4387 4394 -3886 4392 4394 -3890 5677 5684 -3897 4493 4499 -3812 5678 5792 -3813 5669 5788 -3816 6306 6309 -3818 5783 5788 -3824 5663 5784 -3827 5671 5676 -3839 5678 5798 -3843 5670 5794 -3843 5783 5784 -3850 5673 5794 -3864 5676 5792 -3864 5678 5800 -3878 5676 5678 -3880 5678 5682 -3881 5670 5789 -3890 5677 5684 -3891 5676 5794 -3933 1295 1302 -3987 859 860 -3904 2838 2839 -3909 3145 3242 -3912 2839 2840 -3914 3145 3150 -3914 3239 3241 -3919 3239 3242 -3922 2837 2839 -3922 3143 3149 -3923 3237 3242 -3937 3150 3237 -3944 3052 3143 -3944 3143 3150 -3951 3322 3324 -3952 2839 2943 -3952 3322 3323 -3956 3145 3237 -3957 3239 3322 -3965 3239 3330 -3973 3242 3244 -3982 3323 3404 -3987 2726 2731 -3988 3046 3052 -3991 3239 3244 -3992 3322 3327 -3993 2731 2832 -3994 3052 3149 -3996 3045 3052 -3996 3322 3325 -3999 3052 3148 -3901 4392 4499 -3903 5677 5682 -3909 4495 4600 -3925 4494 4496 -3931 5683 5684 -3953 4295 4393 -3961 4494 4499 -3964 5684 5685 -3973 5679 5682 -3973 5562 5682 -3981 4394 4395 -3991 4494 4501 -3991 5682 5685 -3992 5683 5690 -3996 4393 4395 -3903 5677 5682 -3905 5676 5797 -3908 5783 5790 -3914 5676 5800 -3921 5675 5794 -3930 5676 5795 -3931 5683 5684 -3939 6558 6669 -3940 6558 6662 -3941 5783 5789 -3948 6661 6664 -3949 6647 6654 -3954 5676 5681 -3964 5684 5685 -3971 6663 6664 -3973 5562 5682 -3973 5679 5682 -3973 6658 6661 -3977 5669 5789 -3988 6468 6575 -3989 6656 6663 -3991 5682 5685 -3992 5683 5690 -4007 2923 3026 -4010 3327 3329 -4011 3327 3330 -4012 2923 3025 -4014 3404 3405 -4020 2923 3018 -4025 3237 3244 -4033 3325 3330 -4040 3238 3244 -4044 3404 3406 -4048 3013 3110 -4053 3244 3325 -4063 3323 3327 -4012 4395 4398 -4016 4394 4397 -4019 5683 5688 -4023 4397 4499 -4034 5570 5689 -4044 4294 4301 -4053 4397 4398 -4062 5568 5688 -4065 5685 5688 -4082 4299 4393 -4094 4397 4500 -4019 5683 5688 -4028 6569 6570 -4029 6661 6663 -4045 6569 6680 -4053 6019 6022 -4055 6581 6582 -4055 6663 6764 -4062 5568 5688 -4062 6569 6571 -4062 6576 6680 -4065 5685 5688 -4068 5675 5789 -4082 6021 6142 -4084 6570 6574 -4085 6569 6574 -4096 5680 5682 -4097 6680 6688 -4099 6574 6680 -4128 696 4331 -4141 797 4331 -4150 797 4435 -4102 3327 3404 -4105 3024 3026 -4112 3327 3412 -4117 3405 3480 -4119 3405 3409 -4121 3406 3412 -4133 3404 3409 -4154 3024 3029 -4158 3474 3481 -4161 3406 3409 -4168 2508 2613 -4170 3327 3332 -4198 3029 3031 -4102 4299 4301 -4124 4299 4401 -4147 4207 4300 -4147 5576 5689 -4148 5683 5691 -4149 5679 5681 -4150 797 4435 -4151 5574 5689 -4153 4396 4398 -4154 5570 5691 -4158 4331 4433 -4163 5685 5687 -4164 4294 4302 -4166 4331 4435 -4167 4331 4332 -4174 5568 5691 -4182 4331 4434 -4197 5574 5691 -4106 5675 5795 -4108 6574 6682 -4112 6680 6683 -4115 6017 6019 -4117 6022 6142 -4117 6021 6140 -4117 6680 6685 -4119 6570 6680 -4120 6680 6682 -4123 6027 6140 -4123 6570 6571 -4130 6764 6771 -4132 6460 6571 -4135 6674 6680 -4136 6568 6571 -4137 6681 6788 -4138 6681 6685 -4140 6025 6140 -4142 6687 6788 -4147 5576 5689 -4148 5683 5691 -4149 5679 5681 -4151 5574 5689 -4151 6680 6681 -4154 5570 5691 -4163 5685 5687 -4165 6025 6142 -4166 6685 6687 -4168 6685 6688 -4174 5568 5691 -4174 6146 6147 -4176 6080 6199 -4176 6661 6772 -4180 6764 6772 -4182 6140 6148 -4183 6573 6574 -4187 6794 6795 -4189 6687 6795 -4190 6019 6024 -4195 6147 6148 -4197 5574 5691 -4201 962 969 -4210 1440 1555 -4204 3029 3130 -4209 3325 3332 -4227 3133 3138 -4239 3130 3133 -4253 3024 3025 -4271 3405 3482 -4282 3327 3407 -4290 3474 3482 -4238 5576 5695 -4246 5674 5681 -4259 5582 5695 -4275 5576 5697 -4294 5574 5694 -4299 5574 5697 -4211 6788 6796 -4214 6687 6796 -4219 6146 6265 -4220 6571 6573 -4226 6566 6571 -4232 6024 6142 -4233 6682 6683 -4237 6795 6799 -4238 5576 5695 -4241 6794 6899 -4242 6794 6796 -4245 6147 6151 -4246 5674 5681 -4249 6795 6899 -4254 6017 6018 -4258 6145 6148 -4259 5582 5695 -4266 6691 6796 -4275 5576 5697 -4278 6153 6265 -4286 6683 6684 -4287 6899 6900 -4291 6899 6901 -4294 5574 5694 -4296 6142 6143 -4296 6151 6265 -4299 5574 5697 -4300 6683 6685 -4301 3138 3230 -4302 3129 3130 -4306 3122 3130 -4317 3133 3136 -4345 3406 3407 -4352 3406 3482 -4364 3403 3482 -4370 3231 3232 -4302 4206 4213 -4308 4299 4302 -4309 5582 5701 -4339 4396 4401 -4340 4301 4302 -4349 5580 5695 -4384 4300 4302 -4388 4301 4304 -4390 5582 5703 -4305 6151 6267 -4306 6796 6799 -4306 6799 6901 -4326 6271 6272 -4328 6024 6137 -4337 6899 6904 -4337 6906 7000 -4341 6265 6273 -4349 5580 5695 -4360 6573 6683 -4360 6685 6690 -4363 6272 6273 -4365 6143 6145 -4368 6796 6797 -4368 6904 6906 -4369 5680 5681 -4375 5680 5687 -4397 6690 6796 -4405 3128 3133 -4408 3479 3481 -4411 3475 3543 -4455 3326 3332 -4457 3479 3482 -4458 3138 3231 -4476 3481 3543 -4497 3408 3409 -4408 5685 5686 -4431 5573 5691 -4433 5568 5686 -4447 5580 5697 -4472 4302 4305 -4474 5586 5701 -4477 5574 5692 -4479 4302 4308 -4480 5580 5700 -4408 5685 5686 -4416 6661 6767 -4417 7006 7007 -4425 6278 6388 -4431 5573 5691 -4433 5568 5686 -4434 6145 6150 -4434 6904 6907 -4439 6904 6910 -4442 6150 6267 -4443 6453 6459 -4445 6272 6276 -4447 5580 5697 -4451 5686 5688 -4452 5686 5691 -4456 6798 6799 -4464 6143 6144 -4465 6276 6388 -4465 7000 7008 -4467 6459 6566 -4470 6572 6573 -4470 6904 7008 -4474 6901 6902 -4475 7000 7007 -4477 7007 7097 -4480 5580 5700 -4480 6270 6273 -4485 6904 6909 -4487 6265 6268 -4489 5686 5687 -4492 6902 6904 -4500 7005 7007 -4502 3326 3407 -4508 3144 3237 -4518 3408 3483 -4524 3128 3129 -4533 3031 3122 -4534 3144 3231 -4550 3479 3551 -4559 3139 3231 -4566 3139 3230 -4567 3407 3408 -4567 3408 3482 -4568 3144 3236 -4573 3477 3482 -4596 3479 3543 -4598 3142 3236 -4507 4211 4300 -4530 4211 4213 -4535 5580 5703 -4555 5586 5703 -4568 4304 4401 -4585 5586 5707 -4586 4211 4308 -4511 7005 7008 -4512 6267 6268 -4519 6276 6390 -4521 7003 7008 -4526 7097 7105 -4536 7005 7105 -4545 6690 6791 -4547 7097 7104 -4553 7103 7104 -4554 7102 7105 -4556 6660 6661 -4563 6268 6270 -4564 6797 6798 -4572 6798 6902 -4572 7104 7188 -4574 7102 7104 -4580 6150 6262 -4587 5686 5693 -4595 6902 6903 -4598 7194 7195 -4627 904 4541 -4660 904 4435 -4667 1413 1523 -4672 962 963 -4691 854 963 -4699 962 967 -4609 3145 3236 -4610 3543 3545 -4618 3139 3236 -4618 3537 3544 -4641 3133 3135 -4645 3139 3228 -4663 3479 3484 -4667 3141 3236 -4667 3544 3545 -4676 3135 3139 -4686 3477 3484 -4606 4327 4334 -4627 904 4541 -4636 4235 4327 -4645 5572 5692 -4655 4327 4335 -4660 904 4435 -4667 4213 4214 -4692 5573 5692 -4616 6394 6395 -4635 7188 7196 -4647 7005 7010 -4653 7102 7196 -4657 7100 7105 -4669 6388 6396 -4674 6903 6909 -4674 7188 7195 -4688 6909 7003 -4699 7003 7010 -4711 967 970 -4718 1406 1407 -4735 854 956 -4742 963 964 -4746 848 854 -4754 956 964 -4762 852 956 -4765 961 964 -4770 964 967 -4788 964 966 -4790 965 967 -4710 3542 3544 -4720 3545 3548 -4730 3135 3136 -4733 3544 3597 -4743 3141 3228 -4746 3234 3236 -4766 3134 3135 -4766 3546 3551 -4789 3545 3605 -4794 3484 3546 -4706 4141 4228 -4708 5579 5697 -4711 5579 5692 -4721 4233 4235 -4723 4228 4235 -4739 4233 4335 -4744 4228 4236 -4763 4134 4141 -4776 4233 4236 -4776 5585 5703 -4779 4139 4141 -4789 4330 4335 -4708 5579 5697 -4710 6395 6396 -4710 7102 7107 -4755 7193 7196 -4767 7189 7273 -4768 7191 7196 -4780 7195 7196 -4782 7201 7279 -4792 6270 6275 -4801 959 964 -4806 848 950 -4807 965 966 -4813 848 956 -4825 848 958 -4832 852 854 -4833 852 964 -4840 852 958 -4858 846 848 -4888 841 846 -4891 841 842 -4895 852 959 -4898 959 965 -4802 3234 3235 -4804 3597 3604 -4807 3546 3548 -4808 3546 3553 -4828 3598 3604 -4831 3128 3135 -4831 3597 3605 -4836 3542 3605 -4841 3234 3239 -4851 3547 3548 -4857 3542 3602 -4859 3547 3605 -4864 3602 3604 -4872 3600 3605 -4873 3597 3602 -4879 3604 3645 -4883 3602 3605 -4900 3645 3652 -4807 4053 4134 -4808 4139 4236 -4828 5698 5703 -4839 4134 4142 -4840 4233 4238 -4884 5585 5704 -4892 4231 4236 -4896 4238 4330 -4802 5697 5698 -4808 6275 6390 -4809 6393 6396 -4809 7193 7199 -4809 7195 7199 -4811 7196 7198 -4828 5698 5703 -4829 7199 7281 -4830 7010 7100 -4833 7285 7286 -4836 6269 6275 -4844 6268 6269 -4845 6150 6269 -4857 7107 7191 -4867 7199 7279 -4873 7279 7287 -4876 5698 5700 -4883 7198 7199 -4884 5585 5704 -4896 6395 6399 -4899 7286 7287 -4904 857 959 -4906 851 852 -4908 850 959 -4910 846 849 -4914 851 959 -4917 1758 1869 -4922 849 851 -4938 850 851 -4943 1758 1877 -4945 841 843 -4952 735 842 -4963 843 849 -4971 735 835 -4979 844 851 -4990 735 843 -4925 3645 3653 -4943 1758 1877 -4949 3602 3653 -4958 3605 3607 -4972 3646 3678 -4979 3645 3650 -4985 3602 3650 -4985 3646 3650 -4995 3647 3650 -4998 3645 3648 -4939 4231 4238 -4947 4139 4142 -4910 7100 7107 -4926 7284 7287 -4935 7286 7364 -4937 7199 7282 -4940 7286 7290 -4963 5698 5705 -4967 7364 7365 -4981 7364 7366 -4984 6275 6385 -4991 7282 7287 -5002 739 843 -5011 736 843 -5018 1752 1869 -5022 1756 1869 -5027 844 846 -5031 1752 1863 -5036 733 735 -5036 1752 1871 -5044 844 845 -5065 1756 1877 -5072 1746 1863 -5073 1756 1871 -5076 843 844 -5084 733 835 -5015 3646 3679 -5017 3602 3607 -5030 3547 3606 -5034 3600 3607 -5036 1752 1871 -5048 3675 3679 -5065 1756 1877 -5065 2336 2343 -5073 1756 1871 -5084 3602 3648 -5036 5586 5704 -5032 7371 7436 -5036 5586 5704 -5049 7290 7366 -5088 7364 7369 -5102 733 843 -5118 729 835 -5121 1750 1863 -5101 3648 3653 -5104 3648 3650 -5119 3644 3679 -5148 3649 3650 -5154 3648 3655 -5161 3649 3679 -5187 3607 3648 -5188 2227 2344 -5127 4396 4403 -5185 4299 4304 -5119 7287 7289 -5127 7369 7371 -5130 7191 7197 -5131 5698 5699 -5154 7436 7443 -5168 6655 6761 -5171 6390 6391 -5193 7369 7372 -5243 3677 3679 -5291 3649 3680 -5224 4304 4396 -5263 4125 4206 -5217 7197 7198 -5243 7289 7290 -5252 7204 7282 -5258 7369 7443 -5299 7289 7295 -5310 4206 4214 -5372 4118 4125 -5311 7366 7367 -5314 7288 7289 -5346 6766 6772 -5360 7367 7369 -5365 7369 7374 -5373 7289 7367 -5418 728 735 -5478 632 728 -5481 733 736 -5489 728 736 -5406 4123 4125 -5414 4304 4305 -5439 4118 4124 -5459 4304 4403 -5463 4211 4214 -5478 4208 4214 -5482 4042 4124 -5496 4123 4214 -5415 7441 7443 -5529 1761 1877 -5542 738 843 -5529 1761 1877 -5509 4123 4129 -5523 4118 4126 -5526 4123 4126 -5546 4302 4303 -5564 4304 4402 -5568 4042 4118 -5583 4211 4216 -5506 7439 7443 -5521 7367 7368 -5557 7374 7439 -5602 4211 4303 -5612 4209 4214 -5616 4041 4126 -5617 4297 4303 -5621 4042 4045 -5627 4123 4128 -5628 4042 4126 -5634 4210 4303 -5659 4043 4045 -5662 4303 4304 -5696 4043 4044 -5789 630 728 -5789 630 733 -5795 738 838 -5715 4121 4126 -5849 733 738 -5856 732 738 -5833 4209 4216 -5888 4044 4126 -5909 731 733 -5941 731 732 -5944 630 730 -5946 625 626 -5951 626 630 -5956 730 731 -5959 627 730 -5973 626 627 -5983 629 730 -5988 619 626 -5917 4121 4128 -6013 629 725 -6037 619 624 -6039 624 627 -6050 619 620 -6063 624 629 -6083 519 620 -6009 4044 4045 -6013 4044 4127 -6052 4128 4209 -6228 620 621 -6242 621 624 -6250 4046 4047 -6259 622 624 -6292 519 613 -6250 4046 4047 -6337 618 621 -6354 613 621 -6364 621 623 -6378 622 623 -6445 616 621 -6514 517 613 -6539 6275 6391 -6656 517 615 -6724 513 517 -6760 517 616 -6765 512 513 -6784 506 512 -6792 513 514 -6801 522 616 -6831 516 517 -6853 506 514 -6854 515 616 -6877 511 514 -6885 417 506 -6908 514 516 -6921 415 506 -6942 515 516 -6947 411 506 -6959 509 515 -6966 509 514 -7022 411 500 -7023 420 509 -7043 411 508 -7047 415 508 -7068 412 508 -7093 415 509 -7099 409 500 -7102 413 509 -7135 409 502 -7160 414 508 -7161 409 508 -7174 409 503 -7175 409 411 -7177 409 415 -7180 414 509 -7185 409 414 -7194 409 412 -7173 6274 6281 -7231 414 503 -7204 6280 6281 diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index d2a4559010..82da5bfac5 100644 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 ''' -UPDATE: August 13, 2024: +UPDATE: August 28, 2024: Launching the LAMMPS binary under testing using a configuration defined in a yaml file (e.g. config.yaml). Comparing the output thermo with that in the existing log file (with the same nprocs) + data in the log files are extracted and converted into yaml data structure @@ -13,6 +13,8 @@ With the current features, users can: + specify tolerances for individual quantities for any input script to override the global values + launch tests with `mpirun` with all supported command line features (multiple procs, multiple paritions, and suffices) + skip certain input files (whose names match specified patterns) if not interested, or packaged not installed, or no reference log file exists + + set a timeout for every input script run if they may take too long + + skip numerical checks if the goal is just to check if the runs do not fail + simplify the main LAMMPS builds, as long as a LAMMPS binary is available + keep track of the testing progress to resume the testing from the last checkpoint (skipping completed runs) + distribute the input list across multiple processes via multiprocessing, or @@ -49,16 +51,10 @@ Example usage: --list-subfolders=list_subfolders1.txt --output-file=output1.txt --progress-file=progress1.yaml \ --log-file=run1.log - 4) Specify a list of example input scripts + 4) Specify a list of example input scripts (e.g. obtained from running tools/regression-tests/get-quick-list.py) python3 run_tests.py --lmp-bin=/path/to/lmp_binary --config-file=/path/to/config/file/config.yaml \ - --list-input=input-list-1.txt --output-file=output1.txt --progress-file=progress1.yaml \ - --log-file=run1.log - - The example subfolders can also be loaded from a text file list_subfolders1.txt: - python3 run_tests.py --lmp-bin=/path/to/lmp_binary --config-file=/path/to/config/file/config.yaml \ - --list-subfolders=list_subfolders1.txt --output-file=output1.txt --progress-file=progress1.yaml \ - --log-file=run1.log - + --list-input=input_list.txt + 5) Test a LAMMPS binary with the whole top-level /examples folder in a LAMMPS source tree python3 run_tests.py --lmp-bin=/path/to/lmp_binary --examples-top-level=/path/to/lammps/examples @@ -248,7 +244,8 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file if fnmatch.fnmatch(file, pattern): p = file.rsplit('.', 1) if p[1].isnumeric(): - if use_valgrind == True: + # if using valgrind or running in serial, then use the log file with 1 proc + if use_valgrind == True or config['mpiexec'] == "": if int(p[1]) == 1: max_np = int(p[1]) ref_logfile_exist = True @@ -263,10 +260,11 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file # if there is no ref log file and not running with valgrind if ref_logfile_exist == False and use_valgrind == False: max_np = 4 - - # if the maximum number of procs is different from the value in the configuration file - # then override the setting for this input script + saved_nprocs = config['nprocs'] + + # if the maximum number of procs is different from the value in the configuration file + # then override the setting for this particular input script if max_np != int(config['nprocs']): config['nprocs'] = str(max_np) @@ -318,6 +316,41 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file test_id = test_id + 1 continue + # check if a log.lammps file exists in the current folder + if os.path.isfile("log.lammps") == False: + logger.info(f" ERROR: No log.lammps generated with {input_test} with return code {returncode}.\n") + logger.info(f" Output:") + logger.info(f" {output}") + logger.info(f" Error:\n{error}") + progress.write(f"{input}: {{ folder: {input_folder}, status: \"error, no log.lammps\" }}\n") + progress.close() + num_error = num_error + 1 + test_id = test_id + 1 + continue + else: + # save a copy of the log file for further inspection + cmd_str = f"cp log.lammps log.{basename}.{nprocs}" + p = subprocess.run(cmd_str, shell=True, text=True, capture_output=True) + + # if skip numerical checks, then skip the rest + if skip_numerical_check == True: + msg = "completed, skipping numerical checks" + if use_valgrind == True: + if "All heap blocks were freed" in error: + msg += ", no memory leak" + else: + msg += ", memory leaks detected" + num_memleak = num_memleak + 1 + result.status = msg + results.append(result) + progress.write(f"{input}: {{ folder: {input_folder}, status: \"{msg}\" }}\n") + progress.close() + + # count the number of completed runs + num_completed = num_completed + 1 + test_id = test_id + 1 + continue + # if there is no ERROR in the output, but there is no Total wall time printed out if "Total wall time" not in output: logger.info(f" ERROR: no Total wall time in the output.\n") @@ -342,22 +375,6 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file test_id = test_id + 1 continue - # check if a log.lammps file exists in the current folder - if os.path.isfile("log.lammps") == False: - logger.info(f" ERROR: No log.lammps generated with {input_test} with return code {returncode}.\n") - logger.info(f" Output:") - logger.info(f" {output}") - logger.info(f" Error:\n{error}") - progress.write(f"{input}: {{ folder: {input_folder}, status: \"error, no log.lammps\" }}\n") - progress.close() - num_error = num_error + 1 - test_id = test_id + 1 - continue - else: - # save a copy of the log file for further inspection - cmd_str = f"cp log.lammps log.{basename}.{nprocs}" - p = subprocess.run(cmd_str, shell=True, text=True, capture_output=True) - # parse thermo output in log.lammps from the run thermo = extract_data_to_yaml("log.lammps") num_runs = len(thermo) @@ -724,9 +741,12 @@ def get_lammps_build_configuration(lmp_binary): ''' def execute(lmp_binary, config, input_file_name, generate_ref_yaml=False): cmd_str = "" + # check if mpiexec/mpirun is used if config['mpiexec']: cmd_str += config['mpiexec'] + " " + config['mpiexec_numproc_flag'] + " " + config['nprocs'] + " " + cmd_str += lmp_binary + " -in " + input_file_name + " " + config['args'] + logger.info(f" Executing: {cmd_str}") # set a timeout (in seconds) for each run timeout = 60 @@ -739,7 +759,7 @@ def execute(lmp_binary, config, input_file_name, generate_ref_yaml=False): return cmd_str, p.stdout, p.stderr, p.returncode except subprocess.TimeoutExpired: - msg = f" Timeout for {cmd_str} ({timeout} s) expired" + msg = f" Timeout for: {cmd_str} ({timeout}s expired)" logger.info(msg) print(msg) @@ -886,6 +906,8 @@ if __name__ == "__main__": parser.add_argument("--progress-file",dest="progress_file", default=progress_file, help="Progress file") parser.add_argument("--analyze",dest="analyze", action='store_true', default=False, help="Analyze the testing folders and report statistics, not running the tests") + parser.add_argument("--skip-numerical-check",dest="skip_numerical_check", action='store_true', default=False, + help="Generating reference data") args = parser.parse_args() @@ -907,6 +929,7 @@ if __name__ == "__main__": verbose = args.verbose log_file = args.logfile analyze = args.analyze + skip_numerical_check = args.skip_numerical_check resume = args.resume progress_file = args.progress_file From c60e69ea1eece9e9a659447a77846230a1bc3147 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Wed, 28 Aug 2024 14:06:40 -0500 Subject: [PATCH 115/355] have a single job definition with matrix strategy, the build and env do not persist between jobs --- .github/workflows/full-regression.yml | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index 1dcccfcdcc..458b48ef0a 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -17,6 +17,10 @@ jobs: runs-on: ubuntu-latest env: CCACHE_DIR: ${{ github.workspace }}/.ccache + strategy: + max-parallel: 2 + matrix: + idx: [ 0, 1, 2, 3 ] steps: - name: Checkout repository @@ -67,29 +71,15 @@ jobs: cmake --build build ccache -s - - name: Analyze top-level examples folder, split into 8 seperate lists of input scripts + - name: Full regression tests, splitting the top-level example input into 4 lists shell: bash run: | source linuxenv/bin/activate python3 tools/regression-tests/run_tests.py \ --lmp-bin=build/lmp \ --config-file=tools/regression-tests/config_serial.yaml \ - --examples-top-level=examples --analyze --num-workers=8 + --examples-top-level=examples --analyze --num-workers=4 - run_tests: - name: Full Regression Test - # restrict to official LAMMPS repository - if: ${{ github.repository == 'lammps/lammps' }} - runs-on: ubuntu-latest - strategy: - max-parallel: 2 - matrix: - idx: [ 0, 1 ] - - steps: - - name: Run regression tests - run: | - source linuxenv/bin/activate python3 tools/regression-tests/run_tests.py \ --lmp-bin=build/lmp \ --config-file=tools/regression-tests/config_serial.yaml \ @@ -101,3 +91,5 @@ jobs: - name: Upload artifacts uses: actions/upload-artifact@v4 + + From 4e40b4ba63c0b613bcca2b670693f691fc342951 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Wed, 28 Aug 2024 14:11:23 -0500 Subject: [PATCH 116/355] upload the artifacts of the full regression test workflow --- .github/workflows/full-regression.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index 458b48ef0a..d1b0c0c86a 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -88,8 +88,13 @@ jobs: --progress-file=progress-${{ matrix.idx }}.yaml \ --log-file=run-${{ matrix.idx }}.log + tar -cvf full-regression-test.tar run*.log progress*.yaml output*.xml + - name: Upload artifacts uses: actions/upload-artifact@v4 + with: + name: full-regression-test-artifact + path: full-regression-test.tar From acfed1268401818ff3db374e7cf5c0db5b2e603b Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 28 Aug 2024 15:12:46 -0400 Subject: [PATCH 117/355] avoid recursion. there is only one executable. --- examples/PACKAGES/plumed/plugin/plumedplugin.nsis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/PACKAGES/plumed/plugin/plumedplugin.nsis b/examples/PACKAGES/plumed/plugin/plumedplugin.nsis index 229473ddeb..1de9800d26 100644 --- a/examples/PACKAGES/plumed/plugin/plumedplugin.nsis +++ b/examples/PACKAGES/plumed/plugin/plumedplugin.nsis @@ -98,7 +98,7 @@ Section "${PLUMEDPLUGIN}" SecPlumedplugin File plumedplugin.so SetOutPath "$INSTDIR\bin" - File /r *.exe + File plumed.exe # Register Application and its uninstaller WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\LAMMPS-PLUMED" \ From af5f437aeb239890f2727f5da7c6defc147b0d6c Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 28 Aug 2024 15:30:44 -0400 Subject: [PATCH 118/355] update list of dependencies --- examples/PACKAGES/plumed/plugin/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/PACKAGES/plumed/plugin/CMakeLists.txt b/examples/PACKAGES/plumed/plugin/CMakeLists.txt index b14d3e5f9b..33ed34123d 100644 --- a/examples/PACKAGES/plumed/plugin/CMakeLists.txt +++ b/examples/PACKAGES/plumed/plugin/CMakeLists.txt @@ -40,17 +40,17 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") if(BUILD_MPI) if(USE_MSMPI) add_custom_target(package ${MAKENSIS_PATH} -V1 -DVERSION=${LAMMPS_VERSION}-MSMPI plumedplugin.nsis - DEPENDS plumedplugin + DEPENDS plumedplugin plumed_copy lammps.ico lammps-text-logo-wide.bmp plumedplugin.nsis BYPRODUCTS LAMMPS-PLUMED-plugin-${LAMMPS_VERSION}-MSMPI.exe) else() add_custom_target(package ${MAKENSIS_PATH} -V1 -DVERSION=${LAMMPS_VERSION}-MPI plumedplugin.nsis - DEPENDS plumedplugin + DEPENDS plumedplugin plumed_copy lammps.ico lammps-text-logo-wide.bmp plumedplugin.nsis BYPRODUCTS LAMMPS-PLUMED-plugin-${LAMMPS_VERSION}-MPI.exe) endif() else() add_custom_target(package ${MAKENSIS_PATH} -V1 -DVERSION=${LAMMPS_VERSION} plumedplugin.nsis COMMAND ${CMAKE_COMMAND} -E echo ${PWD} - DEPENDS plumedplugin lammps.ico lammps-text-logo-wide.bmp plumedplugin.nsis + DEPENDS plumedplugin plumed_copy lammps.ico lammps-text-logo-wide.bmp plumedplugin.nsis BYPRODUCTS LAMMPS-PLUMED-plugin-${LAMMPS_VERSION}.exe) endif() endif() From aa088da59bb16ed5ec6d3a685adf30295d3ad106 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Wed, 28 Aug 2024 14:43:27 -0500 Subject: [PATCH 119/355] pack test output into separate artifacts --- .github/workflows/full-regression.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index d1b0c0c86a..34a4907d02 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -88,13 +88,10 @@ jobs: --progress-file=progress-${{ matrix.idx }}.yaml \ --log-file=run-${{ matrix.idx }}.log - tar -cvf full-regression-test.tar run*.log progress*.yaml output*.xml + tar -cvf full-regression-test-${{ matrix.idx }}.tar run-${{ matrix.idx }}.log progress-${{ matrix.idx }}.yaml output-${{ matrix.idx }}.xml - name: Upload artifacts uses: actions/upload-artifact@v4 - with: - name: full-regression-test-artifact - path: full-regression-test.tar From 5306f5ff18f43d038a1ddf1b0bbd1c61ec8eb696 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Wed, 28 Aug 2024 15:35:25 -0500 Subject: [PATCH 120/355] provide path to the artifacts --- .github/workflows/full-regression.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index 34a4907d02..7ae324f99a 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -92,6 +92,8 @@ jobs: - name: Upload artifacts uses: actions/upload-artifact@v4 - + with: + name: full-regression-test-artifact + path: full-regression-test-*.tar From 796a0f18d3880619a577339ac965b114161d6792 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Wed, 28 Aug 2024 16:07:13 -0500 Subject: [PATCH 121/355] upload per-job artifacts, try action merge in a separate job --- .github/workflows/full-regression.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index 7ae324f99a..b97910d32c 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -94,6 +94,6 @@ jobs: uses: actions/upload-artifact@v4 with: name: full-regression-test-artifact - path: full-regression-test-*.tar + path: full-regression-test-${{ matrix.idx }}.tar From 04bd62a677ef869e2caaa7bf27987dda88a6023f Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Wed, 28 Aug 2024 16:13:10 -0500 Subject: [PATCH 122/355] add another job with the action merge --- .github/workflows/full-regression.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index b97910d32c..1b8a3d402d 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -95,5 +95,14 @@ jobs: with: name: full-regression-test-artifact path: full-regression-test-${{ matrix.idx }}.tar - + + merge: + runs-on: ubuntu-latest + needs: build + steps: + - name: Merge Artifacts + uses: actions/upload-artifact/merge@v4 + with: + name: merged-full-regresssion-artifact + pattern: full-regression-test-*.tar From a1a3e4e5b7c58af6f024b8cdf21f7f3bf26b84b3 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Wed, 28 Aug 2024 16:45:00 -0500 Subject: [PATCH 123/355] name the artifacts with their matrix idx --- .github/workflows/full-regression.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index 1b8a3d402d..fb3ce9cbef 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -93,7 +93,7 @@ jobs: - name: Upload artifacts uses: actions/upload-artifact@v4 with: - name: full-regression-test-artifact + name: full-regression-test-artifact-${{ matrix.idx }} path: full-regression-test-${{ matrix.idx }}.tar merge: From 2d3cd2a0b91fe3c2945deb87f182176c49b28473 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Wed, 28 Aug 2024 17:22:13 -0500 Subject: [PATCH 124/355] fix typo in the per-job artifact names --- .github/workflows/full-regression.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index fb3ce9cbef..94068252a7 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -104,5 +104,5 @@ jobs: uses: actions/upload-artifact/merge@v4 with: name: merged-full-regresssion-artifact - pattern: full-regression-test-*.tar + pattern: full-regression-test-artifact-* From 2581b1abfe03e8e4bf588081fdb3ecb75aff9ea1 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 28 Aug 2024 22:13:04 -0400 Subject: [PATCH 125/355] update change log info for flatpak --- tools/lammps-gui/lammps-gui.appdata.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/lammps-gui/lammps-gui.appdata.xml b/tools/lammps-gui/lammps-gui.appdata.xml index 312abc66cf..66a15223a2 100644 --- a/tools/lammps-gui/lammps-gui.appdata.xml +++ b/tools/lammps-gui/lammps-gui.appdata.xml @@ -56,7 +56,8 @@ - + Resolve plugin mode issues. + Add -p command line flag to override path to liblammps.so From ba51b59add6ddd4ccaeaefd13887e85d317f62b8 Mon Sep 17 00:00:00 2001 From: cjknight Date: Thu, 29 Aug 2024 12:28:54 -0500 Subject: [PATCH 126/355] first pass on MKL FFT for Intel GPUs; rhodo benchmark results are close to CPU... --- src/KOKKOS/fft3d_kokkos.cpp | 81 ++++++++++++++++++++++++++++++++----- src/KOKKOS/fft3d_kokkos.h | 10 ++++- src/KOKKOS/fftdata_kokkos.h | 35 ++++++++++++++-- src/KOKKOS/kokkos_type.h | 14 +++---- 4 files changed, 117 insertions(+), 23 deletions(-) diff --git a/src/KOKKOS/fft3d_kokkos.cpp b/src/KOKKOS/fft3d_kokkos.cpp index 202d46e788..608a17a3ea 100644 --- a/src/KOKKOS/fft3d_kokkos.cpp +++ b/src/KOKKOS/fft3d_kokkos.cpp @@ -45,7 +45,11 @@ FFT3dKokkos::FFT3dKokkos(LAMMPS *lmp, MPI_Comm comm, int nfast, int ExecutionSpace execution_space = ExecutionSpaceFromDevice::space; #endif -#if defined(FFT_KOKKOS_MKL) + // CHRIS:: what about supporting MKL on both CPU and GPU in same build?? +#if defined(FFT_KOKKOS_MKL_GPU) + if (ngpus > 0 && execution_space == Host) + lmp->error->all(FLERR,"Cannot use the MKL library with Kokkos on the host CPUs in a GPU build"); +#elif defined(FFT_KOKKOS_MKL) if (ngpus > 0 && execution_space == Device) lmp->error->all(FLERR,"Cannot use the MKL library with Kokkos on GPUs"); #elif defined(FFT_KOKKOS_FFTW3) @@ -156,6 +160,8 @@ public: *(out_ptr++) *= norm; #elif defined(FFT_KOKKOS_MKL) d_out(i) *= norm; +#elif defined(FFT_KOKKOS_MKL_GPU) + d_out(i) *= norm; #else // FFT_KOKKOS_KISS d_out(i).re *= norm; d_out(i).im *= norm; @@ -219,8 +225,13 @@ void FFT3dKokkos::fft_3d_kokkos(typename FFT_AT::t_FFT_DATA_1d d_in, total = plan->total1; length = plan->length1; - - #if defined(FFT_KOKKOS_MKL) + + #if defined(FFT_KOKKOS_MKL_GPU) + if (flag == 1) + oneapi::mkl::dft::compute_forward(*(plan->desc_fast), d_data.data()); + else + oneapi::mkl::dft::compute_backward(*(plan->desc_fast), d_data.data()); + #elif defined(FFT_KOKKOS_MKL) if (flag == 1) DftiComputeForward(plan->handle_fast,d_data.data()); else @@ -265,8 +276,13 @@ void FFT3dKokkos::fft_3d_kokkos(typename FFT_AT::t_FFT_DATA_1d d_in, total = plan->total2; length = plan->length2; - - #if defined(FFT_KOKKOS_MKL) + + #if defined(FFT_KOKKOS_MKL_GPU) + if (flag == 1) + oneapi::mkl::dft::compute_forward(*(plan->desc_mid), d_data.data()); + else + oneapi::mkl::dft::compute_backward(*(plan->desc_mid), d_data.data()); + #elif defined(FFT_KOKKOS_MKL) if (flag == 1) DftiComputeForward(plan->handle_mid,d_data.data()); else @@ -310,7 +326,12 @@ void FFT3dKokkos::fft_3d_kokkos(typename FFT_AT::t_FFT_DATA_1d d_in, total = plan->total3; length = plan->length3; - #if defined(FFT_KOKKOS_MKL) + #if defined(FFT_KOKKOS_MKL_GPU) + if (flag == 1) + oneapi::mkl::dft::compute_forward(*(plan->desc_slow), d_data.data()); + else + oneapi::mkl::dft::compute_backward(*(plan->desc_slow), d_data.data()); + #elif defined(FFT_KOKKOS_MKL) if (flag == 1) DftiComputeForward(plan->handle_slow,d_data.data()); else @@ -609,7 +630,31 @@ struct fft_plan_3d_kokkos* FFT3dKokkos::fft_3d_create_pl // system specific pre-computation of 1d FFT coeffs // and scaling normalization -#if defined(FFT_KOKKOS_MKL) +#if defined(FFT_KOKKOS_MKL_GPU) + sycl::queue queue = LMPDeviceType().sycl_queue(); // is this the correct queue? + + plan->desc_fast = new descriptor_t (nfast); + plan->desc_fast->set_value(oneapi::mkl::dft::config_param::BACKWARD_SCALE, (FFT_SCALAR)(1.0/nfast)); + plan->desc_fast->set_value(oneapi::mkl::dft::config_param::NUMBER_OF_TRANSFORMS, plan->total1/nfast); + plan->desc_fast->set_value(oneapi::mkl::dft::config_param::FWD_DISTANCE, nfast); + plan->desc_fast->set_value(oneapi::mkl::dft::config_param::BWD_DISTANCE, nfast); + plan->desc_fast->commit(queue); + + plan->desc_mid = new descriptor_t (nmid); + plan->desc_mid->set_value(oneapi::mkl::dft::config_param::BACKWARD_SCALE, (FFT_SCALAR)(1.0/nmid)); + plan->desc_mid->set_value(oneapi::mkl::dft::config_param::NUMBER_OF_TRANSFORMS, plan->total1/nmid); + plan->desc_mid->set_value(oneapi::mkl::dft::config_param::FWD_DISTANCE, nmid); + plan->desc_mid->set_value(oneapi::mkl::dft::config_param::BWD_DISTANCE, nmid); + plan->desc_mid->commit(queue); + + plan->desc_slow = new descriptor_t (nslow); + plan->desc_slow->set_value(oneapi::mkl::dft::config_param::BACKWARD_SCALE, (FFT_SCALAR)(1.0/nslow)); + plan->desc_slow->set_value(oneapi::mkl::dft::config_param::NUMBER_OF_TRANSFORMS, plan->total1/nslow); + plan->desc_slow->set_value(oneapi::mkl::dft::config_param::FWD_DISTANCE, nslow); + plan->desc_slow->set_value(oneapi::mkl::dft::config_param::BWD_DISTANCE, nslow); + plan->desc_slow->commit(queue); + +#elif defined(FFT_KOKKOS_MKL) DftiCreateDescriptor( &(plan->handle_fast), FFT_KOKKOS_MKL_PREC, DFTI_COMPLEX, 1, (MKL_LONG)nfast); DftiSetValue(plan->handle_fast, DFTI_NUMBER_OF_TRANSFORMS, @@ -781,7 +826,11 @@ void FFT3dKokkos::fft_3d_destroy_plan_kokkos(struct fft_plan_3d_kokk if (plan->mid2_plan) remapKK->remap_3d_destroy_plan_kokkos(plan->mid2_plan); if (plan->post_plan) remapKK->remap_3d_destroy_plan_kokkos(plan->post_plan); -#if defined(FFT_KOKKOS_MKL) +#if defined(FFT_KOKKOS_MKL_GPU) + delete plan->desc_fast; + delete plan->desc_mid; + delete plan->desc_slow; +#elif defined(FFT_KOKKOS_MKL) DftiFreeDescriptor(&(plan->handle_fast)); DftiFreeDescriptor(&(plan->handle_mid)); DftiFreeDescriptor(&(plan->handle_slow)); @@ -856,7 +905,7 @@ void FFT3dKokkos::fft_3d_1d_only_kokkos(typename FFT_AT::t_FFT_DATA_ // fftw3 and Dfti in MKL encode the number of transforms // into the plan, so we cannot operate on a smaller data set -#if defined(FFT_KOKKOS_MKL) || defined(FFT_KOKKOS_FFTW3) +#if defined(FFT_KOKKOS_MKL_GPU) || defined(FFT_KOKKOS_MKL) || defined(FFT_KOKKOS_FFTW3) if ((total1 > nsize) || (total2 > nsize) || (total3 > nsize)) return; #endif @@ -866,8 +915,18 @@ void FFT3dKokkos::fft_3d_1d_only_kokkos(typename FFT_AT::t_FFT_DATA_ // perform 1d FFTs in each of 3 dimensions // data is just an array of 0.0 - -#if defined(FFT_KOKKOS_MKL) + +#if defined(FFT_KOKKOS_MKL_GPU) + if (flag == -1) { + oneapi::mkl::dft::compute_forward(*(plan->desc_fast), d_data.data()); + oneapi::mkl::dft::compute_forward(*(plan->desc_mid), d_data.data()); + oneapi::mkl::dft::compute_forward(*(plan->desc_slow), d_data.data()); + } else { + oneapi::mkl::dft::compute_backward(*(plan->desc_fast), d_data.data()); + oneapi::mkl::dft::compute_backward(*(plan->desc_mid), d_data.data()); + oneapi::mkl::dft::compute_backward(*(plan->desc_slow), d_data.data()); + } +#elif defined(FFT_KOKKOS_MKL) if (flag == -1) { DftiComputeForward(plan->handle_fast,d_data.data()); DftiComputeForward(plan->handle_mid,d_data.data()); diff --git a/src/KOKKOS/fft3d_kokkos.h b/src/KOKKOS/fft3d_kokkos.h index 48b0fd76de..6c8296d399 100644 --- a/src/KOKKOS/fft3d_kokkos.h +++ b/src/KOKKOS/fft3d_kokkos.h @@ -20,7 +20,9 @@ #include "fftdata_kokkos.h" namespace LAMMPS_NS { - + + typedef oneapi::mkl::dft::descriptor descriptor_t; + // ------------------------------------------------------------------------- // plan for how to perform a 3d FFT @@ -45,7 +47,11 @@ struct fft_plan_3d_kokkos { double norm; // normalization factor for rescaling // system specific 1d FFT info -#if defined(FFT_KOKKOS_MKL) +#if defined(FFT_KOKKOS_MKL_GPU) + descriptor_t *desc_fast; + descriptor_t *desc_mid; + descriptor_t *desc_slow; +#elif defined(FFT_KOKKOS_MKL) DFTI_DESCRIPTOR *handle_fast; DFTI_DESCRIPTOR *handle_mid; DFTI_DESCRIPTOR *handle_slow; diff --git a/src/KOKKOS/fftdata_kokkos.h b/src/KOKKOS/fftdata_kokkos.h index 0cb59f49cb..90d917c093 100644 --- a/src/KOKKOS/fftdata_kokkos.h +++ b/src/KOKKOS/fftdata_kokkos.h @@ -36,8 +36,8 @@ # endif #endif -// with KOKKOS in CUDA or HIP mode we can only have -// CUFFT/HIPFFT or KISS, thus undefine all other +// with KOKKOS in CUDA, HIP, or SYCL mode we can only have +// CUFFT/HIPFFT/oneMKL or KISS, thus undefine all other // FFTs here #ifdef KOKKOS_ENABLE_CUDA @@ -66,6 +66,19 @@ # if !defined(FFT_KOKKOS_HIPFFT) && !defined(FFT_KOKKOS_KISS) # define FFT_KOKKOS_KISS # endif +#elif defined(KOKKOS_ENABLE_SYCL) +# if defined(FFT_KOKKOS_FFTW) +# undef FFT_KOKKOS_FFTW +# endif +# if defined(FFT_KOKKOS_FFTW3) +# undef FFT_KOKKOS_FFTW3 +# endif +# if defined(FFT_KOKKOS_MKL) +# undef FFT_KOKKOS_MKL +# endif +# if !defined(FFT_KOKKOS_MKL_GPU) && !defined(FFT_KOKKOS_KISS) +# define FFT_KOKKOS_KISS +# endif #else # if defined(FFT_KOKKOS_CUFFT) # error "Must enable CUDA with KOKKOS to use -DFFT_KOKKOS_CUFFT" @@ -73,6 +86,9 @@ # if defined(FFT_KOKKOS_HIPFFT) # error "Must enable HIP with KOKKOS to use -DFFT_KOKKOS_HIPFFT" # endif +# if defined(FFT_KOKKOS_MKL_GPU) +# error "Must enable SYCL with KOKKOS to use -DFFT_KOKKOS_MKL_GPU" +# endif #endif // set strings for library info output @@ -85,12 +101,25 @@ #define LMP_FFT_KOKKOS_LIB "FFTW3" #elif defined(FFT_KOKKOS_MKL) #define LMP_FFT_KOKKOS_LIB "MKL FFT" +#elif defined(FFT_KOKKOS_MKL_GPU) +#define LMP_FFT_KOKKOS_LIB "MKL_GPU FFT" #else #define LMP_FFT_KOKKOS_LIB "KISS FFT" #endif -#if defined(FFT_KOKKOS_MKL) +#if defined(FFT_KOKKOS_MKL_GPU) + #include "CL/sycl.hpp" + #include "oneapi/mkl/dfti.hpp" // conflict between PRECISION macro in dfti.hpp and kokkos_type.h + #include "mkl.h" + #if defined(FFT_SINGLE) + typedef float FFT_KOKKOS_DATA; + #define FFT_KOKKOS_MKL_PREC DFTI_SINGLE + #else + typedef double FFT_KOKKOS_DATA; + #define FFT_KOKKOS_MKL_PREC DFTI_DOUBLE + #endif +#elif defined(FFT_KOKKOS_MKL) #include "mkl_dfti.h" #if defined(FFT_SINGLE) typedef float _Complex FFT_KOKKOS_DATA; diff --git a/src/KOKKOS/kokkos_type.h b/src/KOKKOS/kokkos_type.h index 7f0eb5c105..e9061dd7a3 100644 --- a/src/KOKKOS/kokkos_type.h +++ b/src/KOKKOS/kokkos_type.h @@ -341,17 +341,17 @@ public: // define precision // handle global precision, force, energy, positions, kspace separately -#ifndef PRECISION -#define PRECISION 2 +#ifndef LMP_PRECISION +#define LMP_PRECISION 2 #endif -#if PRECISION==1 +#if LMP_PRECISION==1 typedef float LMP_FLOAT; #else typedef double LMP_FLOAT; #endif #ifndef PREC_FORCE -#define PREC_FORCE PRECISION +#define PREC_FORCE LMP_PRECISION #endif #if PREC_FORCE==1 @@ -361,7 +361,7 @@ typedef double F_FLOAT; #endif #ifndef PREC_ENERGY -#define PREC_ENERGY PRECISION +#define PREC_ENERGY LMP_PRECISION #endif #if PREC_ENERGY==1 @@ -521,7 +521,7 @@ struct BinOp3DLAMMPS { }; #ifndef PREC_POS -#define PREC_POS PRECISION +#define PREC_POS LMP_PRECISION #endif #if PREC_POS==1 @@ -531,7 +531,7 @@ typedef double X_FLOAT; #endif #ifndef PREC_VELOCITIES -#define PREC_VELOCITIES PRECISION +#define PREC_VELOCITIES LMP_PRECISION #endif #if PREC_VELOCITIES==1 From f0513bad4c372e206ffecec54fe9f439648b6ec9 Mon Sep 17 00:00:00 2001 From: Chris Knight Date: Thu, 29 Aug 2024 18:20:41 +0000 Subject: [PATCH 127/355] cleanup --- src/MAKE/MACHINES/Makefile.aurora | 2 +- src/MAKE/MACHINES/Makefile.aurora_kokkos | 10 +++++----- src/info.cpp | 4 ++++ src/lmpfftsettings.h | 2 ++ 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/MAKE/MACHINES/Makefile.aurora b/src/MAKE/MACHINES/Makefile.aurora index 1b16025f7c..78c08b8e95 100644 --- a/src/MAKE/MACHINES/Makefile.aurora +++ b/src/MAKE/MACHINES/Makefile.aurora @@ -116,6 +116,6 @@ depend : fastdep.exe $(SRC) @./fastdep.exe $(EXTRA_INC) -- $^ > .depend || exit 1 fastdep.exe: ../DEPEND/fastdep.c - cc -O -o $@ $< + icx -O -o $@ $< sinclude .depend diff --git a/src/MAKE/MACHINES/Makefile.aurora_kokkos b/src/MAKE/MACHINES/Makefile.aurora_kokkos index 4aa737346a..a9000bcfd6 100644 --- a/src/MAKE/MACHINES/Makefile.aurora_kokkos +++ b/src/MAKE/MACHINES/Makefile.aurora_kokkos @@ -7,7 +7,7 @@ SHELL = /bin/sh # specify flags and libraries needed for your compiler CC = mpicxx -CCFLAGS = -g -O3 -DNDEBUG +CCFLAGS = -g -O3 -DNDEBUG -w CCFLAGS += -fsycl-device-code-split=per_kernel SHFLAGS = -fPIC DEPFLAGS = -M @@ -54,9 +54,9 @@ MPI_LIB = # PATH = path for FFT library # LIB = name of FFT library -FFT_INC = -FFT_PATH = -FFT_LIB = +FFT_INC = -DFFT_KOKKOS_MKL_GPU -I${MKL_ROOT}/include +FFT_PATH = -L${MKL_ROOT}/lib -L${TBB_ROOT}/lib/intel64/gcc4.8 +FFT_LIB = -lmkl_sycl_dft -lmkl_intel_ilp64 -lmkl_tbb_thread -lmkl_core -ltbb # JPEG and/or PNG library # see discussion in Section 3.5.4 of manual @@ -118,6 +118,6 @@ depend : fastdep.exe $(SRC) @./fastdep.exe $(EXTRA_INC) -- $^ > .depend || exit 1 fastdep.exe: ../DEPEND/fastdep.c - cc -O -o $@ $< + icx -O -o $@ $< sinclude .depend diff --git a/src/info.cpp b/src/info.cpp index 98ed06f498..f50a2ccc6a 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -1316,6 +1316,8 @@ std::string Info::get_fft_info() #else fft_info += "FFT library = MKL\n"; #endif +#elif defined(FFT_MKL_GPU) + fft_info += "FFT library = MKL GPU\n"; #elif defined(FFT_FFTW3) #if defined(FFT_FFTW_THREADS) fft_info += "FFT library = FFTW3 with threads\n"; @@ -1344,6 +1346,8 @@ std::string Info::get_fft_info() #else fft_info += "KOKKOS FFT library = MKL\n"; #endif +#elif defined(FFT_KOKKOS_MKL_GPU) + fft_info += "KOKKOS FFT library = MKL GPU\n"; #else fft_info += "KOKKOS FFT library = KISS\n"; #endif diff --git a/src/lmpfftsettings.h b/src/lmpfftsettings.h index 1b9c89274c..3bcab4a61b 100644 --- a/src/lmpfftsettings.h +++ b/src/lmpfftsettings.h @@ -39,6 +39,8 @@ #define LMP_FFT_LIB "FFTW3" #elif defined(FFT_MKL) #define LMP_FFT_LIB "MKL FFT" +#elif defined(FFT_MKL_GPU) +#define LMP_FFT_LIB "MKL GPU FFT" #elif defined(FFT_CUFFT) #define LMP_FFT_LIB "cuFFT" #elif defined(FFT_HIPFFT) From 0c753d92ba4f60bc436d5b27ddf783788018ea8b Mon Sep 17 00:00:00 2001 From: Chris Knight Date: Thu, 29 Aug 2024 18:21:26 +0000 Subject: [PATCH 128/355] single & double support ; fix plan bug --- src/KOKKOS/fft3d_kokkos.cpp | 4 ++-- src/KOKKOS/fft3d_kokkos.h | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/KOKKOS/fft3d_kokkos.cpp b/src/KOKKOS/fft3d_kokkos.cpp index 87ed73288d..e555cfeabb 100644 --- a/src/KOKKOS/fft3d_kokkos.cpp +++ b/src/KOKKOS/fft3d_kokkos.cpp @@ -643,14 +643,14 @@ struct fft_plan_3d_kokkos* FFT3dKokkos::fft_3d_create_pl plan->desc_mid = new descriptor_t (nmid); plan->desc_mid->set_value(oneapi::mkl::dft::config_param::BACKWARD_SCALE, (FFT_SCALAR)(1.0/nmid)); - plan->desc_mid->set_value(oneapi::mkl::dft::config_param::NUMBER_OF_TRANSFORMS, plan->total1/nmid); + plan->desc_mid->set_value(oneapi::mkl::dft::config_param::NUMBER_OF_TRANSFORMS, plan->total2/nmid); plan->desc_mid->set_value(oneapi::mkl::dft::config_param::FWD_DISTANCE, nmid); plan->desc_mid->set_value(oneapi::mkl::dft::config_param::BWD_DISTANCE, nmid); plan->desc_mid->commit(queue); plan->desc_slow = new descriptor_t (nslow); plan->desc_slow->set_value(oneapi::mkl::dft::config_param::BACKWARD_SCALE, (FFT_SCALAR)(1.0/nslow)); - plan->desc_slow->set_value(oneapi::mkl::dft::config_param::NUMBER_OF_TRANSFORMS, plan->total1/nslow); + plan->desc_slow->set_value(oneapi::mkl::dft::config_param::NUMBER_OF_TRANSFORMS, plan->total3/nslow); plan->desc_slow->set_value(oneapi::mkl::dft::config_param::FWD_DISTANCE, nslow); plan->desc_slow->set_value(oneapi::mkl::dft::config_param::BWD_DISTANCE, nslow); plan->desc_slow->commit(queue); diff --git a/src/KOKKOS/fft3d_kokkos.h b/src/KOKKOS/fft3d_kokkos.h index 6c8296d399..69217171f5 100644 --- a/src/KOKKOS/fft3d_kokkos.h +++ b/src/KOKKOS/fft3d_kokkos.h @@ -20,9 +20,13 @@ #include "fftdata_kokkos.h" namespace LAMMPS_NS { - + +#ifdef FFT_SINGLE + typedef oneapi::mkl::dft::descriptor descriptor_t; +#else typedef oneapi::mkl::dft::descriptor descriptor_t; - +#endif + // ------------------------------------------------------------------------- // plan for how to perform a 3d FFT From 571076a5a7dfa3127218ad97cc000a2df76a14cc Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Thu, 29 Aug 2024 14:18:08 -0600 Subject: [PATCH 129/355] Gracefully error out if integer overflow in ReaxFF or QEq --- src/KOKKOS/fix_qeq_reaxff_kokkos.cpp | 9 ++++++--- src/KOKKOS/fix_qeq_reaxff_kokkos.h | 6 +++--- src/KOKKOS/pair_reaxff_kokkos.cpp | 8 ++++++++ src/QEQ/fix_qeq.cpp | 8 ++++++-- src/REAXFF/fix_qeq_reaxff.cpp | 8 ++++++-- src/REAXFF/reaxff_allocate.cpp | 26 ++++++++++++++++++++------ 6 files changed, 49 insertions(+), 16 deletions(-) diff --git a/src/KOKKOS/fix_qeq_reaxff_kokkos.cpp b/src/KOKKOS/fix_qeq_reaxff_kokkos.cpp index deb41944bc..7fe0285aec 100644 --- a/src/KOKKOS/fix_qeq_reaxff_kokkos.cpp +++ b/src/KOKKOS/fix_qeq_reaxff_kokkos.cpp @@ -301,7 +301,7 @@ void FixQEqReaxFFKokkos::pre_force(int /*vflag*/) template KOKKOS_INLINE_FUNCTION -void FixQEqReaxFFKokkos::num_neigh_item(int ii, int &maxneigh) const +void FixQEqReaxFFKokkos::num_neigh_item(int ii, bigint &maxneigh) const { const int i = d_ilist[ii]; maxneigh += d_numneigh[i]; @@ -316,13 +316,16 @@ void FixQEqReaxFFKokkos::allocate_matrix() // determine the total space for the H matrix - m_cap = 0; + bigint m_cap_big = 0; // limit scope of functor to allow deallocation of views { FixQEqReaxFFKokkosNumNeighFunctor neigh_functor(this); - Kokkos::parallel_reduce(nn,neigh_functor,m_cap); + Kokkos::parallel_reduce(nn,neigh_functor,m_cap_big); } + if (m_cap_big > MAXSMALLINT) + error->one(FLERR,"Number of neighbors too large in fix qeq/reaxff"); + m_cap = m_cap_big; // deallocate first to reduce memory overhead diff --git a/src/KOKKOS/fix_qeq_reaxff_kokkos.h b/src/KOKKOS/fix_qeq_reaxff_kokkos.h index 92026b209d..4bc5f8385b 100644 --- a/src/KOKKOS/fix_qeq_reaxff_kokkos.h +++ b/src/KOKKOS/fix_qeq_reaxff_kokkos.h @@ -70,7 +70,7 @@ class FixQEqReaxFFKokkos : public FixQEqReaxFF, public KokkosBase { void pre_force(int) override; KOKKOS_INLINE_FUNCTION - void num_neigh_item(int, int&) const; + void num_neigh_item(int, bigint&) const; KOKKOS_INLINE_FUNCTION void operator()(TagQEqZero, const int&) const; @@ -290,13 +290,13 @@ class FixQEqReaxFFKokkos : public FixQEqReaxFF, public KokkosBase { template struct FixQEqReaxFFKokkosNumNeighFunctor { typedef DeviceType device_type; - typedef int value_type; + typedef bigint value_type; FixQEqReaxFFKokkos c; FixQEqReaxFFKokkosNumNeighFunctor(FixQEqReaxFFKokkos* c_ptr):c(*c_ptr) { c.cleanup_copy(); }; KOKKOS_INLINE_FUNCTION - void operator()(const int ii, int &maxneigh) const { + void operator()(const int ii, bigint &maxneigh) const { c.num_neigh_item(ii, maxneigh); } }; diff --git a/src/KOKKOS/pair_reaxff_kokkos.cpp b/src/KOKKOS/pair_reaxff_kokkos.cpp index 7dd86e07a9..6ff955e6d8 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.cpp +++ b/src/KOKKOS/pair_reaxff_kokkos.cpp @@ -1504,10 +1504,18 @@ void PairReaxFFKokkos::allocate_array() if (cut_hbsq > 0.0) { MemKK::realloc_kokkos(d_hb_first,"reaxff/kk:hb_first",nmax); MemKK::realloc_kokkos(d_hb_num,"reaxff/kk:hb_num",nmax); + + if (((bigint) nmax*maxhb) > MAXSMALLINT) + error->one(FLERR,"Too many hydrogen bonds in pair reaxff"); + MemKK::realloc_kokkos(d_hb_list,"reaxff/kk:hb_list",nmax*maxhb); } MemKK::realloc_kokkos(d_bo_first,"reaxff/kk:bo_first",nmax); MemKK::realloc_kokkos(d_bo_num,"reaxff/kk:bo_num",nmax); + + if (((bigint) nmax*maxbo) > MAXSMALLINT) + error->one(FLERR,"Too many bonds in pair reaxff"); + MemKK::realloc_kokkos(d_bo_list,"reaxff/kk:bo_list",nmax*maxbo); MemKK::realloc_kokkos(d_BO,"reaxff/kk:BO",nmax,maxbo); diff --git a/src/QEQ/fix_qeq.cpp b/src/QEQ/fix_qeq.cpp index 0e1335282c..7f0ac6200e 100644 --- a/src/QEQ/fix_qeq.cpp +++ b/src/QEQ/fix_qeq.cpp @@ -237,8 +237,9 @@ void FixQEq::reallocate_storage() void FixQEq::allocate_matrix() { - int i,ii,inum,m; + int i,ii,inum; int *ilist, *numneigh; + bigint m; int mincap; double safezone; @@ -261,7 +262,10 @@ void FixQEq::allocate_matrix() i = ilist[ii]; m += numneigh[i]; } - m_cap = MAX((int)(m * safezone), mincap * MIN_NBRS); + bigint m_cap_big = MAX((int)(m * safezone), mincap * MIN_NBRS); + if (m_cap_big > MAXSMALLINT) + error->one(FLERR,"Number of neighbors too large in fix qeq/reaxff"); + m_cap = m_cap_big; H.n = n_cap; H.m = m_cap; diff --git a/src/REAXFF/fix_qeq_reaxff.cpp b/src/REAXFF/fix_qeq_reaxff.cpp index 7e935fd6cd..eaeb7f788c 100644 --- a/src/REAXFF/fix_qeq_reaxff.cpp +++ b/src/REAXFF/fix_qeq_reaxff.cpp @@ -338,7 +338,8 @@ void FixQEqReaxFF::reallocate_storage() void FixQEqReaxFF::allocate_matrix() { - int i,ii,m; + int i,ii; + bigint m; int mincap; double safezone; @@ -360,7 +361,10 @@ void FixQEqReaxFF::allocate_matrix() i = ilist[ii]; m += numneigh[i]; } - m_cap = MAX((int)(m * safezone), mincap * REAX_MIN_NBRS); + bigint m_cap_big = MAX((int)(m * safezone), mincap * REAX_MIN_NBRS); + if (m_cap_big > MAXSMALLINT) + error->one(FLERR,"Number of neighbors too large in fix qeq/reaxff"); + m_cap = m_cap_big; H.n = n_cap; H.m = m_cap; diff --git a/src/REAXFF/reaxff_allocate.cpp b/src/REAXFF/reaxff_allocate.cpp index 06ebc20f30..04940064f0 100644 --- a/src/REAXFF/reaxff_allocate.cpp +++ b/src/REAXFF/reaxff_allocate.cpp @@ -169,16 +169,23 @@ namespace ReaxFF { static int Reallocate_HBonds_List(reax_system *system, reax_list *hbonds) { int i, total_hbonds; + LAMMPS_NS::bigint total_hbonds_big; int mincap = system->mincap; double saferzone = system->saferzone; - total_hbonds = 0; + total_hbonds_big = 0; for (i = 0; i < system->n; ++i) if ((system->my_atoms[i].Hindex) >= 0) { - total_hbonds += system->my_atoms[i].num_hbonds; + total_hbonds_big += system->my_atoms[i].num_hbonds; } - total_hbonds = (int)(MAX(total_hbonds*saferzone, mincap*system->minhbonds)); + total_hbonds_big = (int)(MAX(total_hbonds_big*saferzone, mincap*system->minhbonds)); + + auto error = system->error_ptr; + if (total_hbonds_big > MAXSMALLINT) + error->one(FLERR,"Too many hydrogen bonds in pair reaxff"); + + total_hbonds = total_hbonds_big; Delete_List(hbonds); Make_List(system->Hcap, total_hbonds, TYP_HBOND, hbonds); @@ -190,17 +197,24 @@ namespace ReaxFF { reax_list *bonds, int *total_bonds, int *est_3body) { int i; + LAMMPS_NS::bigint total_bonds_big; int mincap = system->mincap; double safezone = system->safezone; - *total_bonds = 0; + total_bonds_big = 0; *est_3body = 0; for (i = 0; i < system->N; ++i) { *est_3body += SQR(system->my_atoms[i].num_bonds); - *total_bonds += system->my_atoms[i].num_bonds; + total_bonds_big += system->my_atoms[i].num_bonds; } - *total_bonds = (int)(MAX(*total_bonds * safezone, mincap*MIN_BONDS)); + total_bonds_big = (int)(MAX(total_bonds_big * safezone, mincap*MIN_BONDS)); + + auto error = system->error_ptr; + if (total_bonds_big > MAXSMALLINT) + error->one(FLERR,"Too many bonds in pair reaxff"); + + *total_bonds = total_bonds_big; if (system->omp_active) for (i = 0; i < bonds->num_intrs; ++i) From 6fd022695aa071dc4a6462b1e934f935b54e4aed Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Thu, 29 Aug 2024 14:23:49 -0600 Subject: [PATCH 130/355] Tweak error message --- src/KOKKOS/fix_qeq_reaxff_kokkos.cpp | 2 +- src/QEQ/fix_qeq.cpp | 2 +- src/REAXFF/fix_qeq_reaxff.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/KOKKOS/fix_qeq_reaxff_kokkos.cpp b/src/KOKKOS/fix_qeq_reaxff_kokkos.cpp index 7fe0285aec..6527ed9314 100644 --- a/src/KOKKOS/fix_qeq_reaxff_kokkos.cpp +++ b/src/KOKKOS/fix_qeq_reaxff_kokkos.cpp @@ -324,7 +324,7 @@ void FixQEqReaxFFKokkos::allocate_matrix() Kokkos::parallel_reduce(nn,neigh_functor,m_cap_big); } if (m_cap_big > MAXSMALLINT) - error->one(FLERR,"Number of neighbors too large in fix qeq/reaxff"); + error->one(FLERR,"Too many neighbors in fix qeq/reaxff"); m_cap = m_cap_big; // deallocate first to reduce memory overhead diff --git a/src/QEQ/fix_qeq.cpp b/src/QEQ/fix_qeq.cpp index 7f0ac6200e..e9238c6065 100644 --- a/src/QEQ/fix_qeq.cpp +++ b/src/QEQ/fix_qeq.cpp @@ -264,7 +264,7 @@ void FixQEq::allocate_matrix() } bigint m_cap_big = MAX((int)(m * safezone), mincap * MIN_NBRS); if (m_cap_big > MAXSMALLINT) - error->one(FLERR,"Number of neighbors too large in fix qeq/reaxff"); + error->one(FLERR,"Too many neighbors in fix qeq"); m_cap = m_cap_big; H.n = n_cap; diff --git a/src/REAXFF/fix_qeq_reaxff.cpp b/src/REAXFF/fix_qeq_reaxff.cpp index eaeb7f788c..29b1149773 100644 --- a/src/REAXFF/fix_qeq_reaxff.cpp +++ b/src/REAXFF/fix_qeq_reaxff.cpp @@ -363,7 +363,7 @@ void FixQEqReaxFF::allocate_matrix() } bigint m_cap_big = MAX((int)(m * safezone), mincap * REAX_MIN_NBRS); if (m_cap_big > MAXSMALLINT) - error->one(FLERR,"Number of neighbors too large in fix qeq/reaxff"); + error->one(FLERR,"Too many neighbors in fix qeq/reaxff"); m_cap = m_cap_big; H.n = n_cap; From 7a9f0cd60d5262665d92c546052485500f84f096 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Thu, 29 Aug 2024 14:40:50 -0600 Subject: [PATCH 131/355] Fix potential overflow in Kokkos neighbor counting --- src/KOKKOS/kokkos.cpp | 4 ++-- src/KOKKOS/kokkos.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/KOKKOS/kokkos.cpp b/src/KOKKOS/kokkos.cpp index f4ba967c9a..8019925c6f 100644 --- a/src/KOKKOS/kokkos.cpp +++ b/src/KOKKOS/kokkos.cpp @@ -638,10 +638,10 @@ void KokkosLMP::accelerator(int narg, char **arg) called by Finish ------------------------------------------------------------------------- */ -int KokkosLMP::neigh_count(int m) +bigint KokkosLMP::neigh_count(int m) { int inum = 0; - int nneigh = 0; + bigint nneigh = 0; ArrayTypes::t_int_1d h_ilist; ArrayTypes::t_int_1d h_numneigh; diff --git a/src/KOKKOS/kokkos.h b/src/KOKKOS/kokkos.h index 748aff7f83..419de62dec 100644 --- a/src/KOKKOS/kokkos.h +++ b/src/KOKKOS/kokkos.h @@ -64,7 +64,7 @@ class KokkosLMP : protected Pointers { static void initialize(const Kokkos::InitializationSettings&, Error *); static void finalize(); void accelerator(int, char **); - int neigh_count(int); + bigint neigh_count(int); template int need_dup(int qeq_flag = 0) From 4aca808da69757c0f7bc2bbb05191587a7f7465d Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Thu, 29 Aug 2024 17:40:38 -0600 Subject: [PATCH 132/355] Casts need to be bigint --- src/QEQ/fix_qeq.cpp | 2 +- src/REAXFF/fix_qeq_reaxff.cpp | 2 +- src/REAXFF/reaxff_allocate.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/QEQ/fix_qeq.cpp b/src/QEQ/fix_qeq.cpp index e9238c6065..52ab20c9e1 100644 --- a/src/QEQ/fix_qeq.cpp +++ b/src/QEQ/fix_qeq.cpp @@ -262,7 +262,7 @@ void FixQEq::allocate_matrix() i = ilist[ii]; m += numneigh[i]; } - bigint m_cap_big = MAX((int)(m * safezone), mincap * MIN_NBRS); + bigint m_cap_big = (bigint)MAX(m * safezone, mincap * MIN_NBRS); if (m_cap_big > MAXSMALLINT) error->one(FLERR,"Too many neighbors in fix qeq"); m_cap = m_cap_big; diff --git a/src/REAXFF/fix_qeq_reaxff.cpp b/src/REAXFF/fix_qeq_reaxff.cpp index 29b1149773..adaf5be031 100644 --- a/src/REAXFF/fix_qeq_reaxff.cpp +++ b/src/REAXFF/fix_qeq_reaxff.cpp @@ -361,7 +361,7 @@ void FixQEqReaxFF::allocate_matrix() i = ilist[ii]; m += numneigh[i]; } - bigint m_cap_big = MAX((int)(m * safezone), mincap * REAX_MIN_NBRS); + bigint m_cap_big = (bigint)MAX(m * safezone, mincap * REAX_MIN_NBRS); if (m_cap_big > MAXSMALLINT) error->one(FLERR,"Too many neighbors in fix qeq/reaxff"); m_cap = m_cap_big; diff --git a/src/REAXFF/reaxff_allocate.cpp b/src/REAXFF/reaxff_allocate.cpp index 04940064f0..9e125bea05 100644 --- a/src/REAXFF/reaxff_allocate.cpp +++ b/src/REAXFF/reaxff_allocate.cpp @@ -179,7 +179,7 @@ namespace ReaxFF { if ((system->my_atoms[i].Hindex) >= 0) { total_hbonds_big += system->my_atoms[i].num_hbonds; } - total_hbonds_big = (int)(MAX(total_hbonds_big*saferzone, mincap*system->minhbonds)); + total_hbonds_big = (LAMMPS_NS::bigint)(MAX(total_hbonds_big*saferzone, mincap*system->minhbonds)); auto error = system->error_ptr; if (total_hbonds_big > MAXSMALLINT) @@ -208,7 +208,7 @@ namespace ReaxFF { *est_3body += SQR(system->my_atoms[i].num_bonds); total_bonds_big += system->my_atoms[i].num_bonds; } - total_bonds_big = (int)(MAX(total_bonds_big * safezone, mincap*MIN_BONDS)); + total_bonds_big = (LAMMPS_NS::bigint)(MAX(total_bonds_big * safezone, mincap*MIN_BONDS)); auto error = system->error_ptr; if (total_bonds_big > MAXSMALLINT) From 02f16251d41720a6477d211511aa6afc5da81715 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 29 Aug 2024 22:09:11 -0400 Subject: [PATCH 133/355] flag development version --- src/version.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/version.h b/src/version.h index 7ef4ade45e..9c382b3768 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1,2 @@ #define LAMMPS_VERSION "29 Aug 2024" +#define LAMMPS_UPDATE "Development" From 6b1aec981e1663184e5ad847ff5df9c18e8638c2 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 29 Aug 2024 22:33:40 -0400 Subject: [PATCH 134/355] exceptions are always enabled now --- tools/lammps-gui/CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tools/lammps-gui/CMakeLists.txt b/tools/lammps-gui/CMakeLists.txt index 1dfd8f451d..46f36cdb13 100644 --- a/tools/lammps-gui/CMakeLists.txt +++ b/tools/lammps-gui/CMakeLists.txt @@ -86,9 +86,7 @@ else() add_compile_options(/wd4244) add_compile_options(/wd4267) add_compile_options(/wd4250) - if(LAMMPS_EXCEPTIONS) - add_compile_options(/EHsc) - endif() + add_compile_options(/EHsc) endif() add_compile_definitions(_CRT_SECURE_NO_WARNINGS) endif() From f3f69da328a7a75597894447ffbce59a3c102171 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 30 Aug 2024 10:29:41 -0400 Subject: [PATCH 135/355] expand tests on properties including custom properties --- .../c-library/test_library_properties.cpp | 84 +++++++++++++++++-- 1 file changed, 75 insertions(+), 9 deletions(-) diff --git a/unittest/c-library/test_library_properties.cpp b/unittest/c-library/test_library_properties.cpp index 3d0eeef5ea..1d27075ada 100644 --- a/unittest/c-library/test_library_properties.cpp +++ b/unittest/c-library/test_library_properties.cpp @@ -49,6 +49,7 @@ protected: if (verbose) std::cout << output; EXPECT_THAT(output, StartsWith("LAMMPS (")); } + void TearDown() override { ::testing::internal::CaptureStdout(); @@ -470,9 +471,9 @@ TEST_F(LibraryProperties, global) EXPECT_EQ(lammps_extract_global_datatype(lmp, "xlattice"), LAMMPS_DOUBLE); EXPECT_EQ(lammps_extract_global_datatype(lmp, "ylattice"), LAMMPS_DOUBLE); EXPECT_EQ(lammps_extract_global_datatype(lmp, "zlattice"), LAMMPS_DOUBLE); - auto *xlattice = (double *)lammps_extract_global(lmp, "xlattice"); - auto *ylattice = (double *)lammps_extract_global(lmp, "ylattice"); - auto *zlattice = (double *)lammps_extract_global(lmp, "zlattice"); + auto *xlattice = (double *)lammps_extract_global(lmp, "xlattice"); + auto *ylattice = (double *)lammps_extract_global(lmp, "ylattice"); + auto *zlattice = (double *)lammps_extract_global(lmp, "zlattice"); EXPECT_NE(xlattice, nullptr); EXPECT_NE(ylattice, nullptr); EXPECT_NE(zlattice, nullptr); @@ -484,9 +485,9 @@ TEST_F(LibraryProperties, global) lammps_command(lmp, "units real"); lammps_command(lmp, "lattice fcc 2.0"); if (!verbose) ::testing::internal::GetCapturedStdout(); - xlattice = (double *)lammps_extract_global(lmp, "xlattice"); - ylattice = (double *)lammps_extract_global(lmp, "ylattice"); - zlattice = (double *)lammps_extract_global(lmp, "zlattice"); + xlattice = (double *)lammps_extract_global(lmp, "xlattice"); + ylattice = (double *)lammps_extract_global(lmp, "ylattice"); + zlattice = (double *)lammps_extract_global(lmp, "zlattice"); EXPECT_NE(xlattice, nullptr); EXPECT_NE(ylattice, nullptr); EXPECT_NE(zlattice, nullptr); @@ -695,10 +696,8 @@ class AtomProperties : public ::testing::Test { protected: void *lmp; - AtomProperties() = default; - ; + AtomProperties() = default; ~AtomProperties() override = default; - ; void SetUp() override { @@ -713,11 +712,26 @@ protected: if (verbose) std::cout << output; EXPECT_THAT(output, StartsWith("LAMMPS (")); ::testing::internal::CaptureStdout(); + lammps_command(lmp, "fix props all property/atom i_one i2_two 2 d_three d2_four 2"); + lammps_command(lmp, "fix rmass all property/atom mol q rmass ghost yes"); lammps_command(lmp, "region box block 0 2 0 2 0 2"); lammps_command(lmp, "create_box 1 box"); lammps_command(lmp, "mass 1 3.0"); lammps_command(lmp, "create_atoms 1 single 1.0 1.0 1.5"); lammps_command(lmp, "create_atoms 1 single 0.2 0.1 0.1"); + lammps_command(lmp, "set group all mass 2.0"); + lammps_command(lmp, "set atom 1 charge -1"); + lammps_command(lmp, "set atom 2 charge 1"); + lammps_command(lmp, "set atom 1 mol 2"); + lammps_command(lmp, "set atom 2 mol 1"); + lammps_command(lmp, "set atom 1 i_one -3"); + lammps_command(lmp, "set atom 2 i_one 3"); + lammps_command(lmp, "set atom 1 d_three -1.3"); + lammps_command(lmp, "set atom 2 d_three 3.5"); + lammps_command(lmp, "set atom 1 i_two[1] -3"); + lammps_command(lmp, "set atom 2 i_two[2] 3"); + lammps_command(lmp, "set atom * d_four[1] -1.3"); + lammps_command(lmp, "set atom * d_four[2] 3.5"); output = ::testing::internal::GetCapturedStdout(); if (verbose) std::cout << output; } @@ -743,6 +757,29 @@ TEST_F(AtomProperties, mass) auto *mass = (double *)lammps_extract_atom(lmp, "mass"); ASSERT_NE(mass, nullptr); ASSERT_DOUBLE_EQ(mass[1], 3.0); + EXPECT_EQ(lammps_extract_atom_datatype(lmp, "rmass"), LAMMPS_DOUBLE); + mass = (double *)lammps_extract_atom(lmp, "rmass"); + ASSERT_NE(mass, nullptr); + ASSERT_DOUBLE_EQ(mass[0], 2.0); + ASSERT_DOUBLE_EQ(mass[1], 2.0); +} + +TEST_F(AtomProperties, charge) +{ + EXPECT_EQ(lammps_extract_atom_datatype(lmp, "q"), LAMMPS_DOUBLE); + auto *charge = (double *)lammps_extract_atom(lmp, "q"); + ASSERT_NE(charge, nullptr); + ASSERT_DOUBLE_EQ(charge[0], -1.0); + ASSERT_DOUBLE_EQ(charge[1], 1.0); +} + +TEST_F(AtomProperties, molecule) +{ + EXPECT_EQ(lammps_extract_atom_datatype(lmp, "molecule"), LAMMPS_TAGINT); + auto *molecule = (tagint *)lammps_extract_atom(lmp, "molecule"); + ASSERT_NE(molecule, nullptr); + ASSERT_EQ(molecule[0], 2); + ASSERT_EQ(molecule[1], 1); } TEST_F(AtomProperties, id) @@ -776,6 +813,35 @@ TEST_F(AtomProperties, position) EXPECT_DOUBLE_EQ(x[1][2], 0.1); } +TEST_F(AtomProperties, custom) +{ + EXPECT_EQ(lammps_extract_atom_datatype(lmp, "i_one"), LAMMPS_INT); + auto *one = (int *)lammps_extract_atom(lmp, "i_one"); + ASSERT_NE(one, nullptr); + EXPECT_EQ(lammps_extract_atom_datatype(lmp, "i2_two"), LAMMPS_INT_2D); + auto **two = (int **)lammps_extract_atom(lmp, "i2_two"); + ASSERT_NE(two, nullptr); + EXPECT_EQ(lammps_extract_atom_datatype(lmp, "d_three"), LAMMPS_DOUBLE); + auto *three = (double *)lammps_extract_atom(lmp, "d_three"); + ASSERT_NE(three, nullptr); + EXPECT_EQ(lammps_extract_atom_datatype(lmp, "d2_four"), LAMMPS_DOUBLE_2D); + auto **four = (double **)lammps_extract_atom(lmp, "d2_four"); + ASSERT_NE(four, nullptr); + + EXPECT_EQ(one[0], -3); + EXPECT_EQ(one[1], 3); + EXPECT_EQ(two[0][0], -3); + EXPECT_EQ(two[0][1], 0); + EXPECT_EQ(two[1][0], 0); + EXPECT_EQ(two[1][1], 3); + EXPECT_DOUBLE_EQ(three[0], -1.3); + EXPECT_DOUBLE_EQ(three[1], 3.5); + EXPECT_DOUBLE_EQ(four[0][0], -1.3); + EXPECT_DOUBLE_EQ(four[0][1], 3.5); + EXPECT_DOUBLE_EQ(four[1][0], -1.3); + EXPECT_DOUBLE_EQ(four[1][1], 3.5); +} + TEST(SystemSettings, kokkos) { if (!lammps_config_has_package("KOKKOS")) GTEST_SKIP(); From e921af8efa934731edeadb41f30ac283341bc3e3 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 30 Aug 2024 10:30:04 -0400 Subject: [PATCH 136/355] fix bug where custom property dimension was not reported --- src/atom.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/atom.cpp b/src/atom.cpp index 52cc2c9bc9..4151074a40 100644 --- a/src/atom.cpp +++ b/src/atom.cpp @@ -3248,14 +3248,16 @@ int Atom::extract_datatype(const char *name) if (!array) index = find_custom(&name[2],flag,cols); else index = find_custom(&name[3],flag,cols); + // consistency checks if (index < 0) return -1; if (which != flag) return -1; if ((!array && cols) || (array && !cols)) return -1; - if (which == 0) return LAMMPS_INT; - else return LAMMPS_DOUBLE; + if (!which && !array) return LAMMPS_INT; + if (which && !array) return LAMMPS_DOUBLE; + if (!which && array) return LAMMPS_INT_2D; + if (which && array) return LAMMPS_DOUBLE_2D; } - return -1; } From d9e6dff93b0bf8a4b6d6ac3f5925cf411186390f Mon Sep 17 00:00:00 2001 From: cjknight Date: Fri, 30 Aug 2024 14:05:36 -0500 Subject: [PATCH 137/355] cleanup --- src/KOKKOS/fft3d_kokkos.cpp | 19 +++++++------------ src/KOKKOS/fft3d_kokkos.h | 2 ++ src/KOKKOS/fftdata_kokkos.h | 2 +- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/KOKKOS/fft3d_kokkos.cpp b/src/KOKKOS/fft3d_kokkos.cpp index e555cfeabb..8256c5f4ee 100644 --- a/src/KOKKOS/fft3d_kokkos.cpp +++ b/src/KOKKOS/fft3d_kokkos.cpp @@ -155,14 +155,12 @@ public: KOKKOS_INLINE_FUNCTION void operator() (const int &i) const { -#if defined(FFT_KOKKOS_FFTW3) || defined(FFT_KOKKOS_CUFFT) || defined(FFT_KOKKOS_HIPFFT) +#if defined(FFT_KOKKOS_FFTW3) || defined(FFT_KOKKOS_CUFFT) || defined(FFT_KOKKOS_HIPFFT) || defined(FFT_KOKKOS_MKL_GPU) FFT_SCALAR* out_ptr = (FFT_SCALAR *)(d_out.data()+i); *(out_ptr++) *= norm; *(out_ptr++) *= norm; #elif defined(FFT_KOKKOS_MKL) d_out(i) *= norm; -#elif defined(FFT_KOKKOS_MKL_GPU) - d_out(i) *= norm; #else // FFT_KOKKOS_KISS d_out(i).re *= norm; d_out(i).im *= norm; @@ -635,24 +633,21 @@ struct fft_plan_3d_kokkos* FFT3dKokkos::fft_3d_create_pl sycl::queue queue = LMPDeviceType().sycl_queue(); // is this the correct queue? plan->desc_fast = new descriptor_t (nfast); - plan->desc_fast->set_value(oneapi::mkl::dft::config_param::BACKWARD_SCALE, (FFT_SCALAR)(1.0/nfast)); plan->desc_fast->set_value(oneapi::mkl::dft::config_param::NUMBER_OF_TRANSFORMS, plan->total1/nfast); - plan->desc_fast->set_value(oneapi::mkl::dft::config_param::FWD_DISTANCE, nfast); - plan->desc_fast->set_value(oneapi::mkl::dft::config_param::BWD_DISTANCE, nfast); + plan->desc_fast->set_value(oneapi::mkl::dft::config_param::FWD_DISTANCE, plan->length1); + plan->desc_fast->set_value(oneapi::mkl::dft::config_param::BWD_DISTANCE, plan->length1); plan->desc_fast->commit(queue); plan->desc_mid = new descriptor_t (nmid); - plan->desc_mid->set_value(oneapi::mkl::dft::config_param::BACKWARD_SCALE, (FFT_SCALAR)(1.0/nmid)); plan->desc_mid->set_value(oneapi::mkl::dft::config_param::NUMBER_OF_TRANSFORMS, plan->total2/nmid); - plan->desc_mid->set_value(oneapi::mkl::dft::config_param::FWD_DISTANCE, nmid); - plan->desc_mid->set_value(oneapi::mkl::dft::config_param::BWD_DISTANCE, nmid); + plan->desc_mid->set_value(oneapi::mkl::dft::config_param::FWD_DISTANCE, plan->length2); + plan->desc_mid->set_value(oneapi::mkl::dft::config_param::BWD_DISTANCE, plan->length2); plan->desc_mid->commit(queue); plan->desc_slow = new descriptor_t (nslow); - plan->desc_slow->set_value(oneapi::mkl::dft::config_param::BACKWARD_SCALE, (FFT_SCALAR)(1.0/nslow)); plan->desc_slow->set_value(oneapi::mkl::dft::config_param::NUMBER_OF_TRANSFORMS, plan->total3/nslow); - plan->desc_slow->set_value(oneapi::mkl::dft::config_param::FWD_DISTANCE, nslow); - plan->desc_slow->set_value(oneapi::mkl::dft::config_param::BWD_DISTANCE, nslow); + plan->desc_slow->set_value(oneapi::mkl::dft::config_param::FWD_DISTANCE, plan->length3); + plan->desc_slow->set_value(oneapi::mkl::dft::config_param::BWD_DISTANCE, plan->length3); plan->desc_slow->commit(queue); #elif defined(FFT_KOKKOS_MKL) diff --git a/src/KOKKOS/fft3d_kokkos.h b/src/KOKKOS/fft3d_kokkos.h index 69217171f5..427e33ad73 100644 --- a/src/KOKKOS/fft3d_kokkos.h +++ b/src/KOKKOS/fft3d_kokkos.h @@ -21,11 +21,13 @@ namespace LAMMPS_NS { +#if defined(FFT_KOKKOS_MKL_GPU) #ifdef FFT_SINGLE typedef oneapi::mkl::dft::descriptor descriptor_t; #else typedef oneapi::mkl::dft::descriptor descriptor_t; #endif +#endif // ------------------------------------------------------------------------- diff --git a/src/KOKKOS/fftdata_kokkos.h b/src/KOKKOS/fftdata_kokkos.h index 90d917c093..f11e7f1509 100644 --- a/src/KOKKOS/fftdata_kokkos.h +++ b/src/KOKKOS/fftdata_kokkos.h @@ -110,7 +110,7 @@ #if defined(FFT_KOKKOS_MKL_GPU) #include "CL/sycl.hpp" - #include "oneapi/mkl/dfti.hpp" // conflict between PRECISION macro in dfti.hpp and kokkos_type.h + #include "oneapi/mkl/dfti.hpp" #include "mkl.h" #if defined(FFT_SINGLE) typedef float FFT_KOKKOS_DATA; From 9a1157083668f10a5659d7e90d8e2ee7cff3aa8e Mon Sep 17 00:00:00 2001 From: Chris Knight Date: Fri, 30 Aug 2024 19:08:35 +0000 Subject: [PATCH 138/355] cleanup --- src/MAKE/MACHINES/Makefile.aurora | 2 +- src/MAKE/MACHINES/Makefile.aurora_kokkos | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MAKE/MACHINES/Makefile.aurora b/src/MAKE/MACHINES/Makefile.aurora index 78c08b8e95..7c656e6379 100644 --- a/src/MAKE/MACHINES/Makefile.aurora +++ b/src/MAKE/MACHINES/Makefile.aurora @@ -1,4 +1,4 @@ -# aurora_kokkos = KOKKOS/SYCL, Intel Data Center Max (Ponte Vecchio) GPU, Intel Sapphire Rapids CPU, mpicxx compiler +# aurora = Intel Sapphire Rapids CPU, mpicxx compiler (compatible w/ GPU package) SHELL = /bin/sh diff --git a/src/MAKE/MACHINES/Makefile.aurora_kokkos b/src/MAKE/MACHINES/Makefile.aurora_kokkos index a9000bcfd6..f164188dc0 100644 --- a/src/MAKE/MACHINES/Makefile.aurora_kokkos +++ b/src/MAKE/MACHINES/Makefile.aurora_kokkos @@ -54,7 +54,7 @@ MPI_LIB = # PATH = path for FFT library # LIB = name of FFT library -FFT_INC = -DFFT_KOKKOS_MKL_GPU -I${MKL_ROOT}/include +FFT_INC = -DFFT_KOKKOS_MKL_GPU -DFFT_SINGLE -I${MKL_ROOT}/include FFT_PATH = -L${MKL_ROOT}/lib -L${TBB_ROOT}/lib/intel64/gcc4.8 FFT_LIB = -lmkl_sycl_dft -lmkl_intel_ilp64 -lmkl_tbb_thread -lmkl_core -ltbb From 1fe905d8a229e18c18c34e18a87a69714b373226 Mon Sep 17 00:00:00 2001 From: cjknight Date: Fri, 30 Aug 2024 15:15:18 -0500 Subject: [PATCH 139/355] update types --- src/KOKKOS/fft3d_kokkos.cpp | 24 ++++++++++++------------ src/KOKKOS/fftdata_kokkos.h | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/KOKKOS/fft3d_kokkos.cpp b/src/KOKKOS/fft3d_kokkos.cpp index 8256c5f4ee..09164731a3 100644 --- a/src/KOKKOS/fft3d_kokkos.cpp +++ b/src/KOKKOS/fft3d_kokkos.cpp @@ -227,9 +227,9 @@ void FFT3dKokkos::fft_3d_kokkos(typename FFT_AT::t_FFT_DATA_1d d_in, #if defined(FFT_KOKKOS_MKL_GPU) if (flag == 1) - oneapi::mkl::dft::compute_forward(*(plan->desc_fast), d_data.data()); + oneapi::mkl::dft::compute_forward(*(plan->desc_fast), (FFT_SCALAR*)d_data.data()); else - oneapi::mkl::dft::compute_backward(*(plan->desc_fast), d_data.data()); + oneapi::mkl::dft::compute_backward(*(plan->desc_fast), (FFT_SCALAR*)d_data.data()); #elif defined(FFT_KOKKOS_MKL) if (flag == 1) DftiComputeForward(plan->handle_fast,d_data.data()); @@ -278,9 +278,9 @@ void FFT3dKokkos::fft_3d_kokkos(typename FFT_AT::t_FFT_DATA_1d d_in, #if defined(FFT_KOKKOS_MKL_GPU) if (flag == 1) - oneapi::mkl::dft::compute_forward(*(plan->desc_mid), d_data.data()); + oneapi::mkl::dft::compute_forward(*(plan->desc_mid), (FFT_SCALAR*)d_data.data()); else - oneapi::mkl::dft::compute_backward(*(plan->desc_mid), d_data.data()); + oneapi::mkl::dft::compute_backward(*(plan->desc_mid), (FFT_SCALAR*)d_data.data()); #elif defined(FFT_KOKKOS_MKL) if (flag == 1) DftiComputeForward(plan->handle_mid,d_data.data()); @@ -327,9 +327,9 @@ void FFT3dKokkos::fft_3d_kokkos(typename FFT_AT::t_FFT_DATA_1d d_in, #if defined(FFT_KOKKOS_MKL_GPU) if (flag == 1) - oneapi::mkl::dft::compute_forward(*(plan->desc_slow), d_data.data()); + oneapi::mkl::dft::compute_forward(*(plan->desc_slow), (FFT_SCALAR*)d_data.data()); else - oneapi::mkl::dft::compute_backward(*(plan->desc_slow), d_data.data()); + oneapi::mkl::dft::compute_backward(*(plan->desc_slow), (FFT_SCALAR*)d_data.data()); #elif defined(FFT_KOKKOS_MKL) if (flag == 1) DftiComputeForward(plan->handle_slow,d_data.data()); @@ -914,13 +914,13 @@ void FFT3dKokkos::fft_3d_1d_only_kokkos(typename FFT_AT::t_FFT_DATA_ #if defined(FFT_KOKKOS_MKL_GPU) if (flag == -1) { - oneapi::mkl::dft::compute_forward(*(plan->desc_fast), d_data.data()); - oneapi::mkl::dft::compute_forward(*(plan->desc_mid), d_data.data()); - oneapi::mkl::dft::compute_forward(*(plan->desc_slow), d_data.data()); + oneapi::mkl::dft::compute_forward(*(plan->desc_fast), (FFT_SCALAR*)d_data.data()); + oneapi::mkl::dft::compute_forward(*(plan->desc_mid), (FFT_SCALAR*)d_data.data()); + oneapi::mkl::dft::compute_forward(*(plan->desc_slow), (FFT_SCALAR*)d_data.data()); } else { - oneapi::mkl::dft::compute_backward(*(plan->desc_fast), d_data.data()); - oneapi::mkl::dft::compute_backward(*(plan->desc_mid), d_data.data()); - oneapi::mkl::dft::compute_backward(*(plan->desc_slow), d_data.data()); + oneapi::mkl::dft::compute_backward(*(plan->desc_fast), (FFT_SCALAR*)d_data.data()); + oneapi::mkl::dft::compute_backward(*(plan->desc_mid), (FFT_SCALAR*)d_data.data()); + oneapi::mkl::dft::compute_backward(*(plan->desc_slow), (FFT_SCALAR*)d_data.data()); } #elif defined(FFT_KOKKOS_MKL) if (flag == -1) { diff --git a/src/KOKKOS/fftdata_kokkos.h b/src/KOKKOS/fftdata_kokkos.h index f11e7f1509..a967be5338 100644 --- a/src/KOKKOS/fftdata_kokkos.h +++ b/src/KOKKOS/fftdata_kokkos.h @@ -113,10 +113,10 @@ #include "oneapi/mkl/dfti.hpp" #include "mkl.h" #if defined(FFT_SINGLE) - typedef float FFT_KOKKOS_DATA; + typedef std::complex FFT_KOKKOS_DATA; #define FFT_KOKKOS_MKL_PREC DFTI_SINGLE #else - typedef double FFT_KOKKOS_DATA; + typedef std::complex FFT_KOKKOS_DATA; #define FFT_KOKKOS_MKL_PREC DFTI_DOUBLE #endif #elif defined(FFT_KOKKOS_MKL) From 9d9e591b542f104fdccaca51a4378ec32ea55491 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 30 Aug 2024 22:50:42 -0400 Subject: [PATCH 140/355] first try at implementing lammps_extract_atom_size() --- doc/src/Library_atoms.rst | 6 + examples/COUPLE/plugin/liblammpsplugin.c | 1 + examples/COUPLE/plugin/liblammpsplugin.h | 1 + fortran/lammps.f90 | 24 +- python/lammps/core.py | 68 +++-- python/lammps/numpy_wrapper.py | 69 +---- src/atom.cpp | 259 +++++++++++++++++- src/atom.h | 1 + src/library.cpp | 62 ++++- src/library.h | 1 + tools/swig/lammps.i | 2 + .../c-library/test_library_properties.cpp | 19 ++ unittest/python/python-numpy.py | 31 --- 13 files changed, 411 insertions(+), 133 deletions(-) diff --git a/doc/src/Library_atoms.rst b/doc/src/Library_atoms.rst index cebd8f0c2e..5ebfe04b37 100644 --- a/doc/src/Library_atoms.rst +++ b/doc/src/Library_atoms.rst @@ -4,6 +4,7 @@ Per-atom properties This section documents the following functions: - :cpp:func:`lammps_extract_atom_datatype` +- :cpp:func:`lammps_extract_atom_size` - :cpp:func:`lammps_extract_atom` ----------------------- @@ -13,6 +14,11 @@ This section documents the following functions: ----------------------- +.. doxygenfunction:: lammps_extract_atom_size + :project: progguide + +----------------------- + .. doxygenfunction:: lammps_extract_atom :project: progguide diff --git a/examples/COUPLE/plugin/liblammpsplugin.c b/examples/COUPLE/plugin/liblammpsplugin.c index 50ad2f5192..c8a38c21e4 100644 --- a/examples/COUPLE/plugin/liblammpsplugin.c +++ b/examples/COUPLE/plugin/liblammpsplugin.c @@ -105,6 +105,7 @@ liblammpsplugin_t *liblammpsplugin_load(const char *lib) ADDSYM(map_atom); ADDSYM(extract_atom_datatype); + ADDSYM(extract_atom_size); ADDSYM(extract_atom); ADDSYM(extract_compute); diff --git a/examples/COUPLE/plugin/liblammpsplugin.h b/examples/COUPLE/plugin/liblammpsplugin.h index 556718816c..b444f75215 100644 --- a/examples/COUPLE/plugin/liblammpsplugin.h +++ b/examples/COUPLE/plugin/liblammpsplugin.h @@ -151,6 +151,7 @@ struct _liblammpsplugin { int (*map_atom)(void *, const void *); int (*extract_atom_datatype)(void *, const char *); + int (*extract_atom_size)(void *, const char *, int); void *(*extract_atom)(void *, const char *); void *(*extract_compute)(void *, const char *, int, int); diff --git a/fortran/lammps.f90 b/fortran/lammps.f90 index 1617891b92..9688dae638 100644 --- a/fortran/lammps.f90 +++ b/fortran/lammps.f90 @@ -542,6 +542,14 @@ MODULE LIBLAMMPS INTEGER(c_int) :: lammps_extract_atom_datatype END FUNCTION lammps_extract_atom_datatype + FUNCTION lammps_extract_atom_size(handle, name, dtype) BIND(C) + IMPORT :: c_ptr, c_int + IMPLICIT NONE + TYPE(c_ptr), INTENT(IN), VALUE :: handle, name + INTEGER(c_int), INTENT(IN), VALUE :: dtype + INTEGER(c_int) :: lammps_extract_atom_size + END FUNCTION lammps_extract_atom_size + FUNCTION lammps_extract_atom(handle, name) BIND(C) IMPORT :: c_ptr IMPLICIT NONE @@ -1461,21 +1469,12 @@ CONTAINS ntypes = lmp_extract_setting(self, 'ntypes') Cname = f2c_string(name) datatype = lammps_extract_atom_datatype(self%handle, Cname) + ! Fortran and C/C++ have rows and columns switched + ncols = lammps_extract_atom_size(self%handle, Cname, LMP_SIZE_ROWS) + nrows = lammps_extract_atom_size(self%handle, Cname, LMP_SIZE_COLS) Cptr = lammps_extract_atom(self%handle, Cname) CALL lammps_free(Cname) - SELECT CASE (name) - CASE ('mass') - ncols = ntypes + 1 - nrows = 1 - CASE ('x','v','f','mu','omega','torque','angmom') - ncols = nmax - nrows = 3 - CASE DEFAULT - ncols = nmax - nrows = 1 - END SELECT - peratom_data%lammps_instance => self SELECT CASE (datatype) CASE (LAMMPS_INT) @@ -1486,6 +1485,7 @@ CONTAINS CALL C_F_POINTER(Cptr, peratom_data%i64_vec, [ncols]) CASE (LAMMPS_DOUBLE) peratom_data%datatype = DATA_DOUBLE_1D + ! The mass array is allocated from 0, but only used from 1. We also want to use it from 1. IF (name == 'mass') THEN CALL C_F_POINTER(Cptr, dummy, [ncols]) peratom_data%r64_vec(0:) => dummy diff --git a/python/lammps/core.py b/python/lammps/core.py index 9e6329fe3c..b1907888e3 100644 --- a/python/lammps/core.py +++ b/python/lammps/core.py @@ -318,6 +318,8 @@ class lammps(object): self.lib.lammps_extract_atom.argtypes = [c_void_p, c_char_p] self.lib.lammps_extract_atom_datatype.argtypes = [c_void_p, c_char_p] self.lib.lammps_extract_atom_datatype.restype = c_int + self.lib.lammps_extract_atom_size.argtypes = [c_void_p, c_char_p, c_int] + self.lib.lammps_extract_atom_size.restype = c_int self.lib.lammps_extract_fix.argtypes = [c_void_p, c_char_p, c_int, c_int, c_int, c_int] @@ -1070,31 +1072,59 @@ class lammps(object): else: return None return self.lib.lammps_extract_atom_datatype(self.lmp, newname) + # ------------------------------------------------------------------------- + # extract per-atom info datatype + + def extract_atom_size(self, name, dtype): + """Retrieve per-atom property dimensions from LAMMPS + + This is a wrapper around the :cpp:func:`lammps_extract_atom_size` + function of the C-library interface. Its documentation includes a + list of the supported keywords. + This function returns ``None`` if the keyword is not + recognized. Otherwise it will return an integer value with the size + of the per-atom vector or array. If *name* corresponds to a per-atom + array, the *dtype* keyword must be either LMP_SIZE_ROWS or LMP_SIZE_COLS + from the :ref:`type ` constants defined in the + :py:mod:`lammps` module. The return value is the requested size. + If *name* corresponds to a per-atom vector the *dtype* keyword is ignored. + + :param name: name of the property + :type name: string + :param type: either LMP_SIZE_ROWS or LMP_SIZE_COLS for arrays, otherwise ignored + :type type: int + :return: data type of per-atom property (see :ref:`py_datatype_constants`) + :rtype: int + """ + if name: newname = name.encode() + else: return None + return self.lib.lammps_extract_atom_size(self.lmp, newname, dtype) + # ------------------------------------------------------------------------- # extract per-atom info def extract_atom(self, name, dtype=LAMMPS_AUTODETECT): """Retrieve per-atom properties from LAMMPS - This is a wrapper around the :cpp:func:`lammps_extract_atom` - function of the C-library interface. Its documentation includes a - list of the supported keywords and their data types. - Since Python needs to know the data type to be able to interpret - the result, by default, this function will try to auto-detect the data type - by asking the library. You can also force a specific data type by setting ``dtype`` - to one of the :ref:`data type ` constants defined in the - :py:mod:`lammps` module. - This function returns ``None`` if either the keyword is not - recognized, or an invalid data type constant is used. + This is a wrapper around the :cpp:func:`lammps_extract_atom` function of the + C-library interface. Its documentation includes a list of the supported + keywords and their data types. Since Python needs to know the data type to + be able to interpret the result, by default, this function will try to + auto-detect the data type by asking the library. You can also force a + specific data type by setting ``dtype`` to one of the :ref:`data type + ` constants defined in the :py:mod:`lammps` module. + This function returns ``None`` if either the keyword is not recognized, or + an invalid data type constant is used. .. note:: - While the returned arrays of per-atom data are dimensioned - for the range [0:nmax] - as is the underlying storage - - the data is usually only valid for the range of [0:nlocal], - unless the property of interest is also updated for ghost - atoms. In some cases, this depends on a LAMMPS setting, see - for example :doc:`comm_modify vel yes `. + While the returned vectors or arrays of per-atom data are dimensioned for + the range [0:nmax] - as is the underlying storage - the data is usually + only valid for the range of [0:nlocal], unless the property of interest + is also updated for ghost atoms. In some cases, this depends on a LAMMPS + setting, see for example :doc:`comm_modify vel yes `. + The actual size can be determined by calling + py:meth:`extract_atom_size() `. :param name: name of the property :type name: string @@ -1105,6 +1135,7 @@ class lammps(object): ctypes.POINTER(ctypes.c_int64), ctypes.POINTER(ctypes.POINTER(ctypes.c_int64)), ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.POINTER(ctypes.c_double)), or NoneType + """ if dtype == LAMMPS_AUTODETECT: dtype = self.extract_atom_datatype(name) @@ -2522,3 +2553,8 @@ class lammps(object): newcomputeid = computeid.encode() idx = self.lib.lammps_find_compute_neighlist(self.lmp, newcomputeid, reqid) return idx + +# Local Variables: +# fill-column: 80 +# End: + diff --git a/python/lammps/numpy_wrapper.py b/python/lammps/numpy_wrapper.py index 9ab7f538de..a980a972fd 100644 --- a/python/lammps/numpy_wrapper.py +++ b/python/lammps/numpy_wrapper.py @@ -54,7 +54,8 @@ class numpy_wrapper: # ------------------------------------------------------------------------- - def extract_atom(self, name, dtype=LAMMPS_AUTODETECT, nelem=LAMMPS_AUTODETECT, dim=LAMMPS_AUTODETECT): + def extract_atom(self, name, dtype=LAMMPS_AUTODETECT, nelem=LAMMPS_AUTODETECT, + dim=LAMMPS_AUTODETECT): """Retrieve per-atom properties from LAMMPS as NumPy arrays This is a wrapper around the :py:meth:`lammps.extract_atom()` method. @@ -63,16 +64,16 @@ class numpy_wrapper: .. note:: - The returned arrays of per-atom data are by default dimensioned - for the range [0:nlocal] since that data is *always* valid. The - underlying storage for the data, however, is typically allocated - for the range of [0:nmax]. Whether there is valid data in the range - [nlocal:nlocal+nghost] depends on whether the property of interest - is also updated for ghost atoms. This is not often the case. In - some cases, it depends on a LAMMPS setting, see for example - :doc:`comm_modify vel yes `. By using the optional - *nelem* parameter the size of the returned NumPy can be overridden. - There is no check whether the number of elements chosen is valid. + The returned vectors or arrays of per-atom data are dimensioned + according to the return value of :py:meth:`lammps.extract_atom_size()`. + Except for the "mass" property, the underlying storage will always be + dimensioned for the range [0:nmax]. The actual usable data may be + only in the range [0:nlocal] or [0:nlocal][0:dim]. Whether there is + valid data in the range [nlocal:nlocal+nghost] or [nlocal:local+nghost][0:dim] + depends on whether the property of interest is also updated for ghost atoms. + Also the value of *dim* depends on the value of *name*. By using the optional + *nelem* and *dim* parameters the dimensions of the returned NumPy array can + be overridden. There is no check whether the number of elements chosen is valid. :param name: name of the property :type name: string @@ -89,21 +90,10 @@ class numpy_wrapper: dtype = self.lmp.extract_atom_datatype(name) if nelem == LAMMPS_AUTODETECT: - if name == "mass": - nelem = self.lmp.extract_global("ntypes") + 1 - else: - nelem = self.lmp.extract_global("nlocal") + nelem = self.lmp.extract_atom_size(name, LMP_SIZE_ROWS) if dim == LAMMPS_AUTODETECT: if dtype in (LAMMPS_INT_2D, LAMMPS_DOUBLE_2D, LAMMPS_INT64_2D): - # TODO add other fields - if name in ("x", "v", "f", "x0","omega", "angmom", "torque", "csforce", "vforce", "vest"): - dim = 3 - elif name == "smd_data_9": - dim = 9 - elif name == "smd_stress": - dim = 6 - else: - dim = 2 + dim = self.lmp.extract_atom_size(name, LMP_SIZE_COLS) else: dim = 1 @@ -119,37 +109,6 @@ class numpy_wrapper: # ------------------------------------------------------------------------- - def extract_atom_iarray(self, name, nelem, dim=1): - warnings.warn("deprecated, use extract_atom instead", DeprecationWarning) - - if name in ['id', 'molecule']: - c_int_type = self.lmp.c_tagint - elif name in ['image']: - c_int_type = self.lmp.c_imageint - else: - c_int_type = c_int - - if dim == 1: - raw_ptr = self.lmp.extract_atom(name, LAMMPS_INT) - else: - raw_ptr = self.lmp.extract_atom(name, LAMMPS_INT_2D) - - return self.iarray(c_int_type, raw_ptr, nelem, dim) - - # ------------------------------------------------------------------------- - - def extract_atom_darray(self, name, nelem, dim=1): - warnings.warn("deprecated, use extract_atom instead", DeprecationWarning) - - if dim == 1: - raw_ptr = self.lmp.extract_atom(name, LAMMPS_DOUBLE) - else: - raw_ptr = self.lmp.extract_atom(name, LAMMPS_DOUBLE_2D) - - return self.darray(raw_ptr, nelem, dim) - - # ------------------------------------------------------------------------- - def extract_compute(self, cid, cstyle, ctype): """Retrieve data from a LAMMPS compute diff --git a/src/atom.cpp b/src/atom.cpp index 4151074a40..4a638652c7 100644 --- a/src/atom.cpp +++ b/src/atom.cpp @@ -2739,10 +2739,10 @@ Classes rarely need to check on ghost communication and so `find_custom` is typically preferred to this function. See :doc:`pair amoeba ` for an example where checking ghost communication is necessary. \endverbatim - * \param name Name of the property (w/o a "i_" or "d_" or "i2_" or "d2_" prefix) - * \param &flag Returns data type of property: 0 for int, 1 for double - * \param &cols Returns number of values: 0 for a single value, 1 or more for a vector of values - * \param &ghost Returns whether property is communicated to ghost atoms: 0 for no, 1 for yes + * \param name Name of the property (w/o a "i_" or "d_" or "i2_" or "d2_" prefix) + * \param &flag Returns data type of property: 0 for int, 1 for double + * \param &cols Returns number of values: 0 for a single value, 1 or more for a vector of values + * \param &ghost Returns whether property is communicated to ghost atoms: 0 for no, 1 for yes * \return index of property in the respective list of properties */ int Atom::find_custom_ghost(const char *name, int &flag, int &cols, int &ghost) @@ -2999,11 +2999,13 @@ length of the data area, and a short description. - N double values defined by fix property/atom array name *See also* - :cpp:func:`lammps_extract_atom` + :cpp:func:`lammps_extract_atom`, + :cpp:func:`lammps_extract_atom_datatype`, + :cpp:func:`lammps_extract_atom_size` \endverbatim * - * \sa extract_datatype + * \sa extract_datatype, extract_size * * \param name string with the keyword of the desired property. Typically the name of the pointer variable returned @@ -3142,7 +3144,7 @@ void *Atom::extract(const char *name) \endverbatim * - * \sa extract + * \sa extract extract_size * * \param name string with the keyword of the desired property. * \return data type constant for desired property or -1 */ @@ -3177,10 +3179,14 @@ int Atom::extract_datatype(const char *name) if (strcmp(name,"temperature") == 0) return LAMMPS_DOUBLE; if (strcmp(name,"heatflow") == 0) return LAMMPS_DOUBLE; + // PERI package (and in part MACHDYN) + if (strcmp(name,"vfrac") == 0) return LAMMPS_DOUBLE; if (strcmp(name,"s0") == 0) return LAMMPS_DOUBLE; if (strcmp(name,"x0") == 0) return LAMMPS_DOUBLE_2D; + // AWPMD package (and in part EFF and ELECTRODE) + if (strcmp(name,"espin") == 0) return LAMMPS_INT; if (strcmp(name,"spin") == 0) return LAMMPS_INT; // backwards compatibility if (strcmp(name,"eradius") == 0) return LAMMPS_DOUBLE; @@ -3261,6 +3267,245 @@ int Atom::extract_datatype(const char *name) return -1; } +/** Provide vector or array size info of internal data of the Atom class + * +\verbatim embed:rst + +.. versionadded:: TBD + +\endverbatim + * + * \sa extract extract_datatype + * + * \param name string with the keyword of the desired property. + * \param type either LMP_SIZE_ROWS or LMP_SIZE_COLS for per-atom array or ignored + * \return size of the vector or size of the array for the requested dimension or -1 */ + +int Atom::extract_size(const char *name, int type) +{ + // -------------------------------------------------------------------- + // 6th customization section: customize by adding new variable name + + const auto datatype = extract_datatype(name); + const auto nall = nlocal + nghost; + const auto ghost_vel = comm->ghost_velocity; + + if ((datatype == LAMMPS_DOUBLE_2D) || (datatype == LAMMPS_INT_2D)) { + if (type == LMP_SIZE_ROWS) { + if (strcmp(name,"x") == 0) return nall; + if (strcmp(name,"v") == 0) { + if (ghost_vel) return nall; + else return nlocal; + } + if (strcmp(name,"f") == 0) return nall; + if (strcmp(name,"mu") == 0) return nall; + if (strcmp(name,"omega") == 0) { + if (ghost_vel) return nall; + else return nlocal; + } + if (strcmp(name,"angmom") == 0) { + if (ghost_vel) return nall; + else return nlocal; + } + if (strcmp(name,"torque") == 0) return nlocal; + if (strcmp(name,"quat") == 0) { + if (ghost_vel) return nall; + else return nlocal; + } + + // PERI package + + if (strcmp(name,"x0") == 0) return nall; + + // SPIN package + + if (strcmp(name,"sp") == 0) return nall; + if (strcmp(name,"fm") == 0) return nlocal; + if (strcmp(name,"fm_long") == 0) return nlocal; + + // AWPMD package + + if (strcmp(name,"cs") == 0) { + if (ghost_vel) return nall; + else return nlocal; + } + if (strcmp(name,"csforce") == 0) return nlocal; + if (strcmp(name,"vforce") == 0) return nlocal; + + // SPH package + + if (strcmp(name,"vest") == 0) return nall; + + // MACHDYN package + + if (strcmp(name, "smd_data_9") == 0) return LAMMPS_DOUBLE_2D; + if (strcmp(name, "smd_stress") == 0) return LAMMPS_DOUBLE_2D; + + } else if (type == LMP_SIZE_COLS) { + + if (strcmp(name,"x") == 0) return 3; + if (strcmp(name,"v") == 0) return 3; + if (strcmp(name,"f") == 0) return 3; + if (strcmp(name,"mu") == 0) return 4; + if (strcmp(name,"omega") == 0) return 3; + if (strcmp(name,"angmom") == 0) return 3; + if (strcmp(name,"torque") == 0) return 3; + if (strcmp(name,"quat") == 0) return 4; + + // PERI package + + if (strcmp(name,"x0") == 0) return 3; + + // SPIN package + + if (strcmp(name,"sp") == 0) return 4; + if (strcmp(name,"fm") == 0) return 3; + if (strcmp(name,"fm_long") == 0) return 3; + + // AWPMD package + + if (strcmp(name,"cs") == 0) return 2; + if (strcmp(name,"csforce") == 0) return 2; + if (strcmp(name,"vforce") == 0) return 3; + + // SPH package + + if (strcmp(name,"vest") == 0) return 3; + + // MACHDYN package + + if (strcmp(name, "smd_data_9") == 0) return 9; + if (strcmp(name, "smd_stress") == 0) return 6; + } + + // custom arrays + + if (utils::strmatch(name,"^[id]2_")) { + int which = 0; + if (name[0] == 'd') which = 1; + + int index,flag,cols,ghost; + index = find_custom_ghost(&name[3],flag,cols,ghost); + + // consistency checks + if (index < 0) return -1; + if (which != flag) return -1; + if (!cols) return -1; + + if (type == LMP_SIZE_ROWS) { + if (ghost) return nall; + else return nlocal; + } else if (type == LMP_SIZE_COLS) { + return cols; + } + } + } else { + + if (strcmp(name,"mass") == 0) return ntypes + 1; + + if (strcmp(name,"id") == 0) return nall; + if (strcmp(name,"type") == 0) return nall; + if (strcmp(name,"mask") == 0) return nall; + if (strcmp(name,"image") == 0) return nlocal; + if (strcmp(name,"molecule") == 0) return nall; + if (strcmp(name,"q") == 0) return nall; + if (strcmp(name,"radius") == 0) return nall; + if (strcmp(name,"rmass") == 0) return nall; + + // ASPHERE package + + if (strcmp(name,"ellipsoid") == 0) return nlocal; + + // BODY package + + if (strcmp(name,"line") == 0) return nlocal; + if (strcmp(name,"tri") == 0) return nlocal; + if (strcmp(name,"body") == 0) return nlocal; + + // PERI package (and in part MACHDYN) + + if (strcmp(name,"vfrac") == 0) return nall; + if (strcmp(name,"s0") == 0) return nall; + + // AWPMD package (and in part EFF and ELECTRODE) + + if (strcmp(name,"espin") == 0) return nall; + if (strcmp(name,"spin") == 0) return nall; // backwards compatibility + if (strcmp(name,"eradius") == 0) return nall; + if (strcmp(name,"ervel") == 0) return nlocal; + if (strcmp(name,"erforce") == 0) return nlocal; + if (strcmp(name,"ervelforce") == 0) return nlocal; + if (strcmp(name,"etag") == 0) return nall; + + // CG-DNA package + + if (strcmp(name,"id5p") == 0) return nall; + + // RHEO package + + if (strcmp(name,"temperature") == 0) return nlocal; + if (strcmp(name,"heatflow") == 0) return nlocal; + if (strcmp(name,"rheo_status") == 0) return nall; + if (strcmp(name,"conductivity") == 0) return nlocal; + if (strcmp(name,"pressure") == 0) return nlocal; + if (strcmp(name,"viscosity") == 0) return nlocal; + + // SPH package + + if (strcmp(name,"rho") == 0) return nall; + if (strcmp(name,"drho") == 0) return nlocal; + if (strcmp(name,"esph") == 0) return nall; + if (strcmp(name,"desph") == 0) return nlocal; + if (strcmp(name,"cv") == 0) return nall; + + // MACHDYN package + + if (strcmp(name, "contact_radius") == 0) return nall; + if (strcmp(name, "eff_plastic_strain") == 0) return nlocal; + if (strcmp(name, "eff_plastic_strain_rate") == 0) return nlocal; + if (strcmp(name, "damage") == 0) return nlocal; + + // DPD-REACT package + + if (strcmp(name,"dpdTheta") == 0) return nall; + + // DPD-MESO package + + if (strcmp(name,"edpd_temp") == 0) return nall; + + // DIELECTRIC package + + if (strcmp(name,"area") == 0) return nall; + if (strcmp(name,"ed") == 0) return nall; + if (strcmp(name,"em") == 0) return nall; + if (strcmp(name,"epsilon") == 0) return nall; + if (strcmp(name,"curvature") == 0) return nall; + if (strcmp(name,"q_unscaled") == 0) return nall; + + // end of customization section + // -------------------------------------------------------------------- + + // custom vectors + + if (utils::strmatch(name,"^[id]_")) { + int which = 0; + if (name[0] == 'd') which = 1; + + int index,flag,cols,ghost; + index = find_custom_ghost(&name[2],flag,cols,ghost); + + // consistency checks + if (index < 0) return -1; + if (which != flag) return -1; + if (cols) return -1; + + if (ghost) return nall; + else return nlocal; + } + } + return -1; +} + /* ---------------------------------------------------------------------- return # of bytes of allocated memory call to avec tallies per-atom vectors diff --git a/src/atom.h b/src/atom.h index bd5b352cd0..c98f06cbe8 100644 --- a/src/atom.h +++ b/src/atom.h @@ -378,6 +378,7 @@ class Atom : protected Pointers { void *extract(const char *); int extract_datatype(const char *); + int extract_size(const char *, int); inline int *get_map_array() { return map_array; }; inline int get_map_size() { return map_tag_max + 1; }; diff --git a/src/library.cpp b/src/library.cpp index 950702c420..29cec30488 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -2087,10 +2087,13 @@ int lammps_map_atom(void *handle, const void *id) .. versionadded:: 18Sep2020 -This function returns an integer that encodes the data type of the per-atom -property with the specified name. See :cpp:enum:`_LMP_DATATYPE_CONST` for valid -values. Callers of :cpp:func:`lammps_extract_atom` can use this information -to then decide how to cast the ``void *`` pointer and access the data. +This function returns an integer that encodes the data type of the +per-atom property with the specified name. See +:cpp:enum:`_LMP_DATATYPE_CONST` for valid values. Callers of +:cpp:func:`lammps_extract_atom` can use this information to decide how +to cast the ``void *`` pointer and access the data. In addition, +:cpp:func:`lammps_extract_atom_size` can be used to get information +about the vector or array dimensions. \endverbatim * @@ -2108,18 +2111,53 @@ int lammps_extract_atom_datatype(void *handle, const char *name) /* ---------------------------------------------------------------------- */ +/** Get dimension info of a LAMMPS per-atom property + * +\verbatim embed:rst + +.. versionadded:: TBD + +This function returns an integer with the size of the per-atom +property with the specified name. This allows to accurately determine +the size of the per-atom data vectors or arrays. For per-atom arrays, +the *type* argument is required to return either the number of rows or the +number of columns. It is ignored for per-atom vectors. + +Callers of :cpp:func:`lammps_extract_atom` can use this information in +combination with the result from :cpp:func:`lammps_extract_atom_datatype` +to decide how to cast the ``void *`` pointer and access the data. + +\endverbatim + * + * \param handle pointer to a previously created LAMMPS instance + * \param name string with the name of the extracted property + * \param type either LMP_SIZE_ROWS or LMP_SIZE_COLS if *name* refers + to a per-atom array otherwise ignored + * \return integer with the size of the vector or array dimension or -1 + * */ + +int lammps_extract_atom_size(void *handle, const char *name, int type) +{ + auto lmp = (LAMMPS *) handle; + return lmp->atom->extract_size(name, type); +} + +/* ---------------------------------------------------------------------- */ + /** Get pointer to a LAMMPS per-atom property. * \verbatim embed:rst -This function returns a pointer to the location of per-atom properties -(and per-atom-type properties in the case of the 'mass' keyword). -Per-atom data is distributed across sub-domains and thus MPI ranks. The -returned pointer is cast to ``void *`` and needs to be cast to a pointer -of data type that the entity represents. +This function returns a pointer to the location of per-atom properties (and +per-atom-type properties in the case of the 'mass' keyword). Per-atom data is +distributed across sub-domains and thus MPI ranks. The returned pointer is cast +to ``void *`` and needs to be cast to a pointer of data type that the entity +represents. You can use the functions :cpp:func:`lammps_extract_atom_datatype` +and :cpp:func:`lammps_extract_atom_size` to determine data type, dimensions and +sizes of the storage pointed to by the returned pointer. -A table with supported keywords is included in the documentation -of the :cpp:func:`Atom::extract() ` function. +A table with supported keywords is included in the documentation of the +:cpp:func:`Atom::extract() ` function. .. warning:: @@ -7027,5 +7065,5 @@ int lammps_python_api_version() { } // Local Variables: -// fill-column: 72 +// fill-column: 80 // End: diff --git a/src/library.h b/src/library.h index ff16aaa088..dbfd32a542 100644 --- a/src/library.h +++ b/src/library.h @@ -172,6 +172,7 @@ int lammps_map_atom(void *handle, const void *id); * ---------------------------------------------------------------------- */ int lammps_extract_atom_datatype(void *handle, const char *name); +int lammps_extract_atom_size(void *handle, const char *name, int type); void *lammps_extract_atom(void *handle, const char *name); /* ---------------------------------------------------------------------- diff --git a/tools/swig/lammps.i b/tools/swig/lammps.i index 9bef047da4..11f5a270a1 100644 --- a/tools/swig/lammps.i +++ b/tools/swig/lammps.i @@ -130,6 +130,7 @@ extern void *lammps_extract_pair(void *handle, const char *name); extern int lammps_map_atom(void *handle, const void *id); extern int lammps_extract_atom_datatype(void *handle, const char *name); +extern int lammps_extract_atom_size(void *handle, const char *name, int type); extern void *lammps_extract_atom(void *handle, const char *name); extern void *lammps_extract_compute(void *handle, const char *id, int, int); @@ -319,6 +320,7 @@ extern void *lammps_extract_pair(void *handle, const char *name); extern int lammps_map_atom(void *handle, const void *id); extern int lammps_extract_atom_datatype(void *handle, const char *name); +extern int lammps_extract_atom_size(void *handle, const char *name, int type); extern void *lammps_extract_atom(void *handle, const char *name); extern void *lammps_extract_compute(void *handle, const char *id, int, int); diff --git a/unittest/c-library/test_library_properties.cpp b/unittest/c-library/test_library_properties.cpp index 1d27075ada..737015ccdc 100644 --- a/unittest/c-library/test_library_properties.cpp +++ b/unittest/c-library/test_library_properties.cpp @@ -695,6 +695,7 @@ TEST_F(LibraryProperties, has_error) class AtomProperties : public ::testing::Test { protected: void *lmp; + int ntypes, nlocal, nall; AtomProperties() = default; ~AtomProperties() override = default; @@ -732,6 +733,10 @@ protected: lammps_command(lmp, "set atom 2 i_two[2] 3"); lammps_command(lmp, "set atom * d_four[1] -1.3"); lammps_command(lmp, "set atom * d_four[2] 3.5"); + ntypes = lammps_extract_setting(lmp, "ntypes"); + nlocal = lammps_extract_setting(lmp, "nlocal"); + nall = lammps_extract_setting(lmp, "nall"); + output = ::testing::internal::GetCapturedStdout(); if (verbose) std::cout << output; } @@ -754,10 +759,12 @@ TEST_F(AtomProperties, invalid) TEST_F(AtomProperties, mass) { EXPECT_EQ(lammps_extract_atom_datatype(lmp, "mass"), LAMMPS_DOUBLE); + EXPECT_EQ(lammps_extract_atom_size(lmp, "mass", 0), ntypes + 1); auto *mass = (double *)lammps_extract_atom(lmp, "mass"); ASSERT_NE(mass, nullptr); ASSERT_DOUBLE_EQ(mass[1], 3.0); EXPECT_EQ(lammps_extract_atom_datatype(lmp, "rmass"), LAMMPS_DOUBLE); + EXPECT_EQ(lammps_extract_atom_size(lmp, "rmass", 0), nall); mass = (double *)lammps_extract_atom(lmp, "rmass"); ASSERT_NE(mass, nullptr); ASSERT_DOUBLE_EQ(mass[0], 2.0); @@ -767,6 +774,7 @@ TEST_F(AtomProperties, mass) TEST_F(AtomProperties, charge) { EXPECT_EQ(lammps_extract_atom_datatype(lmp, "q"), LAMMPS_DOUBLE); + EXPECT_EQ(lammps_extract_atom_size(lmp, "rmass", 0), nall); auto *charge = (double *)lammps_extract_atom(lmp, "q"); ASSERT_NE(charge, nullptr); ASSERT_DOUBLE_EQ(charge[0], -1.0); @@ -776,6 +784,7 @@ TEST_F(AtomProperties, charge) TEST_F(AtomProperties, molecule) { EXPECT_EQ(lammps_extract_atom_datatype(lmp, "molecule"), LAMMPS_TAGINT); + EXPECT_EQ(lammps_extract_atom_size(lmp, "molecule", 0), nall); auto *molecule = (tagint *)lammps_extract_atom(lmp, "molecule"); ASSERT_NE(molecule, nullptr); ASSERT_EQ(molecule[0], 2); @@ -785,6 +794,7 @@ TEST_F(AtomProperties, molecule) TEST_F(AtomProperties, id) { EXPECT_EQ(lammps_extract_atom_datatype(lmp, "id"), LAMMPS_TAGINT); + EXPECT_EQ(lammps_extract_atom_size(lmp, "id", 0), nall); auto *id = (tagint *)lammps_extract_atom(lmp, "id"); ASSERT_NE(id, nullptr); ASSERT_EQ(id[0], 1); @@ -794,6 +804,7 @@ TEST_F(AtomProperties, id) TEST_F(AtomProperties, type) { EXPECT_EQ(lammps_extract_atom_datatype(lmp, "type"), LAMMPS_INT); + EXPECT_EQ(lammps_extract_atom_size(lmp, "type", 0), nall); int *type = (int *)lammps_extract_atom(lmp, "type"); ASSERT_NE(type, nullptr); ASSERT_EQ(type[0], 1); @@ -803,6 +814,8 @@ TEST_F(AtomProperties, type) TEST_F(AtomProperties, position) { EXPECT_EQ(lammps_extract_atom_datatype(lmp, "x"), LAMMPS_DOUBLE_2D); + EXPECT_EQ(lammps_extract_atom_size(lmp, "x", LMP_SIZE_ROWS), nall); + EXPECT_EQ(lammps_extract_atom_size(lmp, "x", LMP_SIZE_COLS), 3); auto **x = (double **)lammps_extract_atom(lmp, "x"); ASSERT_NE(x, nullptr); EXPECT_DOUBLE_EQ(x[0][0], 1.0); @@ -816,15 +829,21 @@ TEST_F(AtomProperties, position) TEST_F(AtomProperties, custom) { EXPECT_EQ(lammps_extract_atom_datatype(lmp, "i_one"), LAMMPS_INT); + EXPECT_EQ(lammps_extract_atom_size(lmp, "i_one", 0), nlocal); auto *one = (int *)lammps_extract_atom(lmp, "i_one"); ASSERT_NE(one, nullptr); EXPECT_EQ(lammps_extract_atom_datatype(lmp, "i2_two"), LAMMPS_INT_2D); + EXPECT_EQ(lammps_extract_atom_size(lmp, "i2_two", LMP_SIZE_ROWS), nlocal); + EXPECT_EQ(lammps_extract_atom_size(lmp, "i2_two", LMP_SIZE_COLS), 2); auto **two = (int **)lammps_extract_atom(lmp, "i2_two"); ASSERT_NE(two, nullptr); EXPECT_EQ(lammps_extract_atom_datatype(lmp, "d_three"), LAMMPS_DOUBLE); + EXPECT_EQ(lammps_extract_atom_size(lmp, "d_three", 0), nlocal); auto *three = (double *)lammps_extract_atom(lmp, "d_three"); ASSERT_NE(three, nullptr); EXPECT_EQ(lammps_extract_atom_datatype(lmp, "d2_four"), LAMMPS_DOUBLE_2D); + EXPECT_EQ(lammps_extract_atom_size(lmp, "d2_four", LMP_SIZE_ROWS), nlocal); + EXPECT_EQ(lammps_extract_atom_size(lmp, "d2_four", LMP_SIZE_COLS), 2); auto **four = (double **)lammps_extract_atom(lmp, "d2_four"); ASSERT_NE(four, nullptr); diff --git a/unittest/python/python-numpy.py b/unittest/python/python-numpy.py index 839e5d03af..b7f1a4eedb 100644 --- a/unittest/python/python-numpy.py +++ b/unittest/python/python-numpy.py @@ -155,37 +155,6 @@ class PythonNumpy(unittest.TestCase): self.assertEqual(values[1,0], 1.5) self.assertEqual(values[1,3], 1.5) - def testExtractAtomDeprecated(self): - self.lmp.command("units lj") - self.lmp.command("atom_style atomic") - self.lmp.command("atom_modify map array") - self.lmp.command("region box block 0 2 0 2 0 2") - self.lmp.command("create_box 1 box") - - x = [ - 1.0, 1.0, 1.0, - 1.0, 1.0, 1.5 - ] - - types = [1, 1] - - self.assertEqual(self.lmp.create_atoms(2, id=None, type=types, x=x), 2) - nlocal = self.lmp.extract_global("nlocal", LAMMPS_INT) - self.assertEqual(nlocal, 2) - - ident = self.lmp.numpy.extract_atom_iarray("id", nlocal, dim=1) - self.assertEqual(len(ident), 2) - - ntypes = self.lmp.extract_global("ntypes", LAMMPS_INT) - self.assertEqual(ntypes, 1) - - x = self.lmp.numpy.extract_atom_darray("x", nlocal, dim=3) - v = self.lmp.numpy.extract_atom_darray("v", nlocal, dim=3) - self.assertEqual(len(x), 2) - self.assertTrue((x[0] == (1.0, 1.0, 1.0)).all()) - self.assertTrue((x[1] == (1.0, 1.0, 1.5)).all()) - self.assertEqual(len(v), 2) - def testExtractAtom(self): self.lmp.command("units lj") self.lmp.command("atom_style atomic") From 3e7123e21e6f1463200c7cfdebcedff6c3c67013 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 30 Aug 2024 22:51:08 -0400 Subject: [PATCH 141/355] make compatible with comm_modify vel yes --- src/CG-DNA/atom_vec_oxdna.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CG-DNA/atom_vec_oxdna.cpp b/src/CG-DNA/atom_vec_oxdna.cpp index 38f78f94bf..0836e9b47c 100644 --- a/src/CG-DNA/atom_vec_oxdna.cpp +++ b/src/CG-DNA/atom_vec_oxdna.cpp @@ -37,6 +37,7 @@ AtomVecOxdna::AtomVecOxdna(LAMMPS *lmp) : AtomVec(lmp) fields_grow = {"id5p"}; fields_copy = {"id5p"}; fields_border = {"id5p"}; + fields_border_vel = {"id5p"}; fields_exchange = {"id5p"}; fields_restart = {"id5p"}; fields_data_atom = {"id", "type", "x"}; From b426556632e3235b28baf25922cf274587d1ece4 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 30 Aug 2024 22:51:31 -0400 Subject: [PATCH 142/355] replace bogus allocation --- src/ML-QUIP/pair_quip.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ML-QUIP/pair_quip.cpp b/src/ML-QUIP/pair_quip.cpp index a2de4bf38a..9d18d6bad9 100644 --- a/src/ML-QUIP/pair_quip.cpp +++ b/src/ML-QUIP/pair_quip.cpp @@ -286,7 +286,7 @@ void PairQUIP::coeff(int narg, char **arg) // and returns the necessary size of quip_potential. This behavior // is invoked by setting n_potential_quip to 0. n_quip_potential = 0; - quip_potential = new int[0]; + quip_potential = new int[1]; quip_lammps_potential_initialise(quip_potential, &n_quip_potential, &cutoff, quip_file, &n_quip_file, quip_string, &n_quip_string); delete[] quip_potential; @@ -295,6 +295,7 @@ void PairQUIP::coeff(int narg, char **arg) // the location of the previously initialised potential to the quip_potential // variable, and we will use it as a handle when calling the actual calculation // routine. We return the cutoff as well. + delete[] quip_potential; quip_potential = new int[n_quip_potential]; quip_lammps_potential_initialise(quip_potential, &n_quip_potential, &cutoff, quip_file, &n_quip_file, quip_string, &n_quip_string); From 0cdf8f2658ecc716a44b9385b4bd1c9837d3d6bb Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 30 Aug 2024 23:16:37 -0400 Subject: [PATCH 143/355] swap rows/cols for less confusion --- fortran/lammps.f90 | 17 ++++++++--------- python/lammps/core.py | 1 - 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/fortran/lammps.f90 b/fortran/lammps.f90 index 9688dae638..b7ce528b58 100644 --- a/fortran/lammps.f90 +++ b/fortran/lammps.f90 @@ -1469,9 +1469,8 @@ CONTAINS ntypes = lmp_extract_setting(self, 'ntypes') Cname = f2c_string(name) datatype = lammps_extract_atom_datatype(self%handle, Cname) - ! Fortran and C/C++ have rows and columns switched - ncols = lammps_extract_atom_size(self%handle, Cname, LMP_SIZE_ROWS) - nrows = lammps_extract_atom_size(self%handle, Cname, LMP_SIZE_COLS) + nrows = lammps_extract_atom_size(self%handle, Cname, LMP_SIZE_ROWS) + ncols = lammps_extract_atom_size(self%handle, Cname, LMP_SIZE_COLS) Cptr = lammps_extract_atom(self%handle, Cname) CALL lammps_free(Cname) @@ -1479,25 +1478,25 @@ CONTAINS SELECT CASE (datatype) CASE (LAMMPS_INT) peratom_data%datatype = DATA_INT_1D - CALL C_F_POINTER(Cptr, peratom_data%i32_vec, [ncols]) + CALL C_F_POINTER(Cptr, peratom_data%i32_vec, [nrows]) CASE (LAMMPS_INT64) peratom_data%datatype = DATA_INT64_1D - CALL C_F_POINTER(Cptr, peratom_data%i64_vec, [ncols]) + CALL C_F_POINTER(Cptr, peratom_data%i64_vec, [nrows]) CASE (LAMMPS_DOUBLE) peratom_data%datatype = DATA_DOUBLE_1D ! The mass array is allocated from 0, but only used from 1. We also want to use it from 1. IF (name == 'mass') THEN - CALL C_F_POINTER(Cptr, dummy, [ncols]) + CALL C_F_POINTER(Cptr, dummy, [nrows]) peratom_data%r64_vec(0:) => dummy ELSE - CALL C_F_POINTER(Cptr, peratom_data%r64_vec, [ncols]) + CALL C_F_POINTER(Cptr, peratom_data%r64_vec, [nrows]) END IF CASE (LAMMPS_DOUBLE_2D) peratom_data%datatype = DATA_DOUBLE_2D ! First, we dereference the void** pointer to point to the void* - CALL C_F_POINTER(Cptr, Catomptr, [ncols]) + CALL C_F_POINTER(Cptr, Catomptr, [nrows]) ! Catomptr(1) now points to the first element of the array - CALL C_F_POINTER(Catomptr(1), peratom_data%r64_mat, [nrows,ncols]) + CALL C_F_POINTER(Catomptr(1), peratom_data%r64_mat, [ncols,nrows]) CASE (-1) CALL lmp_error(self, LMP_ERROR_ALL + LMP_ERROR_WORLD, & 'per-atom property ' // name // ' not found in extract_setting') diff --git a/python/lammps/core.py b/python/lammps/core.py index b1907888e3..788da0116f 100644 --- a/python/lammps/core.py +++ b/python/lammps/core.py @@ -2557,4 +2557,3 @@ class lammps(object): # Local Variables: # fill-column: 80 # End: - From cdd26401010e88a86f93b23c6b074eb40a66c845 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 30 Aug 2024 23:22:29 -0400 Subject: [PATCH 144/355] remove double delete[] --- src/ML-QUIP/pair_quip.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ML-QUIP/pair_quip.cpp b/src/ML-QUIP/pair_quip.cpp index 9d18d6bad9..35ba4ff8e4 100644 --- a/src/ML-QUIP/pair_quip.cpp +++ b/src/ML-QUIP/pair_quip.cpp @@ -295,7 +295,6 @@ void PairQUIP::coeff(int narg, char **arg) // the location of the previously initialised potential to the quip_potential // variable, and we will use it as a handle when calling the actual calculation // routine. We return the cutoff as well. - delete[] quip_potential; quip_potential = new int[n_quip_potential]; quip_lammps_potential_initialise(quip_potential, &n_quip_potential, &cutoff, quip_file, &n_quip_file, quip_string, &n_quip_string); From 35c36bb16ea265f5aeb83a5305c5671d6fd7d237 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 00:55:22 -0400 Subject: [PATCH 145/355] step version number for LAMMPS-GUI --- tools/lammps-gui/CMakeLists.txt | 2 +- tools/lammps-gui/lammps-gui.appdata.xml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/lammps-gui/CMakeLists.txt b/tools/lammps-gui/CMakeLists.txt index 46f36cdb13..fc111f5c64 100644 --- a/tools/lammps-gui/CMakeLists.txt +++ b/tools/lammps-gui/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -project(lammps-gui VERSION 1.6.10 LANGUAGES CXX) +project(lammps-gui VERSION 1.6.11 LANGUAGES CXX) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) diff --git a/tools/lammps-gui/lammps-gui.appdata.xml b/tools/lammps-gui/lammps-gui.appdata.xml index 66a15223a2..4c8843957e 100644 --- a/tools/lammps-gui/lammps-gui.appdata.xml +++ b/tools/lammps-gui/lammps-gui.appdata.xml @@ -54,6 +54,10 @@ + + + + Resolve plugin mode issues. From 70b8b987ca3d1640b352f6ce71eb78345020162e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 00:57:59 -0400 Subject: [PATCH 146/355] add noreturn attribute to templated version of error->one and error->all --- src/error.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/error.h b/src/error.h index 89d168652a..805bd4cd0d 100644 --- a/src/error.h +++ b/src/error.h @@ -29,14 +29,14 @@ class Error : protected Pointers { [[noreturn]] void all(const std::string &, int, const std::string &); template - void all(const std::string &file, int line, const std::string &format, Args &&...args) + [[noreturn]] void all(const std::string &file, int line, const std::string &format, Args &&...args) { _all(file, line, format, fmt::make_format_args(args...)); } [[noreturn]] void one(const std::string &, int, const std::string &); template - void one(const std::string &file, int line, const std::string &format, Args &&...args) + [[noreturn]] void one(const std::string &file, int line, const std::string &format, Args &&...args) { _one(file, line, format, fmt::make_format_args(args...)); } From 59fdfaf884ab6f8627968af67b981537692d280f Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 01:50:58 -0400 Subject: [PATCH 147/355] consistent indentation --- doc/src/region.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/src/region.rst b/doc/src/region.rst index a6a9469381..92fc2ec460 100644 --- a/doc/src/region.rst +++ b/doc/src/region.rst @@ -18,13 +18,13 @@ Syntax *delete* = no args *block* args = xlo xhi ylo yhi zlo zhi xlo,xhi,ylo,yhi,zlo,zhi = bounds of block in all dimensions (distance units) - xlo,xhi,ylo,yhi,zlo,zhi can be a variable + xlo,xhi,ylo,yhi,zlo,zhi can be a variable *cone* args = dim c1 c2 radlo radhi lo hi dim = *x* or *y* or *z* = axis of cone c1,c2 = coords of cone axis in other 2 dimensions (distance units) radlo,radhi = cone radii at lo and hi end (distance units) lo,hi = bounds of cone in dim (distance units) - c1,c2,radlo,radhi,lo,hi can be a variable (see below) + c1,c2,radlo,radhi,lo,hi can be a variable (see below) *cylinder* args = dim c1 c2 radius lo hi dim = *x* or *y* or *z* = axis of cylinder c1,c2 = coords of cylinder axis in other 2 dimensions (distance units) @@ -34,7 +34,7 @@ Syntax *ellipsoid* args = x y z a b c x,y,z = center of ellipsoid (distance units) a,b,c = half the length of the principal axes of the ellipsoid (distance units) - x,y,z,a,b and c can be a variable (see below) + x,y,z,a,b and c can be a variable (see below) *plane* args = px py pz nx ny nz px,py,pz = point on the plane (distance units) nx,ny,nz = direction normal to plane (distance units) @@ -47,7 +47,7 @@ Syntax *sphere* args = x y z radius x,y,z = center of sphere (distance units) radius = radius of sphere (distance units) - x,y,z, and radius can be a variable (see below) + x,y,z, and radius can be a variable (see below) *union* args = N reg-ID1 reg-ID2 ... N = # of regions to follow, must be 2 or greater reg-ID1,reg-ID2, ... = IDs of regions to join together @@ -209,7 +209,7 @@ and *ellipsoid* the x-, y-, and z- coordinates of the center of the sphere/ellipsoid can be specified as an equal-style variable. And for style *cylinder* the two center positions c1 and c2 for the location of the cylinder axes can be specified as a equal-style variable. For style *cone* -all properties can be defined via equal-style variables. For style *plane* +all properties can be defined via equal-style variables. For style *plane* the point can be defined via equal-style variables. If the value is a variable, it should be specified as v_name, where From b2ce4051aa27b4092df03e817bf1adf73d7a2717 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 01:53:54 -0400 Subject: [PATCH 148/355] Consistent spacing after full stops. --- doc/src/region.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/src/region.rst b/doc/src/region.rst index 92fc2ec460..7ceef94b04 100644 --- a/doc/src/region.rst +++ b/doc/src/region.rst @@ -167,7 +167,7 @@ extending in the y-direction from -5.0 to the upper box boundary. .. versionadded:: 4May2022 -For style *ellipsoid*, an axis-aligned ellipsoid is defined. The +For style *ellipsoid*, an axis-aligned ellipsoid is defined. The ellipsoid has its center at (x,y,z) and is defined by 3 axis-aligned vectors given by A = (a,0,0); B = (0,b,0); C = (0,0,c). Note that although the ellipsoid is specified as axis-aligned it can be rotated @@ -204,13 +204,13 @@ and with radius as its radius. The *radius* value for styles *sphere* and *cylinder*, and the parameters a,b,c for style *ellipsoid*, can each be specified as an -equal-style :doc:`variable `. Likewise, for style *sphere* +equal-style :doc:`variable `. Likewise, for style *sphere* and *ellipsoid* the x-, y-, and z- coordinates of the center of the -sphere/ellipsoid can be specified as an equal-style variable. And for -style *cylinder* the two center positions c1 and c2 for the location -of the cylinder axes can be specified as a equal-style variable. For style *cone* -all properties can be defined via equal-style variables. For style *plane* -the point can be defined via equal-style variables. +sphere/ellipsoid can be specified as an equal-style variable. And for +style *cylinder* the two center positions c1 and c2 for the location of +the cylinder axes can be specified as a equal-style variable. For style +*cone* all properties can be defined via equal-style variables. For +style *plane* the point can be defined via equal-style variables. If the value is a variable, it should be specified as v_name, where name is the variable name. In this case, the variable will be From a7649309983650785d70f203cff4bea414e3b720 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 02:00:30 -0400 Subject: [PATCH 149/355] consolidate replicated enum to base class header and inside the class definition --- src/region.h | 2 ++ src/region_block.cpp | 2 -- src/region_cone.cpp | 2 -- src/region_cylinder.cpp | 2 -- src/region_ellipsoid.cpp | 2 -- src/region_plane.cpp | 2 -- src/region_sphere.cpp | 2 -- 7 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/region.h b/src/region.h index f273485dce..19fdec31c7 100644 --- a/src/region.h +++ b/src/region.h @@ -20,6 +20,8 @@ namespace LAMMPS_NS { class Region : protected Pointers { public: + enum { CONSTANT, VARIABLE }; + char *id, *style; Region **reglist; int interior; // 1 for interior, 0 for exterior diff --git a/src/region_block.cpp b/src/region_block.cpp index efa3d8ca6a..9376016843 100644 --- a/src/region_block.cpp +++ b/src/region_block.cpp @@ -23,8 +23,6 @@ using namespace LAMMPS_NS; -enum { CONSTANT, VARIABLE }; - static constexpr double BIG = 1.0e20; /* ---------------------------------------------------------------------- */ diff --git a/src/region_cone.cpp b/src/region_cone.cpp index dc37eeefe3..401ed53735 100644 --- a/src/region_cone.cpp +++ b/src/region_cone.cpp @@ -27,8 +27,6 @@ using namespace LAMMPS_NS; -enum { CONSTANT, VARIABLE }; - static constexpr double BIG = 1.0e20; /* ---------------------------------------------------------------------- */ diff --git a/src/region_cylinder.cpp b/src/region_cylinder.cpp index 11783dc125..2ad0ba82f5 100644 --- a/src/region_cylinder.cpp +++ b/src/region_cylinder.cpp @@ -26,8 +26,6 @@ using namespace LAMMPS_NS; static constexpr double BIG = 1.0e20; -enum { CONSTANT, VARIABLE }; - /* ---------------------------------------------------------------------- */ RegCylinder::RegCylinder(LAMMPS *lmp, int narg, char **arg) : diff --git a/src/region_ellipsoid.cpp b/src/region_ellipsoid.cpp index daabd621c8..a0b4b9e544 100644 --- a/src/region_ellipsoid.cpp +++ b/src/region_ellipsoid.cpp @@ -23,8 +23,6 @@ using namespace LAMMPS_NS; -enum { CONSTANT, VARIABLE }; - static double GetRoot2D(double r0, double z0, double z1, double g); static double GetRoot3D(double r0, double r1, double z0, double z1, double z2, double g); diff --git a/src/region_plane.cpp b/src/region_plane.cpp index d8c09fa3cb..5917efc11b 100644 --- a/src/region_plane.cpp +++ b/src/region_plane.cpp @@ -22,8 +22,6 @@ using namespace LAMMPS_NS; -enum { CONSTANT, VARIABLE }; - /* ---------------------------------------------------------------------- */ RegPlane::RegPlane(LAMMPS *lmp, int narg, char **arg) : Region(lmp, narg, arg), diff --git a/src/region_sphere.cpp b/src/region_sphere.cpp index cd20a697d4..f449978938 100644 --- a/src/region_sphere.cpp +++ b/src/region_sphere.cpp @@ -22,8 +22,6 @@ using namespace LAMMPS_NS; -enum { CONSTANT, VARIABLE }; - /* ---------------------------------------------------------------------- */ RegSphere::RegSphere(LAMMPS *lmp, int narg, char **arg) : From abc621ddbd0f8f5449f7595c494d3d2d35ba038a Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 04:33:31 -0400 Subject: [PATCH 150/355] Add GitHub workflow to check for variable length arrays --- .github/workflows/check-vla.yml | 82 +++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 .github/workflows/check-vla.yml diff --git a/.github/workflows/check-vla.yml b/.github/workflows/check-vla.yml new file mode 100644 index 0000000000..d79e145352 --- /dev/null +++ b/.github/workflows/check-vla.yml @@ -0,0 +1,82 @@ +# GitHub action to build LAMMPS on Linux with gcc and -Werror=vla +name: "Check for Variable Length Arrays" + +on: + push: + branches: + - develop + - collected-small-changes + pull_request: + branches: + - develop + + workflow_dispatch: + +jobs: + build: + name: Build with -Werror=vla + if: ${{ github.repository == 'lammps/lammps' }} || ${{ github.repository == 'akohlmey/lammps'}} + runs-on: ubuntu-latest + env: + CCACHE_DIR: ${{ github.workspace }}/.ccache + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Install extra packages + run: | + sudo apt-get install -y ccache mold ninja-build + sudo apt-get install -y libeigen3-dev libgsl-dev libcurl4-openssl-dev python3-dev + + - name: Create Build Environment + run: mkdir build + + - name: Set up ccache + uses: actions/cache@v4 + with: + path: ${{ env.CCACHE_DIR }} + key: linux-vla-ccache-${{ github.sha }} + restore-keys: linux-vla-ccache- + + - name: Building LAMMPS via CMake + shell: bash + run: | + ccache -z + python3 -m venv linuxenv + source linuxenv/bin/activate + python3 -m pip install numpy + python3 -m pip install pyyaml + cmake -S cmake -B build \ + -C cmake/presets/most.cmake \ + -D CMAKE_CXX_COMPILER=g++ \ + -D CMAKE_C_COMPILER=gcc \ + -D CMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -D CMAKE_C_COMPILER_LAUNCHER=ccache \ + -D CMAKE_BUILD_TYPE=Debug \ + -D CMAKE_CXX_FLAGS_DEBUG="-Og -g -Werror=vla" \ + -D DOWNLOAD_POTENTIALS=off \ + -D BUILD_MPI=on \ + -D BUILD_TOOLS=off \ + -D ENABLE_TESTING=off \ + -D MLIAP_ENABLE_ACE=on \ + -D MLIAP_ENABLE_PYTHON=off \ + -D PKG_AWPMD=on \ + -D PKG_GPU=on \ + -D GPU_API=opencl \ + -D PKG_LATBOLTZ=on \ + -D PKG_MDI=on \ + -D PKG_MANIFOLD=on \ + -D PKG_ML-PACE=on \ + -D PKG_ML-RANN=off \ + -D PKG_MOLFILE=on \ + -D PKG_RHEO=on \ + -D PKG_PTM=on \ + -D PKG_PYTHON=on \ + -D PKG_QTB=on \ + -D PKG_SMTBQ=on \ + -G Ninja + cmake --build build + ccache -s From fb2c060d63881a5143fb500905cf30bdc225583e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 04:40:25 -0400 Subject: [PATCH 151/355] add unit test run on GitHub with -DLAMMPS_BIGBIG --- .github/workflows/unittest-linux.yml | 78 ++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 .github/workflows/unittest-linux.yml diff --git a/.github/workflows/unittest-linux.yml b/.github/workflows/unittest-linux.yml new file mode 100644 index 0000000000..ab5753b530 --- /dev/null +++ b/.github/workflows/unittest-linux.yml @@ -0,0 +1,78 @@ +# GitHub action to build LAMMPS on Linux and run standard unit tests +name: "Unittest for Linux /w LAMMPS_BIGBIG" + +on: + push: + branches: + - develop + - collected-small-changes + pull_request: + branches: + - develop + + workflow_dispatch: + +jobs: + build: + name: Linux Unit Test + if: ${{ github.repository == 'lammps/lammps' }} || ${{ github.repository == 'akohlmey/lammps'}} + runs-on: ubuntu-latest + env: + CCACHE_DIR: ${{ github.workspace }}/.ccache + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Install extra packages + run: | + sudo apt-get install -y ccache mold ninja-build + sudo apt-get install -y libeigen3-dev libgsl-dev libcurl4-openssl-dev python3-dev + + - name: Create Build Environment + run: mkdir build + + - name: Set up ccache + uses: actions/cache@v4 + with: + path: ${{ env.CCACHE_DIR }} + key: linux-unit-ccache-${{ github.sha }} + restore-keys: linux-unit-ccache- + + - name: Building LAMMPS via CMake + shell: bash + run: | + ccache -z + python3 -m venv linuxenv + source linuxenv/bin/activate + python3 -m pip install numpy + python3 -m pip install pyyaml + cmake -S cmake -B build \ + -C cmake/presets/gcc.cmake \ + -C cmake/presets/most.cmake \ + -D CMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -D CMAKE_C_COMPILER_LAUNCHER=ccache \ + -D BUILD_SHARED_LIBS=on \ + -D LAMMPS_SIZES=bigbig \ + -D DOWNLOAD_POTENTIALS=off \ + -D ENABLE_TESTING=on \ + -D MLIAP_ENABLE_ACE=on \ + -D MLIAP_ENABLE_PYTHON=off \ + -D PKG_MANIFOLD=on \ + -D PKG_ML-PACE=on \ + -D PKG_ML-RANN=on \ + -D PKG_RHEO=on \ + -D PKG_PTM=on \ + -D PKG_PYTHON=on \ + -D PKG_QTB=on \ + -D PKG_SMTBQ=on \ + -G Ninja + cmake --build build + ccache -s + + - name: Run Tests + working-directory: build + shell: bash + run: ctest -V From e5f8a4bec5c6dffaba07b946aa57f7d392bf71bb Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 04:47:03 -0400 Subject: [PATCH 152/355] must install mpi for compiling with mpi --- .github/workflows/check-vla.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check-vla.yml b/.github/workflows/check-vla.yml index d79e145352..5e702d3648 100644 --- a/.github/workflows/check-vla.yml +++ b/.github/workflows/check-vla.yml @@ -28,8 +28,15 @@ jobs: - name: Install extra packages run: | - sudo apt-get install -y ccache mold ninja-build - sudo apt-get install -y libeigen3-dev libgsl-dev libcurl4-openssl-dev python3-dev + sudo apt-get install -y ccache \ + libeigen3-dev \ + libgsl-dev \ + libcurl4-openssl-dev \ + mold \ + mpi-default-bin \ + mpi-default-dev \ + ninja-build \ + python3-dev - name: Create Build Environment run: mkdir build From 6ebdb0b9827e1b0172e386d4dd099dbc3a85a6d3 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 04:53:09 -0400 Subject: [PATCH 153/355] some more tweaks to the workflows --- .github/workflows/check-vla.yml | 4 ++-- .github/workflows/unittest-linux.yml | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/check-vla.yml b/.github/workflows/check-vla.yml index 5e702d3648..26f23cc33f 100644 --- a/.github/workflows/check-vla.yml +++ b/.github/workflows/check-vla.yml @@ -5,7 +5,6 @@ on: push: branches: - develop - - collected-small-changes pull_request: branches: - develop @@ -15,7 +14,7 @@ on: jobs: build: name: Build with -Werror=vla - if: ${{ github.repository == 'lammps/lammps' }} || ${{ github.repository == 'akohlmey/lammps'}} + if: ${{ github.repository == 'lammps/lammps' }} runs-on: ubuntu-latest env: CCACHE_DIR: ${{ github.workspace }}/.ccache @@ -66,6 +65,7 @@ jobs: -D CMAKE_CXX_FLAGS_DEBUG="-Og -g -Werror=vla" \ -D DOWNLOAD_POTENTIALS=off \ -D BUILD_MPI=on \ + -D BUILD_SHARED_LIBS=off \ -D BUILD_TOOLS=off \ -D ENABLE_TESTING=off \ -D MLIAP_ENABLE_ACE=on \ diff --git a/.github/workflows/unittest-linux.yml b/.github/workflows/unittest-linux.yml index ab5753b530..366db25a99 100644 --- a/.github/workflows/unittest-linux.yml +++ b/.github/workflows/unittest-linux.yml @@ -5,7 +5,6 @@ on: push: branches: - develop - - collected-small-changes pull_request: branches: - develop @@ -15,7 +14,7 @@ on: jobs: build: name: Linux Unit Test - if: ${{ github.repository == 'lammps/lammps' }} || ${{ github.repository == 'akohlmey/lammps'}} + if: ${{ github.repository == 'lammps/lammps' }} runs-on: ubuntu-latest env: CCACHE_DIR: ${{ github.workspace }}/.ccache @@ -28,8 +27,13 @@ jobs: - name: Install extra packages run: | - sudo apt-get install -y ccache mold ninja-build - sudo apt-get install -y libeigen3-dev libgsl-dev libcurl4-openssl-dev python3-dev + sudo apt-get install -y ccache \ + libeigen3-dev \ + libgsl-dev \ + libcurl4-openssl-dev \ + mold \ + ninja-build \ + python3-dev - name: Create Build Environment run: mkdir build From 70a62d5ebfb8b9ba32eef1127c6d8124e8b93ac4 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 06:20:44 -0400 Subject: [PATCH 154/355] make certain that the mass and mass_setflag arrays are fully initialized (to zero) --- src/atom.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/atom.cpp b/src/atom.cpp index 4a638652c7..f8a9bdd79b 100644 --- a/src/atom.cpp +++ b/src/atom.cpp @@ -1911,7 +1911,11 @@ void Atom::allocate_type_arrays() if (avec->mass_type == AtomVec::PER_TYPE) { mass = new double[ntypes+1]; mass_setflag = new int[ntypes+1]; - for (int itype = 1; itype <= ntypes; itype++) mass_setflag[itype] = 0; + // start loop from 0 to avoid uninitialized access when operating on the whole array + for (int itype = 0; itype <= ntypes; itype++) { + mass_setflag[itype] = 0; + mass[itype] = 0.0; + } } } From 7d176cb66ea4028be2ab8bc371da02fdadc5b664 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 07:01:44 -0400 Subject: [PATCH 155/355] fix logic bug --- src/atom.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/atom.cpp b/src/atom.cpp index f8a9bdd79b..e0fceffe9c 100644 --- a/src/atom.cpp +++ b/src/atom.cpp @@ -2752,11 +2752,12 @@ for an example where checking ghost communication is necessary. int Atom::find_custom_ghost(const char *name, int &flag, int &cols, int &ghost) { int i = find_custom(name, flag, cols); + ghost = 0; if (i == -1) return i; if ((flag == 0) && (cols == 0)) ghost = ivghost[i]; else if ((flag == 1) && (cols == 0)) ghost = dvghost[i]; - else if ((flag == 0) && (cols == 1)) ghost = iaghost[i]; - else if ((flag == 1) && (cols == 1)) ghost = daghost[i]; + else if ((flag == 0) && (cols > 0)) ghost = iaghost[i]; + else if ((flag == 1) && (cols > 0)) ghost = daghost[i]; return i; } From 2c05378815934988dfa09559b9af769678d0c36c Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 07:03:14 -0400 Subject: [PATCH 156/355] expand python module unit tests for extract_atom() --- unittest/python/python-numpy.py | 96 ++++++++++++++++++++++++++++----- 1 file changed, 82 insertions(+), 14 deletions(-) diff --git a/unittest/python/python-numpy.py b/unittest/python/python-numpy.py index b7f1a4eedb..4930527a61 100644 --- a/unittest/python/python-numpy.py +++ b/unittest/python/python-numpy.py @@ -160,31 +160,99 @@ class PythonNumpy(unittest.TestCase): self.lmp.command("atom_style atomic") self.lmp.command("atom_modify map array") self.lmp.command("region box block 0 2 0 2 0 2") - self.lmp.command("create_box 1 box") + self.lmp.command("create_box 2 box") - x = [ - 1.0, 1.0, 1.0, - 1.0, 1.0, 1.5 - ] + x = [ 1.0, 1.0, 1.0, 1.0, 1.0, 1.5, 1.5, 1.0, 1.0 ] + types = [1, 2, 1] + ids = [1, 2, 3] + self.assertEqual(self.lmp.create_atoms(3, id=ids, type=types, x=x), 3) + self.lmp.command("mass * 2.0") + self.lmp.command("pair_style zero 1.1") + self.lmp.command("pair_coeff * *") + self.lmp.command("fix props all property/atom i_one i2_two 2 d_three d2_four 2"); + self.lmp.command("fix rmass all property/atom mol q rmass ghost yes"); + self.lmp.command("fix 1 all nve") + self.lmp.command("run 0 post no") + ntypes = self.lmp.extract_setting("ntypes"); + nlocal = self.lmp.extract_setting("nlocal"); + nall = self.lmp.extract_setting("nall"); + self.assertEqual(nlocal, 3) + self.assertEqual(ntypes, 2) + self.assertEqual(nall, 63) - types = [1, 1] + self.lmp.command("set atom 1 charge -1"); + self.lmp.command("set atom 2 charge 1"); + self.lmp.command("set atom 3 charge 0"); + self.lmp.command("set atom * mol 2"); + self.lmp.command("set atom 2 mol 1"); + self.lmp.command("set atom 1 i_one -3"); + self.lmp.command("set atom 2 i_one 3"); + self.lmp.command("set atom 2 d_three -1.3"); + self.lmp.command("set atom 3 d_three 3.5"); + self.lmp.command("set atom 1 i_two[1] -3"); + self.lmp.command("set atom 2 i_two[2] 3"); + self.lmp.command("set atom * d_four[1] -1.3"); + self.lmp.command("set atom * d_four[2] 3.5"); + self.lmp.command("run 0 post no") - self.assertEqual(self.lmp.create_atoms(2, id=None, type=types, x=x), 2) - nlocal = self.lmp.extract_global("nlocal") - self.assertEqual(nlocal, 2) + mass = self.lmp.numpy.extract_atom("mass") + self.assertEqual(len(mass), ntypes + 1) + self.assertTrue((mass == (0.0, 2.0, 2.0)).all()) + + rmass = self.lmp.numpy.extract_atom("rmass") + self.assertEqual(len(rmass), nall) + self.assertTrue((rmass[0:3] == (0.0, 0.0, 0.0)).all()) + + charge = self.lmp.numpy.extract_atom("q") + self.assertEqual(len(charge), nall) + self.assertTrue((charge[0:3] == (-1.0, 1.0, 0.0)).all()) + + molecule = self.lmp.numpy.extract_atom("molecule") + self.assertEqual(len(molecule), nall) + self.assertTrue((molecule[0:3] == (2, 1, 2)).all()) ident = self.lmp.numpy.extract_atom("id") - self.assertEqual(len(ident), 2) + self.assertEqual(len(ident), nall) + self.assertTrue((ident[0:3] == (1, 2, 3)).all()) - ntypes = self.lmp.extract_global("ntypes") - self.assertEqual(ntypes, 1) + atype = self.lmp.numpy.extract_atom("type") + self.assertEqual(len(atype), nall) + self.assertTrue((atype[0:3] == (1, 2, 1)).all()) x = self.lmp.numpy.extract_atom("x") v = self.lmp.numpy.extract_atom("v") - self.assertEqual(len(x), 2) + self.assertEqual(len(x), nall) + self.assertEqual(len(x[0]), 3) self.assertTrue((x[0] == (1.0, 1.0, 1.0)).all()) self.assertTrue((x[1] == (1.0, 1.0, 1.5)).all()) - self.assertEqual(len(v), 2) + self.assertTrue((x[2] == (1.5, 1.0, 1.0)).all()) + self.assertEqual(len(v), nlocal) + self.assertEqual(len(v[0]), 3) + + self.lmp.command("comm_modify vel yes"); + self.lmp.command("run 0 post no") + + v = self.lmp.numpy.extract_atom("v") + self.assertEqual(len(v), nall) + + one = self.lmp.numpy.extract_atom("i_one") + two = self.lmp.numpy.extract_atom("i2_two") + three = self.lmp.numpy.extract_atom("d_three") + four = self.lmp.numpy.extract_atom("d2_four") + self.assertEqual(len(one), nlocal) + self.assertTrue((one == (-3, 3, 0)).all()) + self.assertEqual(len(two), nlocal) + self.assertEqual(len(two[0]), 2) + self.assertTrue((two[0] == (-3, 0)).all()) + self.assertTrue((two[1] == (0, 3)).all()) + self.assertTrue((two[2] == (0, 0)).all()) + self.assertEqual(len(three), nlocal) + self.assertTrue((three == (0.0, -1.3, 3.5)).all()) + self.assertEqual(len(four), nlocal) + self.assertEqual(len(four[0]), 2) + self.assertTrue((four[0] == (-1.3, 3.5)).all()) + self.assertTrue((four[1] == (-1.3, 3.5)).all()) + self.assertTrue((four[2] == (-1.3, 3.5)).all()) @unittest.skipIf(not has_full,"Gather bonds test") def testGatherBond_newton_on(self): From 84fa26ee2c29f66ede462de76c0e82e24db34e3c Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 11:13:53 -0400 Subject: [PATCH 157/355] update test checking tool --- unittest/force-styles/check_tests.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/unittest/force-styles/check_tests.py b/unittest/force-styles/check_tests.py index 4dba8f9b9e..7305168ecb 100755 --- a/unittest/force-styles/check_tests.py +++ b/unittest/force-styles/check_tests.py @@ -88,10 +88,9 @@ for header in headers: style = m[1] if upper.match(style): continue - if style in ['reax/c', 'reax/c/omp', 'reax/c/kk', - 'reax/c/kk/device', 'reax/c/kk/host', - 'reax/c/species', 'reax/c/bonds', - 'reax/c/species/kk', 'reax/c/bonds/kk', 'meam/c']: + if style in ['lj/sdk', 'lj/sdk/coul/long', 'lj/sdk/coul/msm', 'sdk', 'lj/sdk/gpu', + 'lj/sdk/coul/long/gpu', 'lj/sdk/omp', 'lj/sdk/coul/long/omp', 'sdk/omp', + 'lj/sdk/coul/msm/omp', 'lj/sdk/kk', 'lj/sdk/coul/long/kk', 'sdk/kk']: continue # detect, process, and flag suffix styles: @@ -176,11 +175,12 @@ def check_tests(name,styles,yaml,search,skip=()): counter = 0 counter += check_tests('pair',pair,'*-pair-*.yaml', - '.*pair_style:\\s*((\\S+).*)?',skip=('meam','lj/sf')) + '.*pair_style:\\s*((\\S+).*)?', + skip=('lj/sf','lj/sdk', 'lj/sdk/coul/long', 'lj/sdk/coul/msm')) counter += check_tests('bond',bond,'bond-*.yaml', '.*bond_style:\\s*((\\S+).*)?') counter += check_tests('angle',angle,'angle-*.yaml', - '.*angle_style:\\s*((\\S+).*)?') + '.*angle_style:\\s*((\\S+).*)?', skip=('sdk')) counter += check_tests('dihedral',dihedral,'dihedral-*.yaml', '.*dihedral_style:\\s*((\\S+).*)?') counter += check_tests('improper',improper,'improper-*.yaml', From 6f114eddeae799f58a9a4b17263f7d0e5404806c Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Fri, 30 Aug 2024 23:39:40 -0600 Subject: [PATCH 158/355] Fix typo in CMakeLists.txt --- cmake/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 723015c1f9..67b95c2ab8 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -474,13 +474,13 @@ if(BUILD_OMP) if(CMAKE_VERSION VERSION_LESS 3.28) get_filename_component(_exe "${CMAKE_CXX_COMPILER}" NAME) if((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND (_exe STREQUAL "crayCC")) - set(CMAKE_SHARED_LINKER_FLAGS_${BTYPE} "${CMAKE_SHARED_LINKER_FLAGS_${BTYPE} -fopenmp") - set(CMAKE_STATIC_LINKER_FLAGS_${BTYPE} "${CMAKE_STATIC_LINKER_FLAGS_${BTYPE} -fopenmp") + set(CMAKE_SHARED_LINKER_FLAGS_${BTYPE} "${CMAKE_SHARED_LINKER_FLAGS_${BTYPE}} -fopenmp") + set(CMAKE_STATIC_LINKER_FLAGS_${BTYPE} "${CMAKE_STATIC_LINKER_FLAGS_${BTYPE}} -fopenmp") endif() else() if(CMAKE_CXX_COMPILER_ID STREQUAL "CrayClang") - set(CMAKE_SHARED_LINKER_FLAGS_${BTYPE} "${CMAKE_SHARED_LINKER_FLAGS_${BTYPE} -fopenmp") - set(CMAKE_STATIC_LINKER_FLAGS_${BTYPE} "${CMAKE_STATIC_LINKER_FLAGS_${BTYPE} -fopenmp") + set(CMAKE_SHARED_LINKER_FLAGS_${BTYPE} "${CMAKE_SHARED_LINKER_FLAGS_${BTYPE}} -fopenmp") + set(CMAKE_STATIC_LINKER_FLAGS_${BTYPE} "${CMAKE_STATIC_LINKER_FLAGS_${BTYPE}} -fopenmp") endif() endif() endif() From 78597a9c39aad1da64bef79c5c26dc8dd86d9699 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 12:16:27 -0400 Subject: [PATCH 159/355] add fortran module tests for extracting atom properties with array dimensions --- fortran/lammps.f90 | 1 + unittest/fortran/keepstuff.f90 | 28 ++++++---- .../fortran/test_fortran_extract_atom.f90 | 54 ++++++++++++++++++- unittest/fortran/wrap_extract_atom.cpp | 25 ++++++++- 4 files changed, 97 insertions(+), 11 deletions(-) diff --git a/fortran/lammps.f90 b/fortran/lammps.f90 index b7ce528b58..6ced71be49 100644 --- a/fortran/lammps.f90 +++ b/fortran/lammps.f90 @@ -1496,6 +1496,7 @@ CONTAINS ! First, we dereference the void** pointer to point to the void* CALL C_F_POINTER(Cptr, Catomptr, [nrows]) ! Catomptr(1) now points to the first element of the array + ! rows and columns are swapped in Fortran CALL C_F_POINTER(Catomptr(1), peratom_data%r64_mat, [ncols,nrows]) CASE (-1) CALL lmp_error(self, LMP_ERROR_ALL + LMP_ERROR_WORLD, & diff --git a/unittest/fortran/keepstuff.f90 b/unittest/fortran/keepstuff.f90 index 63184e1006..c964c8a9c6 100644 --- a/unittest/fortran/keepstuff.f90 +++ b/unittest/fortran/keepstuff.f90 @@ -4,9 +4,9 @@ MODULE keepstuff TYPE(LAMMPS), SAVE :: lmp INTEGER, SAVE :: mycomm CHARACTER(LEN=40), DIMENSION(3), PARAMETER :: demo_input = & - [ CHARACTER(LEN=40) :: & - 'region box block 0 $x 0 2 0 2', & - 'create_box 1 box', & + [ CHARACTER(LEN=40) :: & + 'region box block 0 $x 0 2 0 2', & + 'create_box 1 box', & 'create_atoms 1 single 1.0 1.0 ${zpos}' ] CHARACTER(LEN=40), DIMENSION(3), PARAMETER :: big_input = & [ CHARACTER(LEN=40) :: & @@ -14,15 +14,26 @@ MODULE keepstuff 'create_box 1 box', & 'create_atoms 1 single 1.0 1.0 ${zpos}' ] CHARACTER(LEN=40), DIMENSION(2), PARAMETER :: cont_input = & - [ CHARACTER(LEN=40) :: & - 'create_atoms 1 single &', & + [ CHARACTER(LEN=40) :: & + 'create_atoms 1 single &', & ' 0.2 0.1 0.1' ] + CHARACTER(LEN=60), DIMENSION(18), PARAMETER :: prop_input = & + [ CHARACTER(LEN=60) :: 'fix 1 all nve', 'mass 1 3.0', & + 'fix 2 all property/atom mol q rmass ghost yes', & + 'fix 3 all property/atom i_one i2_two 2 d_three d2_four 2', & + 'set group all mass 2.0', 'set atom 1 charge -1', & + 'set atom 2 charge 1', 'set atom 1 mol 2', 'set atom 2 mol 1', & + 'set atom 1 i_one -3', 'set atom 2 i_one 3', & + 'set atom 1 d_three -1.3', 'set atom 2 d_three 3.5', & + 'set atom 1 i_two[1] -3', 'set atom 2 i_two[2] 3', & + 'set atom * d_four[1] -1.3', 'set atom * d_four[2] 3.5', & + 'run 0 post no' ] CHARACTER(LEN=40), DIMENSION(1), PARAMETER :: more_input = & [ CHARACTER(LEN=40) :: 'create_atoms 1 single 0.5 0.5 0.5' ] CHARACTER(LEN=40), DIMENSION(3), PARAMETER :: pair_input = & - [ CHARACTER(LEN=40) :: & - 'pair_style lj/cut 2.5', & - 'pair_coeff 1 1 1.0 1.0', & + [ CHARACTER(LEN=40) :: & + 'pair_style lj/cut 2.5', & + 'pair_coeff 1 1 1.0 1.0', & 'mass 1 2.0' ] INTERFACE @@ -63,4 +74,3 @@ CONTAINS END FUNCTION f2c_string END MODULE keepstuff - diff --git a/unittest/fortran/test_fortran_extract_atom.f90 b/unittest/fortran/test_fortran_extract_atom.f90 index 262e5de47d..0c5a52ef25 100644 --- a/unittest/fortran/test_fortran_extract_atom.f90 +++ b/unittest/fortran/test_fortran_extract_atom.f90 @@ -24,12 +24,13 @@ END SUBROUTINE f_lammps_close SUBROUTINE f_lammps_setup_extract_atom() BIND(C) USE LIBLAMMPS - USE keepstuff, ONLY : lmp, big_input, cont_input, pair_input + USE keepstuff, ONLY : lmp, big_input, cont_input, pair_input, prop_input IMPLICIT NONE CALL lmp%commands_list(big_input) CALL lmp%commands_list(cont_input) CALL lmp%commands_list(pair_input) + CALL lmp%commands_list(prop_input) END SUBROUTINE f_lammps_setup_extract_atom FUNCTION f_lammps_extract_atom_mass() BIND(C) @@ -44,6 +45,19 @@ FUNCTION f_lammps_extract_atom_mass() BIND(C) f_lammps_extract_atom_mass = mass(1) END FUNCTION f_lammps_extract_atom_mass +FUNCTION f_lammps_extract_atom_mass_size() BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : c_double, c_int + USE LIBLAMMPS + USE keepstuff, ONLY : lmp + IMPLICIT NONE + INTEGER(c_int) :: f_lammps_extract_atom_mass_size, ntypes + REAL(c_double), DIMENSION(:), POINTER :: mass => NULL() + + ntypes = lmp%extract_setting('ntypes') + mass = lmp%extract_atom('mass') + f_lammps_extract_atom_mass_size = SIZE(mass) +END FUNCTION f_lammps_extract_atom_mass_size + FUNCTION f_lammps_extract_atom_tag_int(i) BIND(C) USE, INTRINSIC :: ISO_C_BINDING, ONLY : c_double, c_int USE LIBLAMMPS @@ -83,6 +97,18 @@ FUNCTION f_lammps_extract_atom_type(i) BIND(C) f_lammps_extract_atom_type = atype(i) END FUNCTION f_lammps_extract_atom_type +FUNCTION f_lammps_extract_atom_type_size() BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : c_int + USE LIBLAMMPS + USE keepstuff, ONLY : lmp + IMPLICIT NONE + INTEGER(c_int) :: f_lammps_extract_atom_type_size + INTEGER(c_int), DIMENSION(:), POINTER :: atype => NULL() + + atype = lmp%extract_atom('type') + f_lammps_extract_atom_type_size = size(atype) +END FUNCTION f_lammps_extract_atom_type_size + FUNCTION f_lammps_extract_atom_mask(i) BIND(C) USE, INTRINSIC :: ISO_C_BINDING, ONLY : c_int USE LIBLAMMPS @@ -109,6 +135,19 @@ SUBROUTINE f_lammps_extract_atom_x(i, x) BIND(C) x = xptr(:,i) END SUBROUTINE f_lammps_extract_atom_x +FUNCTION f_lammps_extract_atom_x_size(i) BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : c_double, c_int + USE LIBLAMMPS + USE keepstuff, ONLY : lmp + IMPLICIT NONE + INTEGER(c_int), INTENT(IN), VALUE :: i + INTEGER(c_int) :: f_lammps_extract_atom_x_size + REAL(c_double), DIMENSION(:,:), POINTER :: xptr => NULL() + + xptr = lmp%extract_atom('x') + f_lammps_extract_atom_x_size = SIZE(xptr, i) +END FUNCTION f_lammps_extract_atom_x_size + SUBROUTINE f_lammps_extract_atom_v(i, v) BIND(C) USE, INTRINSIC :: ISO_C_BINDING, ONLY : c_double, c_int USE LIBLAMMPS @@ -121,3 +160,16 @@ SUBROUTINE f_lammps_extract_atom_v(i, v) BIND(C) vptr = lmp%extract_atom('v') v = vptr(:,i) END SUBROUTINE f_lammps_extract_atom_v + +FUNCTION f_lammps_extract_atom_v_size(i) BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : c_double, c_int + USE LIBLAMMPS + USE keepstuff, ONLY : lmp + IMPLICIT NONE + INTEGER(c_int), INTENT(IN), VALUE :: i + INTEGER(c_int) :: f_lammps_extract_atom_v_size + REAL(c_double), DIMENSION(:,:), POINTER :: xptr => NULL() + + xptr = lmp%extract_atom('v') + f_lammps_extract_atom_v_size = SIZE(xptr, i) +END FUNCTION f_lammps_extract_atom_v_size diff --git a/unittest/fortran/wrap_extract_atom.cpp b/unittest/fortran/wrap_extract_atom.cpp index 2552d6a10f..9430959b2b 100644 --- a/unittest/fortran/wrap_extract_atom.cpp +++ b/unittest/fortran/wrap_extract_atom.cpp @@ -1,6 +1,7 @@ // unit tests for extracting Atom class data from a LAMMPS instance through the // Fortran wrapper +#include "atom.h" #include "lammps.h" #include "library.h" #include @@ -16,12 +17,16 @@ void *f_lammps_with_args(); void f_lammps_close(); void f_lammps_setup_extract_atom(); double f_lammps_extract_atom_mass(); +int f_lammps_extract_atom_mass_size(); int f_lammps_extract_atom_tag_int(int); int64_t f_lammps_extract_atom_tag_int64(int64_t); int f_lammps_extract_atom_type(int); +int f_lammps_extract_atom_type_size(); int f_lammps_extract_atom_mask(int); void f_lammps_extract_atom_x(int, double *); +int f_lammps_extract_atom_x_size(int); void f_lammps_extract_atom_v(int, double *); +int f_lammps_extract_atom_v_size(int); } class LAMMPS_extract_atom : public ::testing::Test { @@ -50,7 +55,9 @@ protected: TEST_F(LAMMPS_extract_atom, mass) { f_lammps_setup_extract_atom(); - EXPECT_DOUBLE_EQ(f_lammps_extract_atom_mass(), 2.0); + int ntypes = lmp->atom->ntypes; + EXPECT_DOUBLE_EQ(f_lammps_extract_atom_mass(), 3.0); + EXPECT_EQ(f_lammps_extract_atom_mass_size(), ntypes + 1); }; TEST_F(LAMMPS_extract_atom, tag) @@ -68,8 +75,10 @@ TEST_F(LAMMPS_extract_atom, tag) TEST_F(LAMMPS_extract_atom, type) { f_lammps_setup_extract_atom(); + int nall = lmp->atom->nlocal + lmp->atom->nghost; EXPECT_EQ(f_lammps_extract_atom_type(1), 1); EXPECT_EQ(f_lammps_extract_atom_type(2), 1); + EXPECT_EQ(f_lammps_extract_atom_type_size(), nall); }; TEST_F(LAMMPS_extract_atom, mask) @@ -86,6 +95,7 @@ TEST_F(LAMMPS_extract_atom, mask) TEST_F(LAMMPS_extract_atom, x) { f_lammps_setup_extract_atom(); + int nall = lmp->atom->nlocal + lmp->atom->nghost; double x1[3]; double x2[3]; f_lammps_extract_atom_x(1, x1); @@ -96,11 +106,15 @@ TEST_F(LAMMPS_extract_atom, x) EXPECT_DOUBLE_EQ(x2[0], 0.2); EXPECT_DOUBLE_EQ(x2[1], 0.1); EXPECT_DOUBLE_EQ(x2[2], 0.1); + // in Fortran row and column are swapped + EXPECT_EQ(f_lammps_extract_atom_x_size(1), 3); + EXPECT_EQ(f_lammps_extract_atom_x_size(2), nall); } TEST_F(LAMMPS_extract_atom, v) { f_lammps_setup_extract_atom(); + int nall = lmp->atom->nlocal + lmp->atom->nghost; double v1[3]; double v2[3]; f_lammps_extract_atom_v(1, v1); @@ -117,4 +131,13 @@ TEST_F(LAMMPS_extract_atom, v) EXPECT_DOUBLE_EQ(v1[0], 1.0); EXPECT_DOUBLE_EQ(v1[1], 2.0); EXPECT_DOUBLE_EQ(v1[2], 3.0); + // in Fortran row and column are swapped! + EXPECT_EQ(f_lammps_extract_atom_v_size(1), 3); + EXPECT_EQ(f_lammps_extract_atom_v_size(2), lmp->atom->nlocal); + lammps_command(lmp, "comm_modify vel yes"); + lammps_command(lmp, "run 0 post no"); + EXPECT_EQ(f_lammps_extract_atom_v_size(1), 3); + EXPECT_EQ(f_lammps_extract_atom_v_size(2), nall); } + +// TODO: write tests for custom properties From 7492ab754116c2cca577fb47acf3607da937c3b4 Mon Sep 17 00:00:00 2001 From: cjknight Date: Sat, 31 Aug 2024 11:18:43 -0500 Subject: [PATCH 160/355] sync forces on step 0 --- src/KOKKOS/fix_shake_kokkos.cpp | 10 ++++++++++ src/KOKKOS/fix_shake_kokkos.h | 1 + 2 files changed, 11 insertions(+) diff --git a/src/KOKKOS/fix_shake_kokkos.cpp b/src/KOKKOS/fix_shake_kokkos.cpp index b25e2dad59..4602546579 100644 --- a/src/KOKKOS/fix_shake_kokkos.cpp +++ b/src/KOKKOS/fix_shake_kokkos.cpp @@ -172,6 +172,16 @@ void FixShakeKokkos::init() k_angle_distance.sync(); } +/* ---------------------------------------------------------------------- + SHAKE as pre-integrator constraint +------------------------------------------------------------------------- */ + +template +void FixShakeKokkos::setup(int vflag) +{ + FixShake::setup(vflag); + atomKK->sync(Host,F_MASK); +} /* ---------------------------------------------------------------------- run setup for minimization. diff --git a/src/KOKKOS/fix_shake_kokkos.h b/src/KOKKOS/fix_shake_kokkos.h index 31a6c340be..519db18b5f 100644 --- a/src/KOKKOS/fix_shake_kokkos.h +++ b/src/KOKKOS/fix_shake_kokkos.h @@ -52,6 +52,7 @@ class FixShakeKokkos : public FixShake, public KokkosBase { FixShakeKokkos(class LAMMPS *, int, char **); ~FixShakeKokkos() override; void init() override; + void setup(int) override; void min_setup(int) override; void pre_neighbor() override; void post_force(int) override; From 55a549a2fba8214184e341da9f035ad4cbc6031d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 12:43:50 -0400 Subject: [PATCH 161/355] try speed up windows compilation with ccache --- .github/workflows/compile-msvc.yml | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/.github/workflows/compile-msvc.yml b/.github/workflows/compile-msvc.yml index 1a0f1ea62f..94de700bb7 100644 --- a/.github/workflows/compile-msvc.yml +++ b/.github/workflows/compile-msvc.yml @@ -1,5 +1,5 @@ # GitHub action to build LAMMPS on Windows with Visual C++ -name: "Native Windows Compilation and Unit Tests" +name: "Windows Unit Tests" on: push: @@ -16,6 +16,8 @@ jobs: name: Windows Compilation Test if: ${{ github.repository == 'lammps/lammps' }} runs-on: windows-latest + env: + CCACHE_DIR: ${{ github.workspace }}/.ccache steps: - name: Checkout repository @@ -23,6 +25,21 @@ jobs: with: fetch-depth: 2 + - name: Install Ccache + run: winget install Ccache.Ccache + + - name: Install MSMPI + run: | + nuget install MSMPIsdk + nuget install MSMPIDIST + + - name: Set up ccache + uses: actions/cache@v4 + with: + path: ${{ env.CCACHE_DIR }} + key: win-unit-ccache-${{ github.sha }} + restore-keys: win-unit-ccache- + - name: Select Python version uses: actions/setup-python@v5 with: @@ -31,20 +48,21 @@ jobs: - name: Building LAMMPS via CMake shell: bash run: | + ccache -z python3 -m pip install numpy python3 -m pip install pyyaml - nuget install MSMPIsdk - nuget install MSMPIDIST cmake -C cmake/presets/windows.cmake \ + -D CMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -D CMAKE_C_COMPILER_LAUNCHER=ccache \ -D DOWNLOAD_POTENTIALS=off \ -D PKG_PYTHON=on \ -D WITH_PNG=off \ -D WITH_JPEG=off \ -S cmake -B build \ -D BUILD_SHARED_LIBS=on \ - -D LAMMPS_EXCEPTIONS=on \ -D ENABLE_TESTING=on cmake --build build --config Release --parallel 2 + ccache -s - name: Run LAMMPS executable shell: bash From 0d7305672b101fed03b9d29bb6379fcec6b946c8 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 12:48:09 -0400 Subject: [PATCH 162/355] use chocolatey instead of winget --- .github/workflows/compile-msvc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/compile-msvc.yml b/.github/workflows/compile-msvc.yml index 94de700bb7..3ce8c1f25b 100644 --- a/.github/workflows/compile-msvc.yml +++ b/.github/workflows/compile-msvc.yml @@ -26,7 +26,7 @@ jobs: fetch-depth: 2 - name: Install Ccache - run: winget install Ccache.Ccache + run: choco install ccache - name: Install MSMPI run: | From 72acea291afab90d82ef08c39fde25f74b98e1d9 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 13:42:55 -0400 Subject: [PATCH 163/355] switch to ninja-build and single configuration setup --- .github/workflows/compile-msvc.yml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/workflows/compile-msvc.yml b/.github/workflows/compile-msvc.yml index 3ce8c1f25b..813579174e 100644 --- a/.github/workflows/compile-msvc.yml +++ b/.github/workflows/compile-msvc.yml @@ -26,7 +26,9 @@ jobs: fetch-depth: 2 - name: Install Ccache - run: choco install ccache + run: | + choco install ccache + choco install ninja - name: Install MSMPI run: | @@ -52,25 +54,26 @@ jobs: python3 -m pip install numpy python3 -m pip install pyyaml cmake -C cmake/presets/windows.cmake \ - -D CMAKE_CXX_COMPILER_LAUNCHER=ccache \ - -D CMAKE_C_COMPILER_LAUNCHER=ccache \ + -D CMAKE_CXX_COMPILER_LAUNCHER=$(which ccache) \ + -D CMAKE_C_COMPILER_LAUNCHER=$(which ccache) \ -D DOWNLOAD_POTENTIALS=off \ -D PKG_PYTHON=on \ -D WITH_PNG=off \ -D WITH_JPEG=off \ -S cmake -B build \ -D BUILD_SHARED_LIBS=on \ - -D ENABLE_TESTING=on - cmake --build build --config Release --parallel 2 + -D ENABLE_TESTING=on \ + -G Ninja + cmake --build build ccache -s - name: Run LAMMPS executable shell: bash run: | - ./build/Release/lmp.exe -h - ./build/Release/lmp.exe -in bench/in.lj + ./build/lmp.exe -h + ./build/lmp.exe -in bench/in.lj - name: Run Unit Tests working-directory: build shell: bash - run: ctest -V -C Release -E FixTimestep:python_move_nve + run: ctest -V -E FixTimestep:python_move_nve From 2998d88edb6b4ee74cfdfed1708e93e0792a8e46 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 13:48:10 -0400 Subject: [PATCH 164/355] prefer MSVC++ over MinGW --- .github/workflows/compile-msvc.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/compile-msvc.yml b/.github/workflows/compile-msvc.yml index 813579174e..6d77e694ef 100644 --- a/.github/workflows/compile-msvc.yml +++ b/.github/workflows/compile-msvc.yml @@ -54,6 +54,8 @@ jobs: python3 -m pip install numpy python3 -m pip install pyyaml cmake -C cmake/presets/windows.cmake \ + -D CMAKE_CXX_COMPILER=cl.exe \ + -D CMAKE_C_COMPILER=cl.exe \ -D CMAKE_CXX_COMPILER_LAUNCHER=$(which ccache) \ -D CMAKE_C_COMPILER_LAUNCHER=$(which ccache) \ -D DOWNLOAD_POTENTIALS=off \ From 6c40e8dc799482f5d7b80873278daf3bfe5c2886 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 13:57:54 -0400 Subject: [PATCH 165/355] revert back to MSBuild and report launcher, if active --- .github/workflows/compile-msvc.yml | 6 +----- cmake/CMakeLists.txt | 6 ++++++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/compile-msvc.yml b/.github/workflows/compile-msvc.yml index 6d77e694ef..3de6a46c7c 100644 --- a/.github/workflows/compile-msvc.yml +++ b/.github/workflows/compile-msvc.yml @@ -28,7 +28,6 @@ jobs: - name: Install Ccache run: | choco install ccache - choco install ninja - name: Install MSMPI run: | @@ -54,8 +53,6 @@ jobs: python3 -m pip install numpy python3 -m pip install pyyaml cmake -C cmake/presets/windows.cmake \ - -D CMAKE_CXX_COMPILER=cl.exe \ - -D CMAKE_C_COMPILER=cl.exe \ -D CMAKE_CXX_COMPILER_LAUNCHER=$(which ccache) \ -D CMAKE_C_COMPILER_LAUNCHER=$(which ccache) \ -D DOWNLOAD_POTENTIALS=off \ @@ -64,8 +61,7 @@ jobs: -D WITH_JPEG=off \ -S cmake -B build \ -D BUILD_SHARED_LIBS=on \ - -D ENABLE_TESTING=on \ - -G Ninja + -D ENABLE_TESTING=on cmake --build build ccache -s diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 67b95c2ab8..1c41191ec8 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -973,6 +973,9 @@ message(STATUS "<<< Compilers and Flags: >>> C++ Standard: ${CMAKE_CXX_STANDARD} C++ Flags: ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${BTYPE}} Defines: ${DEFINES}") +if(CMAKE_CXX_COMPILER_LAUNCHER) + message(STATUS, " Launcher: ${CMAKE_CXX_COMPILER_LAUNCHER}") +endif() get_target_property(OPTIONS lammps COMPILE_OPTIONS) if(OPTIONS) message(" Options: ${OPTIONS}") @@ -991,6 +994,9 @@ if(_index GREATER -1) Type: ${CMAKE_C_COMPILER_ID} Version: ${CMAKE_C_COMPILER_VERSION} C Flags: ${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${BTYPE}}") + if(CMAKE_C_COMPILER_LAUNCHER) + message(STATUS, " Launcher: ${CMAKE_C_COMPILER_LAUNCHER}") + endif() endif() message(STATUS "<<< Linker flags: >>>") message(STATUS "Executable name: ${LAMMPS_BINARY}") From 6d55da72078e744255a9f94cc45bed5df6327c2b Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 14:00:51 -0400 Subject: [PATCH 166/355] bring back multi-config --- .github/workflows/compile-msvc.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/compile-msvc.yml b/.github/workflows/compile-msvc.yml index 3de6a46c7c..941dd11d0f 100644 --- a/.github/workflows/compile-msvc.yml +++ b/.github/workflows/compile-msvc.yml @@ -62,16 +62,15 @@ jobs: -S cmake -B build \ -D BUILD_SHARED_LIBS=on \ -D ENABLE_TESTING=on - cmake --build build - ccache -s + cmake --build build --config Release --parallel 2 - name: Run LAMMPS executable shell: bash run: | - ./build/lmp.exe -h - ./build/lmp.exe -in bench/in.lj + ./build/Release/lmp.exe -h + ./build/Release/lmp.exe -in bench/in.lj - name: Run Unit Tests working-directory: build shell: bash - run: ctest -V -E FixTimestep:python_move_nve + run: ctest -V -C Release -E FixTimestep:python_move_nve From 6e612f68c2a688e0f1844bba0620b463c0fb9f43 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 14:09:47 -0400 Subject: [PATCH 167/355] remove misplaced comma --- cmake/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 1c41191ec8..9c5ba9095a 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -974,7 +974,7 @@ message(STATUS "<<< Compilers and Flags: >>> C++ Flags: ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${BTYPE}} Defines: ${DEFINES}") if(CMAKE_CXX_COMPILER_LAUNCHER) - message(STATUS, " Launcher: ${CMAKE_CXX_COMPILER_LAUNCHER}") + message(STATUS " Launcher: ${CMAKE_CXX_COMPILER_LAUNCHER}") endif() get_target_property(OPTIONS lammps COMPILE_OPTIONS) if(OPTIONS) @@ -995,7 +995,7 @@ if(_index GREATER -1) Version: ${CMAKE_C_COMPILER_VERSION} C Flags: ${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${BTYPE}}") if(CMAKE_C_COMPILER_LAUNCHER) - message(STATUS, " Launcher: ${CMAKE_C_COMPILER_LAUNCHER}") + message(STATUS " Launcher: ${CMAKE_C_COMPILER_LAUNCHER}") endif() endif() message(STATUS "<<< Linker flags: >>>") From d85380476db063e21394c5e08fc46da5a91f4e48 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 14:38:12 -0400 Subject: [PATCH 168/355] alternate approach for using ninja with MSVC++ --- .github/workflows/compile-msvc.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/compile-msvc.yml b/.github/workflows/compile-msvc.yml index 941dd11d0f..e2d9a3628a 100644 --- a/.github/workflows/compile-msvc.yml +++ b/.github/workflows/compile-msvc.yml @@ -22,12 +22,14 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + uses: ilammy/msvc-dev-cmd@v1 with: fetch-depth: 2 - name: Install Ccache run: | choco install ccache + choco install ninja - name: Install MSMPI run: | @@ -47,7 +49,6 @@ jobs: python-version: '3.11' - name: Building LAMMPS via CMake - shell: bash run: | ccache -z python3 -m pip install numpy @@ -61,16 +62,15 @@ jobs: -D WITH_JPEG=off \ -S cmake -B build \ -D BUILD_SHARED_LIBS=on \ - -D ENABLE_TESTING=on - cmake --build build --config Release --parallel 2 + -D ENABLE_TESTING=on \ + -D Ninja + cmake --build build --parallel 2 - name: Run LAMMPS executable - shell: bash run: | - ./build/Release/lmp.exe -h - ./build/Release/lmp.exe -in bench/in.lj + ./build/lmp.exe -h + ./build/lmp.exe -in bench/in.lj - name: Run Unit Tests working-directory: build - shell: bash - run: ctest -V -C Release -E FixTimestep:python_move_nve + run: ctest -V -E FixTimestep:python_move_nve From 73fdd66f039767a6f4ecccb47345915dbe381393 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 14:40:31 -0400 Subject: [PATCH 169/355] correct syntax issue --- .github/workflows/compile-msvc.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/compile-msvc.yml b/.github/workflows/compile-msvc.yml index e2d9a3628a..c33b632ee7 100644 --- a/.github/workflows/compile-msvc.yml +++ b/.github/workflows/compile-msvc.yml @@ -21,8 +21,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 - uses: ilammy/msvc-dev-cmd@v1 + - uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x64 + - uses: actions/checkout@v4 with: fetch-depth: 2 From 11172b1c6248bf93f80b557d91eb7062d1ac9e5d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 14:42:50 -0400 Subject: [PATCH 170/355] use different structure --- .github/workflows/compile-msvc.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/compile-msvc.yml b/.github/workflows/compile-msvc.yml index c33b632ee7..0a5fbb9685 100644 --- a/.github/workflows/compile-msvc.yml +++ b/.github/workflows/compile-msvc.yml @@ -21,13 +21,15 @@ jobs: steps: - name: Checkout repository - - uses: ilammy/msvc-dev-cmd@v1 - with: - arch: x64 - - uses: actions/checkout@v4 + uses: actions/checkout@v4 with: fetch-depth: 2 + - name: Enable MSVC++ + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x64 + - name: Install Ccache run: | choco install ccache From b890aee7de124d7f6a4595667fedbc45b7da704a Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 14:55:01 -0400 Subject: [PATCH 171/355] update alternate action from GitHub marketplace --- .github/workflows/compile-msvc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/compile-msvc.yml b/.github/workflows/compile-msvc.yml index 0a5fbb9685..1596ec0994 100644 --- a/.github/workflows/compile-msvc.yml +++ b/.github/workflows/compile-msvc.yml @@ -26,7 +26,7 @@ jobs: fetch-depth: 2 - name: Enable MSVC++ - uses: ilammy/msvc-dev-cmd@v1 + uses: TheMrMilchmann/setup-msvc-dev@v3 with: arch: x64 From 34bd2c4f2ad7e55951056eac49c297947e4fdf3d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 14:58:46 -0400 Subject: [PATCH 172/355] use our own fork --- .github/workflows/compile-msvc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/compile-msvc.yml b/.github/workflows/compile-msvc.yml index 1596ec0994..a8b2398cc5 100644 --- a/.github/workflows/compile-msvc.yml +++ b/.github/workflows/compile-msvc.yml @@ -26,7 +26,7 @@ jobs: fetch-depth: 2 - name: Enable MSVC++ - uses: TheMrMilchmann/setup-msvc-dev@v3 + uses: lammps/setup-msvc-dev@v3 with: arch: x64 From 06bee65a1aee07254201e62170e31a4c1010dfe8 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 15:10:11 -0400 Subject: [PATCH 173/355] use windows style pathnames --- .github/workflows/compile-msvc.yml | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/.github/workflows/compile-msvc.yml b/.github/workflows/compile-msvc.yml index a8b2398cc5..c5a40dec45 100644 --- a/.github/workflows/compile-msvc.yml +++ b/.github/workflows/compile-msvc.yml @@ -57,23 +57,13 @@ jobs: ccache -z python3 -m pip install numpy python3 -m pip install pyyaml - cmake -C cmake/presets/windows.cmake \ - -D CMAKE_CXX_COMPILER_LAUNCHER=$(which ccache) \ - -D CMAKE_C_COMPILER_LAUNCHER=$(which ccache) \ - -D DOWNLOAD_POTENTIALS=off \ - -D PKG_PYTHON=on \ - -D WITH_PNG=off \ - -D WITH_JPEG=off \ - -S cmake -B build \ - -D BUILD_SHARED_LIBS=on \ - -D ENABLE_TESTING=on \ - -D Ninja + cmake -C cmake\presets\windows.cmake -D CMAKE_CXX_COMPILER=cl -D CMAKE_CXX_COMPILER_LAUNCHER=ccache -D CMAKE_C_COMPILER=cl -D CMAKE_C_COMPILER_LAUNCHER=ccache -D DOWNLOAD_POTENTIALS=off -D PKG_PYTHON=on -D WITH_PNG=off -D WITH_JPEG=off -S cmake -B build -D BUILD_SHARED_LIBS=on -D ENABLE_TESTING=on -G Ninja cmake --build build --parallel 2 - name: Run LAMMPS executable run: | - ./build/lmp.exe -h - ./build/lmp.exe -in bench/in.lj + build\lmp.exe -h + build\lmp.exe -in bench\in.lj - name: Run Unit Tests working-directory: build From b372aa0bdcf357dd2d93970d6c1d1a5bc897417d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 15:18:27 -0400 Subject: [PATCH 174/355] try to skip using Fortran --- .github/workflows/compile-msvc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/compile-msvc.yml b/.github/workflows/compile-msvc.yml index c5a40dec45..b2d07f084d 100644 --- a/.github/workflows/compile-msvc.yml +++ b/.github/workflows/compile-msvc.yml @@ -57,7 +57,7 @@ jobs: ccache -z python3 -m pip install numpy python3 -m pip install pyyaml - cmake -C cmake\presets\windows.cmake -D CMAKE_CXX_COMPILER=cl -D CMAKE_CXX_COMPILER_LAUNCHER=ccache -D CMAKE_C_COMPILER=cl -D CMAKE_C_COMPILER_LAUNCHER=ccache -D DOWNLOAD_POTENTIALS=off -D PKG_PYTHON=on -D WITH_PNG=off -D WITH_JPEG=off -S cmake -B build -D BUILD_SHARED_LIBS=on -D ENABLE_TESTING=on -G Ninja + cmake -C cmake\presets\windows.cmake -D CMAKE_CXX_COMPILER=cl -D CMAKE_CXX_COMPILER_LAUNCHER=ccache -D CMAKE_C_COMPILER=cl -D CMAKE_C_COMPILER_LAUNCHER=ccache -D CMAKE_Fortran_COMPILER="" -D DOWNLOAD_POTENTIALS=off -D PKG_PYTHON=on -D WITH_PNG=off -D WITH_JPEG=off -S cmake -B build -D BUILD_SHARED_LIBS=on -D ENABLE_TESTING=on -G Ninja cmake --build build --parallel 2 - name: Run LAMMPS executable From 0b7ba6f8c15dd252abf1e96dbe8cc7ff5d55291c Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 15:23:55 -0400 Subject: [PATCH 175/355] let ninja decide how many processes to launch --- .github/workflows/compile-msvc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/compile-msvc.yml b/.github/workflows/compile-msvc.yml index b2d07f084d..2cc4da1e9a 100644 --- a/.github/workflows/compile-msvc.yml +++ b/.github/workflows/compile-msvc.yml @@ -58,7 +58,7 @@ jobs: python3 -m pip install numpy python3 -m pip install pyyaml cmake -C cmake\presets\windows.cmake -D CMAKE_CXX_COMPILER=cl -D CMAKE_CXX_COMPILER_LAUNCHER=ccache -D CMAKE_C_COMPILER=cl -D CMAKE_C_COMPILER_LAUNCHER=ccache -D CMAKE_Fortran_COMPILER="" -D DOWNLOAD_POTENTIALS=off -D PKG_PYTHON=on -D WITH_PNG=off -D WITH_JPEG=off -S cmake -B build -D BUILD_SHARED_LIBS=on -D ENABLE_TESTING=on -G Ninja - cmake --build build --parallel 2 + cmake --build build - name: Run LAMMPS executable run: | From 80230746508ea589c4765a840b2640aa15898c7b Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 15:30:40 -0400 Subject: [PATCH 176/355] build release version --- .github/workflows/compile-msvc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/compile-msvc.yml b/.github/workflows/compile-msvc.yml index 2cc4da1e9a..fb702b9d15 100644 --- a/.github/workflows/compile-msvc.yml +++ b/.github/workflows/compile-msvc.yml @@ -57,7 +57,7 @@ jobs: ccache -z python3 -m pip install numpy python3 -m pip install pyyaml - cmake -C cmake\presets\windows.cmake -D CMAKE_CXX_COMPILER=cl -D CMAKE_CXX_COMPILER_LAUNCHER=ccache -D CMAKE_C_COMPILER=cl -D CMAKE_C_COMPILER_LAUNCHER=ccache -D CMAKE_Fortran_COMPILER="" -D DOWNLOAD_POTENTIALS=off -D PKG_PYTHON=on -D WITH_PNG=off -D WITH_JPEG=off -S cmake -B build -D BUILD_SHARED_LIBS=on -D ENABLE_TESTING=on -G Ninja + cmake -C cmake\presets\windows.cmake -D CMAKE_CXX_COMPILER=cl -D CMAKE_CXX_COMPILER_LAUNCHER=ccache -D CMAKE_C_COMPILER=cl -D CMAKE_C_COMPILER_LAUNCHER=ccache -D CMAKE_Fortran_COMPILER="" -D DOWNLOAD_POTENTIALS=off -D PKG_PYTHON=on -D WITH_PNG=off -D WITH_JPEG=off -S cmake -B build -D BUILD_SHARED_LIBS=on -D ENABLE_TESTING=on -D CMAKE_BUILD_TYPE=Release -G Ninja cmake --build build - name: Run LAMMPS executable From debda721700d44d45e0b2eaf64a615c367e31940 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 15:51:52 -0400 Subject: [PATCH 177/355] print ccache statistics after compilation again --- .github/workflows/compile-msvc.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/compile-msvc.yml b/.github/workflows/compile-msvc.yml index fb702b9d15..63b8132592 100644 --- a/.github/workflows/compile-msvc.yml +++ b/.github/workflows/compile-msvc.yml @@ -59,6 +59,7 @@ jobs: python3 -m pip install pyyaml cmake -C cmake\presets\windows.cmake -D CMAKE_CXX_COMPILER=cl -D CMAKE_CXX_COMPILER_LAUNCHER=ccache -D CMAKE_C_COMPILER=cl -D CMAKE_C_COMPILER_LAUNCHER=ccache -D CMAKE_Fortran_COMPILER="" -D DOWNLOAD_POTENTIALS=off -D PKG_PYTHON=on -D WITH_PNG=off -D WITH_JPEG=off -S cmake -B build -D BUILD_SHARED_LIBS=on -D ENABLE_TESTING=on -D CMAKE_BUILD_TYPE=Release -G Ninja cmake --build build + ccache -s - name: Run LAMMPS executable run: | From 2e58e4c4284172e4a0e67e8354bc657b48073b71 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 15:57:34 -0400 Subject: [PATCH 178/355] Simplify some more. don't install undetected MPI. --- .github/workflows/compile-msvc.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/compile-msvc.yml b/.github/workflows/compile-msvc.yml index 63b8132592..e384f191e5 100644 --- a/.github/workflows/compile-msvc.yml +++ b/.github/workflows/compile-msvc.yml @@ -32,13 +32,7 @@ jobs: - name: Install Ccache run: | - choco install ccache - choco install ninja - - - name: Install MSMPI - run: | - nuget install MSMPIsdk - nuget install MSMPIDIST + choco install ccache ninja -y - name: Set up ccache uses: actions/cache@v4 From 62bfd7dc74329f5374641dd210df4aea0966e496 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 16:05:55 -0400 Subject: [PATCH 179/355] Move Linux unit test workflow to PR #4304 --- .github/workflows/unittest-linux.yml | 76 ---------------------------- 1 file changed, 76 deletions(-) delete mode 100644 .github/workflows/unittest-linux.yml diff --git a/.github/workflows/unittest-linux.yml b/.github/workflows/unittest-linux.yml deleted file mode 100644 index 49477f7765..0000000000 --- a/.github/workflows/unittest-linux.yml +++ /dev/null @@ -1,76 +0,0 @@ -# GitHub action to build LAMMPS on Linux and run standard unit tests -name: "Unittest for Linux" - -on: - push: - branches: - - develop - - quick-regression - pull_request: - branches: - - develop - - workflow_dispatch: - -jobs: - build: - name: Quick Regression Test - if: ${{ github.repository == 'lammps/lammps' }} - runs-on: ubuntu-latest - env: - CCACHE_DIR: ${{ github.workspace }}/.ccache - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 2 - - - name: Install extra packages - run: | - sudo apt-get install -y ccache mold ninja-build - sudo apt-get install -y libeigen3-dev libgsl-dev libcurl4-openssl-dev python3-dev - - - name: Create Build Environment - run: mkdir build - - - name: Set up ccache - uses: actions/cache@v4 - with: - path: ${{ env.CCACHE_DIR }} - key: linux-unit-ccache-${{ github.sha }} - restore-keys: linux-unit-ccache- - - - name: Building LAMMPS via CMake - shell: bash - run: | - ccache -z - python3 -m venv linuxenv - source linuxenv/bin/activate - python3 -m pip install numpy - python3 -m pip install pyyaml - cmake -S cmake -B build \ - -C cmake/presets/gcc.cmake \ - -C cmake/presets/most.cmake \ - -D CMAKE_CXX_COMPILER_LAUNCHER=ccache \ - -D CMAKE_C_COMPILER_LAUNCHER=ccache \ - -D BUILD_SHARED_LIBS=on \ - -D LAMMPS_SIZES=bigbig \ - -D DOWNLOAD_POTENTIALS=off \ - -D ENABLE_TESTING=on \ - -D PKG_MANIFOLD=on \ - -D PKG_ML-PACE=on \ - -D PKG_ML-RANN=on \ - -D PKG_RHEO=on \ - -D PKG_PTM=on \ - -D PKG_PYTHON=on \ - -D PKG_QTB=on \ - -D PKG_SMTBQ=on \ - -G Ninja - cmake --build build - ccache -s - - - name: Run Tests - working-directory: build - shell: bash - run: ctest -V From 6fb50cbdc140f9a01ab9fc79f2f2b2fdaba58621 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 23:16:02 -0400 Subject: [PATCH 180/355] integrate quick regression support into regression tester --- .github/workflows/full-regression.yml | 6 +- .github/workflows/quick-regression.yml | 36 ++- tools/regression-tests/config_serial.yaml | 2 +- .../{get-quick-list.py => get_quick_list.py} | 14 +- tools/regression-tests/run_tests.py | 306 +++++++++++------- 5 files changed, 220 insertions(+), 144 deletions(-) rename tools/regression-tests/{get-quick-list.py => get_quick_list.py} (97%) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index 94068252a7..d208538a7d 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -11,14 +11,14 @@ on: jobs: build: - name: Build + name: Build LAMMPS # restrict to official LAMMPS repository if: ${{ github.repository == 'lammps/lammps' }} runs-on: ubuntu-latest env: CCACHE_DIR: ${{ github.workspace }}/.ccache strategy: - max-parallel: 2 + max-parallel: 4 matrix: idx: [ 0, 1, 2, 3 ] @@ -71,7 +71,7 @@ jobs: cmake --build build ccache -s - - name: Full regression tests, splitting the top-level example input into 4 lists + - name: Run Full Regression Tests shell: bash run: | source linuxenv/bin/activate diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index 89da0bfb0a..297b45c5ec 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -14,11 +14,16 @@ on: jobs: build: - name: Quick Regression Test + name: Build LAMMPS + # restrict to official LAMMPS repository if: ${{ github.repository == 'lammps/lammps' }} runs-on: ubuntu-latest env: CCACHE_DIR: ${{ github.workspace }}/.ccache + strategy: + max-parallel: 4 + matrix: + idx: [ 0, 1, 2, 3 ] steps: - name: Checkout repository @@ -70,21 +75,38 @@ jobs: cmake --build build ccache -s - - name: Run Selected Regression Tests + - name: Run Regression Tests for Modified Styles shell: bash run: | source linuxenv/bin/activate - python3 tools/regression-tests/get-quick-list.py python3 tools/regression-tests/run_tests.py \ --lmp-bin=build/lmp \ --config-file=tools/regression-tests/config_serial.yaml \ - --list-input=input_list.txt - tar -cvf quick-regression-test.tar run.log progress.yaml + --examples-top-level=examples --quick --quick-branch=origin/develop --num-workers=4 + + python3 tools/regression-tests/run_tests.py \ + --lmp-bin=build/lmp \ + --config-file=tools/regression-tests/config_serial.yaml \ + --list-input=input-list-${{ matrix.idx }}.txt \ + --output-file=output-${{ matrix.idx }}.xml \ + --progress-file=progress-${{ matrix.idx }}.yaml \ + --log-file=run-${{ matrix.idx }}.log + + tar -cvf quick-regression-test-${{ matrix.idx }}.tar run-${{ matrix.idx }}.log progress-${{ matrix.idx }}.yaml output-${{ matrix.idx }}.xml - name: Upload artifacts uses: actions/upload-artifact@v4 with: - name: quick-regression-test-artifact - path: quick-regression-test.tar + name: quick-regression-test-artifact-${{ matrix.idx }} + path: quick-regression-test-${{ matrix.idx }}.tar + merge: + runs-on: ubuntu-latest + needs: build + steps: + - name: Merge Artifacts + uses: actions/upload-artifact/merge@v4 + with: + name: merged-quick-regresssion-artifact + pattern: quick-regression-test-artifact-* diff --git a/tools/regression-tests/config_serial.yaml b/tools/regression-tests/config_serial.yaml index ce984bb2b8..fb79c301f1 100644 --- a/tools/regression-tests/config_serial.yaml +++ b/tools/regression-tests/config_serial.yaml @@ -36,7 +36,7 @@ in.bucky-plus-cnt*, ] - timeout: 10 + timeout: 30 nugget: 1.0 epsilon: 1e-16 diff --git a/tools/regression-tests/get-quick-list.py b/tools/regression-tests/get_quick_list.py similarity index 97% rename from tools/regression-tests/get-quick-list.py rename to tools/regression-tests/get_quick_list.py index 9af91b139c..457137a7b9 100644 --- a/tools/regression-tests/get-quick-list.py +++ b/tools/regression-tests/get_quick_list.py @@ -244,7 +244,7 @@ def get_examples_using_styles(regex, examples='examples'): with open(filename) as f: for line in f: if commands.match(line): - inputs.append(filename) + inputs.append(str(filename)) break return inputs @@ -258,14 +258,8 @@ if __name__ == "__main__": regex = make_regex(styles) if regex: inputs = get_examples_using_styles(regex, os.path.join(LAMMPS_DIR,'examples')) - - print("Suggested inputs for testing:") - # input_list.txt is used for the regression tester tool - with open('input_list.txt', 'w') as f: - for inp in inputs: - print(inp) - f.write(str(inp) + '\n') - + else: + inputs = [] print("Found changes to the following styles:") print("Commands: ", styles['command']) print("Atom styles: ", styles['atom']) @@ -282,3 +276,5 @@ if __name__ == "__main__": print("Region styles: ", styles['region']) print("Integrate styles: ", styles['integrate']) print("Minimize styles: ", styles['minimize']) + + print("Example input files affected: ", len(inputs)) diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index 82da5bfac5..d369eec9c8 100644 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -29,7 +29,7 @@ TODO: + be able to be invoked from run_tests in the lammps-testing infrastruture The following Python packages need to be installed into an activated environment: - + python3 -m venv testing-env source testing-env/bin/activate pip install numpy pyyaml junit_xml @@ -54,16 +54,16 @@ Example usage: 4) Specify a list of example input scripts (e.g. obtained from running tools/regression-tests/get-quick-list.py) python3 run_tests.py --lmp-bin=/path/to/lmp_binary --config-file=/path/to/config/file/config.yaml \ --list-input=input_list.txt - + 5) Test a LAMMPS binary with the whole top-level /examples folder in a LAMMPS source tree python3 run_tests.py --lmp-bin=/path/to/lmp_binary --examples-top-level=/path/to/lammps/examples - 6) Analyze the LAMMPS binary annd whole top-level /examples folder in a LAMMPS source tree + 6) Analyze the LAMMPS binary and whole top-level /examples folder in a LAMMPS source tree and generate separate input lists for 8 workers: python3 run_tests.py --lmp-bin=/path/to/lmp_binary --examples-top-level=/path/to/lammps/examples \ --analyze --num-workers=8 - The output of this run is 8 files folder-list-[0-7].txt that lists the subfolders + The output of this run is 8 files folder-list-[0-7].txt that lists the subfolders and 8 files input-list-[0-7].txt that lists the input scripts under the top-level example folders. With these lists, one can launch multiple instances of run_tests.py simultaneously each with a list of example subfolders (Case 3), or with a list of input scripts (Case 4). @@ -76,6 +76,7 @@ import logging import os import re import subprocess +import sys #from multiprocessing import Pool # need "pip install numpy pyyaml" @@ -90,6 +91,13 @@ try: except ImportError: from yaml import SafeLoader as Loader +# infer top level LAMMPS dir from filename +LAMMPS_DIR = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..')) + +# import git interface module +sys.path.append(os.path.realpath(os.path.join(LAMMPS_DIR, 'tools', 'regression-tests'))) +import get_quick_list + ''' data structure to store the test result ''' @@ -104,11 +112,11 @@ class TestResult: ''' Iterate over a list of input folders and scripts using the given lmp_binary and the testing configuration - lmp_binary : full path to the LAMMPS binary + lmp_binary : full path to the LAMMPS binary input_folder : the absolute path to the input files input_list : list of the input scripts under the input_folder config : the dict that contains the test configuration - + output_buf: placeholder for storing the output of a given worker return @@ -186,7 +194,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file num_skipped = num_skipped + 1 test_id = test_id + 1 continue - + if 'packaged not installed' in status: msg = " + " + input + f" ({test_id+1}/{num_tests}): due to package not installed (see {progress_file})" logger.info(msg) @@ -196,14 +204,14 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file num_skipped = num_skipped + 1 test_id = test_id + 1 continue - + # if annotating input scripts with REG markers is True if using_markers == True: input_test = 'test.' + input if os.path.isfile(input) == True: if has_markers(input): process_markers(input, input_test) - + else: print(f"WARNING: {input} does not have REG markers") input_markers = input + '.markers' @@ -214,7 +222,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file os.system(cmd_str) generate_markers(input, input_markers) process_markers(input_markers, input_test) - + else: # else the same file name for testing input_test = input @@ -222,7 +230,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file str_t = " + " + input_test + f" ({test_id+1}/{num_tests})" logger.info(str_t) print(str_t) - + # check if a reference log file exists in the current folder: log.DDMMMYY.basename.g++.[nprocs] # assuming that input file names start with "in." (except in.disp, in.disp2 and in.dos in phonon/) basename = input_test[3:] @@ -260,15 +268,15 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file # if there is no ref log file and not running with valgrind if ref_logfile_exist == False and use_valgrind == False: max_np = 4 - + saved_nprocs = config['nprocs'] - + # if the maximum number of procs is different from the value in the configuration file # then override the setting for this particular input script if max_np != int(config['nprocs']): config['nprocs'] = str(max_np) - # store the value of nprocs + # store the value of nprocs nprocs = int(config['nprocs']) # if valgrind is used for mem check, the run command will be @@ -296,7 +304,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file error_line = line break logger.info(f" The run terminated with {input_test} gives the following output:") - logger.info(f" {error_line}") + logger.info(f" {error_line}") if "Unrecognized" in output: result.status = f"error, unrecognized command, package not installed, {error_line}" elif "Unknown" in output: @@ -334,7 +342,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file # if skip numerical checks, then skip the rest if skip_numerical_check == True: - msg = "completed, skipping numerical checks" + msg = "completed, skipping numerical checks" if use_valgrind == True: if "All heap blocks were freed" in error: msg += ", no memory leak" @@ -475,7 +483,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file if verbose == True: print("Quantities".ljust(width) + "Output".center(width) + "Reference".center(width) + "Abs Diff Check".center(width) + "Rel Diff Check".center(width)) - + # check if overrides for this input scipt is specified overrides = {} if 'overrides' in config: @@ -521,7 +529,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file abs_diff_check = "PASSED" rel_diff_check = "PASSED" - + if quantity in config['tolerance'] or quantity in overrides: if quantity in config['tolerance']: @@ -547,7 +555,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file else: # N/A means that tolerances are not defined in the config file abs_diff_check = "N/A" - rel_diff_check = "N/A" + rel_diff_check = "N/A" if verbose == True and abs_diff_check != "N/A" and rel_diff_check != "N/A": print(f"{thermo[irun]['keywords'][i].ljust(width)} {str(val).rjust(20)} {str(ref).rjust(20)} " @@ -580,7 +588,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file msg = f" all {num_checks} thermo checks passed." print(msg) logger.info(msg) - result.status = "passed" + result.status = "passed" num_passed = num_passed + 1 results.append(result) @@ -621,7 +629,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file of output and the inner list the values of the columns matching the header keywords for that step. ''' def extract_thermo(yamlFileName): - docs = "" + docs = "" with open(yamlFileName) as f: for line in f: m = re.search(r"^(keywords:.*$|data:$|---$|\.\.\.$| - \[.*\]$)", line) @@ -658,7 +666,7 @@ def extract_data_to_yaml(inputFileName): if "Loop" in line: reading = False docs += "...\n" - + if reading == True and "Step" not in line: if "WARNING" in line: continue @@ -718,7 +726,7 @@ def get_lammps_build_configuration(lmp_binary): operating_system = line if "Git info" in line: GitInfo = line - + row += 1 packages = packages.strip() @@ -729,7 +737,7 @@ def get_lammps_build_configuration(lmp_binary): if line != "": if "-DLAMMPS" in line: compile_flags += " " + line.strip() - + row += 1 return packages.split(" "), operating_system, GitInfo, compile_flags @@ -780,7 +788,7 @@ def execute(lmp_binary, config, input_file_name, generate_ref_yaml=False): for i in range(num_workers): args.append((input1, input2, output_buf)) - with Pool(num_workers) as pool: + with Pool(num_workers) as pool: results = pool.starmap(func, args) ''' def divide_into_N(original_list, N): @@ -807,7 +815,7 @@ def process_markers(inputFileName, outputFileName): # replace #REG:ADD with empty string (i.e. adding the text at the end of the line) data = data.replace("#REG:ADD", "") - # replace the line contaning #REG:SUB with a line with the text that follows this marker + # replace the line contaning #REG:SUB with a line with the text that follows this marker data = data.splitlines() separator="#REG:SUB" out = [] @@ -881,6 +889,8 @@ if __name__ == "__main__": list_input = "" list_subfolders = "" analyze = False + quick = False + quick_branch = "origin/develop" # distribute the total number of input scripts over the workers num_workers = 1 @@ -888,9 +898,9 @@ if __name__ == "__main__": # parse the arguments parser = ArgumentParser() parser.add_argument("--lmp-bin", dest="lmp_binary", default="", help="LAMMPS binary") - parser.add_argument("--config-file", dest="config_file", default=configFileName, - help="Configuration YAML file") - parser.add_argument("--examples-top-level", dest="example_toplevel", default="", help="Examples top-level") + parser.add_argument("--config-file", dest="config_file", default=configFileName, help="Configuration YAML file") + parser.add_argument("--examples-top-level", dest="example_toplevel", default=os.path.join(LAMMPS_DIR, 'examples'), + help="Examples top-level") parser.add_argument("--example-folders", dest="example_folders", default="", help="Example subfolders") parser.add_argument("--list-input", dest="list_input", default="", help="File that lists the input scripts") parser.add_argument("--list-subfolders", dest="list_subfolders", default="", help="File that lists the subfolders") @@ -904,8 +914,13 @@ if __name__ == "__main__": parser.add_argument("--output-file",dest="output", default=output_file, help="Output file") parser.add_argument("--log-file",dest="logfile", default=log_file, help="Log file") parser.add_argument("--progress-file",dest="progress_file", default=progress_file, help="Progress file") - parser.add_argument("--analyze",dest="analyze", action='store_true', default=False, + analyze = parser.add_mutually_exclusive_group() + analyze.add_argument("--analyze",dest="analyze", action='store_true', default=False, help="Analyze the testing folders and report statistics, not running the tests") + analyze.add_argument("--quick", dest="quick", action='store_true', default=False, + help="Determine which test inputs have commands changed between a branch and the head") + parser.add_argument("--quick-branch", dest="quick_branch", default=quick_branch, + help="Branch to which compare the current head to for changed styles") parser.add_argument("--skip-numerical-check",dest="skip_numerical_check", action='store_true', default=False, help="Generating reference data") @@ -924,11 +939,13 @@ if __name__ == "__main__": example_toplevel = args.example_toplevel if args.example_folders != "": example_subfolders = args.example_folders.split(';') - + genref = args.genref verbose = args.verbose log_file = args.logfile analyze = args.analyze + quick = args.quick + quick_branch = args.quick_branch skip_numerical_check = args.skip_numerical_check resume = args.resume progress_file = args.progress_file @@ -948,51 +965,20 @@ if __name__ == "__main__": inplace_input = True test_cases = [] - # if the example folders are not specified from the command-line argument --example-folders - # then use the path from --example-top-folder, or from the input-list read from a text file - if len(example_subfolders) == 0: - - # if the top level is specified - if len(example_toplevel) != 0: - # getting the list of all the input files because there are subfolders (e.g. PACKAGES) under the top level - cmd_str = f"find {example_toplevel} -name \"in.*\" " - p = subprocess.run(cmd_str, shell=True, text=True, capture_output=True) - input_list = p.stdout.split('\n') - input_list.remove("") - msg = f"\nThere are {len(input_list)} input scripts in total under the {example_toplevel} folder." + # generate list of input scripts with commands that have been changed + if quick: + headers = get_quick_list.changed_files_from_git(quick_branch) + print("headers ", headers) + styles = get_quick_list.get_command_from_header(headers, LAMMPS_DIR) + print("styles ", styles) + regex = get_quick_list.make_regex(styles) + print("regex ", regex) + if regex: + input_list = get_quick_list.get_examples_using_styles(regex, example_toplevel) + msg = f"\nThere are {len(input_list)} input scripts with changed styles relative to branch {quick_branch}." print(msg) logger.info(msg) - # get the input file list - # TODO: generate a list of tuples, each tuple contains a folder list for a worker, - # then use multiprocessing.Pool starmap() - folder_list = [] - for input in input_list: - folder = input.rsplit('/', 1)[0] - # unique folders in the list - if folder not in folder_list: - folder_list.append(folder) - - # divide the list of folders into num_workers chunks - sublists = divide_into_N(folder_list, num_workers) - - # write each chunk to a file - idx = 0 - for list_input in sublists: - filename = f"folder-list-{idx}.txt" - with open(filename, "w") as f: - for folder in list_input: - # count the number of input scripts in each folder - cmd_str = f"ls {folder}/in.* | wc -l" - p = subprocess.run(cmd_str, shell=True, text=True, capture_output=True) - num_input = p.stdout.split('\n')[0] - f.write(folder + ' ' + num_input + '\n') - f.close() - idx = idx + 1 - - # working on all the folders for now - example_subfolders = folder_list - # divide the list of input scripts into num_workers chunks sublists = divide_into_N(input_list, num_workers) @@ -1005,53 +991,125 @@ if __name__ == "__main__": f.write(inp + '\n') f.close() idx = idx + 1 - - # if a list of subfolders is provided from a text file (list_subfolders from the command-line argument) - elif len(list_subfolders) != 0: - num_inputscripts = 0 - with open(list_subfolders, "r") as f: - all_subfolders = f.read().splitlines() - f.close() - for line in all_subfolders: - if len(line) > 0: - # skip subfolders - if line[0] == '#': - continue - folder = line.split()[0] - example_subfolders.append(folder) - num_inputscripts += int(line.split()[1]) - msg = f"\nThere are {len(example_subfolders)} folders with {num_inputscripts} input scripts in total listed in {list_input}." - print(msg) - logger.info(msg) - - # if a list of input scripts is provided from a text file (list_input from the command-line argument) - elif len(list_input) != 0: - num_inputscripts = 0 - folder_list = [] - with open(list_input, "r") as f: - all_inputs = f.read().splitlines() - f.close() - - for line in all_inputs: - if len(line) > 0: - # skip input scripts - if line[0] == '#': - continue - input = line.split()[0] - folder = input.rsplit('/', 1)[0] - # unique folders in the list - if folder not in folder_list: - folder_list.append(folder) - example_inputs.append(input) - num_inputscripts += 1 - - example_subfolders = folder_list - msg = f"\nThere are {num_inputscripts} input scripts listed in {list_input}." - print(msg) - logger.info(msg) - else: - inplace_input = False + msg = f"\nThere are no input scripts with changed styles relative to branch {quick_branch}." + print(msg) + logger.info(msg) + for idx in range(0, num_workers): + try: + os.remove(f"folder-list-{idx}.txt") + except: + pass + try: + os.remove(f"input-list-{idx}.txt") + except: + pass + quit() + else: + # if the example folders are not specified from the command-line argument --example-folders + # then use the path from --example-top-folder, or from the input-list read from a text file + if len(example_subfolders) == 0: + + # if the top level is specified + if len(example_toplevel) != 0: + # getting the list of all the input files because there are subfolders (e.g. PACKAGES) under the top level + cmd_str = f"find {example_toplevel} -name \"in.*\" " + p = subprocess.run(cmd_str, shell=True, text=True, capture_output=True) + input_list = p.stdout.split('\n') + input_list.remove("") + msg = f"\nThere are {len(input_list)} input scripts in total under the {example_toplevel} folder." + print(msg) + logger.info(msg) + + # get the input file list + # TODO: generate a list of tuples, each tuple contains a folder list for a worker, + # then use multiprocessing.Pool starmap() + folder_list = [] + for input in input_list: + folder = input.rsplit('/', 1)[0] + # unique folders in the list + if folder not in folder_list: + folder_list.append(folder) + + # divide the list of folders into num_workers chunks + sublists = divide_into_N(folder_list, num_workers) + + # write each chunk to a file + idx = 0 + for list_input in sublists: + filename = f"folder-list-{idx}.txt" + with open(filename, "w") as f: + for folder in list_input: + # count the number of input scripts in each folder + cmd_str = f"ls {folder}/in.* | wc -l" + p = subprocess.run(cmd_str, shell=True, text=True, capture_output=True) + num_input = p.stdout.split('\n')[0] + f.write(folder + ' ' + num_input + '\n') + f.close() + idx = idx + 1 + + # working on all the folders for now + example_subfolders = folder_list + + # divide the list of input scripts into num_workers chunks + sublists = divide_into_N(input_list, num_workers) + + # write each chunk to a file + idx = 0 + for list_input in sublists: + filename = f"input-list-{idx}.txt" + with open(filename, "w") as f: + for inp in list_input: + f.write(inp + '\n') + f.close() + idx = idx + 1 + + # if a list of subfolders is provided from a text file (list_subfolders from the command-line argument) + elif len(list_subfolders) != 0: + num_inputscripts = 0 + with open(list_subfolders, "r") as f: + all_subfolders = f.read().splitlines() + f.close() + for line in all_subfolders: + if len(line) > 0: + # skip subfolders + if line[0] == '#': + continue + folder = line.split()[0] + example_subfolders.append(folder) + num_inputscripts += int(line.split()[1]) + msg = f"\nThere are {len(example_subfolders)} folders with {num_inputscripts} input scripts in total listed in {list_input}." + print(msg) + logger.info(msg) + + # if a list of input scripts is provided from a text file (list_input from the command-line argument) + elif len(list_input) != 0: + num_inputscripts = 0 + folder_list = [] + with open(list_input, "r") as f: + all_inputs = f.read().splitlines() + f.close() + + for line in all_inputs: + if len(line) > 0: + # skip input scripts + if line[0] == '#': + continue + input = line.split()[0] + folder = input.rsplit('/', 1)[0] + # unique folders in the list + if folder not in folder_list: + folder_list.append(folder) + example_inputs.append(input) + num_inputscripts += 1 + + example_subfolders = folder_list + msg = f"\nThere are {num_inputscripts} input scripts listed in {list_input}." + print(msg) + logger.info(msg) + + else: + inplace_input = False # if analyze the example folders (and split into separate lists for top-level examples), not running any test if analyze == True: @@ -1063,7 +1121,7 @@ if __name__ == "__main__": absolute_path = os.path.abspath(configFileName) print(f"\nRegression test configuration file:\n {absolute_path}") f.close() - + # check if lmp_binary is specified in the config yaml if lmp_binary == "": if config['lmp_binary'] == "": @@ -1091,7 +1149,7 @@ if __name__ == "__main__": pwd = p.stdout.split('\n')[0] pwd = os.path.abspath(pwd) print("\nWorking directory: " + pwd) - + progress_file_abs = pwd + "/" + progress_file last_progress = {} if resume == False: @@ -1124,7 +1182,7 @@ if __name__ == "__main__": for i in range(num_workers): args.append((input1, input2, output)) - with Pool(num_workers) as pool: + with Pool(num_workers) as pool: results = pool.starmap(func, args) ''' @@ -1204,9 +1262,9 @@ if __name__ == "__main__": print(msg) # optional: need to check if junit_xml packaged is already installed in the env - # generate a JUnit XML file + # generate a JUnit XML file with open(output_file, 'w') as f: - test_cases = [] + test_cases = [] for result in all_results: #print(f"{result.name}: {result.status}") case = TestCase(name=result.name, classname=result.name) From f39e795bca9d1f420e74c8ea7dff8d7fe2224809 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 31 Aug 2024 23:19:16 -0400 Subject: [PATCH 181/355] revert changes to create_box --- src/create_box.cpp | 56 +++++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/src/create_box.cpp b/src/create_box.cpp index 93e699e06b..8a74ffd7bd 100644 --- a/src/create_box.cpp +++ b/src/create_box.cpp @@ -49,13 +49,11 @@ void CreateBox::command(int narg, char **arg) Region *region = nullptr; int triclinic_general = 0; - if (strcmp(arg[1], "NULL") == 0) - triclinic_general = 1; + if (strcmp(arg[1],"NULL") == 0) triclinic_general = 1; else { region = domain->get_region_by_id(arg[1]); if (!region) error->all(FLERR, "Create_box region {} does not exist", arg[1]); - if (region->bboxflag == 0) - error->all(FLERR, "Create_box region does not support a bounding box"); + if (region->bboxflag == 0) error->all(FLERR, "Create_box region does not support a bounding box"); region->init(); } @@ -79,9 +77,9 @@ void CreateBox::command(int narg, char **arg) domain->boxlo[2] = region->extent_zlo; domain->boxhi[2] = region->extent_zhi; - // region is prism - // seutp restricted triclinic box - // set simulation domain from prism params + // region is prism + // seutp restricted triclinic box + // set simulation domain from prism params } else { domain->triclinic = 1; @@ -99,17 +97,17 @@ void CreateBox::command(int narg, char **arg) if (domain->dimension == 2) { if (domain->boxlo[2] >= 0.0 || domain->boxhi[2] <= 0.0) - error->all(FLERR, "Create_box region zlo/zhi for 2d simulation must straddle 0.0"); + error->all(FLERR,"Create_box region zlo/zhi for 2d simulation must straddle 0.0"); } - // setup general triclinic box (with no region) - // read next box extent arguments to create ABC edge vectors + origin - // define_general_triclinic() converts - // ABC edge vectors + origin to restricted triclinic + // setup general triclinic box (with no region) + // read next box extent arguments to create ABC edge vectors + origin + // define_general_triclinic() converts + // ABC edge vectors + origin to restricted triclinic } else if (triclinic_general) { if (!domain->lattice->is_general_triclinic()) - error->all(FLERR, "Create_box for general triclinic requires triclnic/general lattice"); + error->all(FLERR,"Create_box for general triclinic requires triclnic/general lattice"); if (iarg + 6 > narg) utils::missing_cmd_args(FLERR, "create_box general triclinic", error); @@ -123,50 +121,42 @@ void CreateBox::command(int narg, char **arg) if (domain->dimension == 2) if (clo != -0.5 || chi != 0.5) - error->all(FLERR, "Create_box for general triclinic requires clo = -0.5 and chi = 0.5"); + error->all(FLERR,"Create_box for general triclinic requires clo = -0.5 and chi = 0.5"); // use lattice2box() to generate origin and ABC vectors // origin = abc lo // ABC vectors = hi in one dim - origin - double avec[3], bvec[3], cvec[3], origin[3]; - double px, py, pz; + double avec[3],bvec[3],cvec[3],origin[3]; + double px,py,pz; - px = alo; - py = blo; - pz = clo; - domain->lattice->lattice2box(px, py, pz); + px = alo; py = blo; pz = clo; + domain->lattice->lattice2box(px,py,pz); origin[0] = px; origin[1] = py; origin[2] = pz; - px = ahi; - py = blo; - pz = clo; - domain->lattice->lattice2box(px, py, pz); + px = ahi; py = blo; pz = clo; + domain->lattice->lattice2box(px,py,pz); avec[0] = px - origin[0]; avec[1] = py - origin[1]; avec[2] = pz - origin[2]; - px = alo; - py = bhi; - pz = clo; - domain->lattice->lattice2box(px, py, pz); + px = alo; py = bhi; pz = clo; + domain->lattice->lattice2box(px,py,pz); bvec[0] = px - origin[0]; bvec[1] = py - origin[1]; bvec[2] = pz - origin[2]; - px = alo; - py = blo; - pz = chi; - domain->lattice->lattice2box(px, py, pz); + px = alo; py = blo; pz = chi; + domain->lattice->lattice2box(px,py,pz); cvec[0] = px - origin[0]; cvec[1] = py - origin[1]; cvec[2] = pz - origin[2]; // define general triclinic box within Domain class - domain->define_general_triclinic(avec, bvec, cvec, origin); + domain->define_general_triclinic(avec,bvec,cvec,origin); } // if molecular, zero out topology info From 0f1b7b5bd6b725740410511acae5c478e4c8bd43 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 1 Sep 2024 00:09:19 -0400 Subject: [PATCH 182/355] simplify even more --- .github/workflows/full-regression.yml | 9 ++++----- .github/workflows/quick-regression.yml | 9 ++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index d208538a7d..d1302836d3 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -30,8 +30,8 @@ jobs: - name: Install extra packages run: | - sudo apt-get install -y ccache ninja-build - sudo apt-get install -y libeigen3-dev libgsl-dev libcurl4-openssl-dev python3-dev + sudo apt-get install -y ccache ninja-build libeigen3-dev \ + libgsl-dev libcurl4-openssl-dev python3-dev - name: Create Build Environment run: mkdir build @@ -49,9 +49,8 @@ jobs: ccache -z python3 -m venv linuxenv source linuxenv/bin/activate - python3 -m pip install numpy - python3 -m pip install pyyaml - python3 -m pip install junit_xml + python3 -m pip install --upgrade pip + python3 -m pip install numpy pyyaml junit_xml cmake -S cmake -B build \ -C cmake/presets/gcc.cmake \ -C cmake/presets/most.cmake \ diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index 297b45c5ec..e3feb637d7 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -34,8 +34,8 @@ jobs: - name: Install extra packages run: | - sudo apt-get install -y ccache ninja-build - sudo apt-get install -y libeigen3-dev libgsl-dev libcurl4-openssl-dev python3-dev + sudo apt-get install -y ccache ninja-build libeigen3-dev \ + libgsl-dev libcurl4-openssl-dev python3-dev - name: Create Build Environment run: mkdir build @@ -53,9 +53,8 @@ jobs: ccache -z python3 -m venv linuxenv source linuxenv/bin/activate - python3 -m pip install numpy - python3 -m pip install pyyaml - python3 -m pip install junit_xml + python3 -m pip install --upgrade pip + python3 -m pip install numpy pyyaml junit_xml cmake -S cmake -B build \ -C cmake/presets/gcc.cmake \ -C cmake/presets/most.cmake \ From af747ac6c00795114d02f10368e49deff800a0fe Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 1 Sep 2024 00:17:25 -0400 Subject: [PATCH 183/355] restore old code structure so we can test subsets again --- tools/regression-tests/run_tests.py | 192 ++++++++++++++-------------- 1 file changed, 96 insertions(+), 96 deletions(-) diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index d369eec9c8..e53fcc6126 100644 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -899,8 +899,7 @@ if __name__ == "__main__": parser = ArgumentParser() parser.add_argument("--lmp-bin", dest="lmp_binary", default="", help="LAMMPS binary") parser.add_argument("--config-file", dest="config_file", default=configFileName, help="Configuration YAML file") - parser.add_argument("--examples-top-level", dest="example_toplevel", default=os.path.join(LAMMPS_DIR, 'examples'), - help="Examples top-level") + parser.add_argument("--examples-top-level", dest="example_toplevel", default="", help="Examples top-level") parser.add_argument("--example-folders", dest="example_folders", default="", help="Example subfolders") parser.add_argument("--list-input", dest="list_input", default="", help="File that lists the input scripts") parser.add_argument("--list-subfolders", dest="list_subfolders", default="", help="File that lists the subfolders") @@ -974,6 +973,7 @@ if __name__ == "__main__": regex = get_quick_list.make_regex(styles) print("regex ", regex) if regex: + if not example_toplevel: example_toplevel = os.path.join(LAMMPS_DIR, 'examples') input_list = get_quick_list.get_examples_using_styles(regex, example_toplevel) msg = f"\nThere are {len(input_list)} input scripts with changed styles relative to branch {quick_branch}." print(msg) @@ -1005,111 +1005,111 @@ if __name__ == "__main__": except: pass quit() - else: - # if the example folders are not specified from the command-line argument --example-folders - # then use the path from --example-top-folder, or from the input-list read from a text file - if len(example_subfolders) == 0: - # if the top level is specified - if len(example_toplevel) != 0: - # getting the list of all the input files because there are subfolders (e.g. PACKAGES) under the top level - cmd_str = f"find {example_toplevel} -name \"in.*\" " - p = subprocess.run(cmd_str, shell=True, text=True, capture_output=True) - input_list = p.stdout.split('\n') - input_list.remove("") - msg = f"\nThere are {len(input_list)} input scripts in total under the {example_toplevel} folder." - print(msg) - logger.info(msg) + # if the example folders are not specified from the command-line argument --example-folders + # then use the path from --example-top-folder, or from the input-list read from a text file + elif len(example_subfolders) == 0: - # get the input file list - # TODO: generate a list of tuples, each tuple contains a folder list for a worker, - # then use multiprocessing.Pool starmap() - folder_list = [] - for input in input_list: - folder = input.rsplit('/', 1)[0] - # unique folders in the list - if folder not in folder_list: - folder_list.append(folder) + # if the top level is specified + if len(example_toplevel) != 0: + # getting the list of all the input files because there are subfolders (e.g. PACKAGES) under the top level + cmd_str = f"find {example_toplevel} -name \"in.*\" " + p = subprocess.run(cmd_str, shell=True, text=True, capture_output=True) + input_list = p.stdout.split('\n') + input_list.remove("") + msg = f"\nThere are {len(input_list)} input scripts in total under the {example_toplevel} folder." + print(msg) + logger.info(msg) - # divide the list of folders into num_workers chunks - sublists = divide_into_N(folder_list, num_workers) + # get the input file list + # TODO: generate a list of tuples, each tuple contains a folder list for a worker, + # then use multiprocessing.Pool starmap() + folder_list = [] + for input in input_list: + folder = input.rsplit('/', 1)[0] + # unique folders in the list + if folder not in folder_list: + folder_list.append(folder) - # write each chunk to a file - idx = 0 - for list_input in sublists: - filename = f"folder-list-{idx}.txt" - with open(filename, "w") as f: - for folder in list_input: - # count the number of input scripts in each folder - cmd_str = f"ls {folder}/in.* | wc -l" - p = subprocess.run(cmd_str, shell=True, text=True, capture_output=True) - num_input = p.stdout.split('\n')[0] - f.write(folder + ' ' + num_input + '\n') - f.close() - idx = idx + 1 + # divide the list of folders into num_workers chunks + sublists = divide_into_N(folder_list, num_workers) - # working on all the folders for now - example_subfolders = folder_list - - # divide the list of input scripts into num_workers chunks - sublists = divide_into_N(input_list, num_workers) - - # write each chunk to a file - idx = 0 - for list_input in sublists: - filename = f"input-list-{idx}.txt" - with open(filename, "w") as f: - for inp in list_input: - f.write(inp + '\n') - f.close() - idx = idx + 1 - - # if a list of subfolders is provided from a text file (list_subfolders from the command-line argument) - elif len(list_subfolders) != 0: - num_inputscripts = 0 - with open(list_subfolders, "r") as f: - all_subfolders = f.read().splitlines() + # write each chunk to a file + idx = 0 + for list_input in sublists: + filename = f"folder-list-{idx}.txt" + with open(filename, "w") as f: + for folder in list_input: + # count the number of input scripts in each folder + cmd_str = f"ls {folder}/in.* | wc -l" + p = subprocess.run(cmd_str, shell=True, text=True, capture_output=True) + num_input = p.stdout.split('\n')[0] + f.write(folder + ' ' + num_input + '\n') f.close() - for line in all_subfolders: - if len(line) > 0: - # skip subfolders - if line[0] == '#': - continue - folder = line.split()[0] - example_subfolders.append(folder) - num_inputscripts += int(line.split()[1]) - msg = f"\nThere are {len(example_subfolders)} folders with {num_inputscripts} input scripts in total listed in {list_input}." - print(msg) - logger.info(msg) + idx = idx + 1 - # if a list of input scripts is provided from a text file (list_input from the command-line argument) - elif len(list_input) != 0: - num_inputscripts = 0 - folder_list = [] - with open(list_input, "r") as f: - all_inputs = f.read().splitlines() + # working on all the folders for now + example_subfolders = folder_list + + # divide the list of input scripts into num_workers chunks + sublists = divide_into_N(input_list, num_workers) + + # write each chunk to a file + idx = 0 + for list_input in sublists: + filename = f"input-list-{idx}.txt" + with open(filename, "w") as f: + for inp in list_input: + f.write(inp + '\n') f.close() + idx = idx + 1 - for line in all_inputs: - if len(line) > 0: - # skip input scripts - if line[0] == '#': - continue - input = line.split()[0] - folder = input.rsplit('/', 1)[0] - # unique folders in the list - if folder not in folder_list: - folder_list.append(folder) - example_inputs.append(input) - num_inputscripts += 1 + # if a list of subfolders is provided from a text file (list_subfolders from the command-line argument) + elif len(list_subfolders) != 0: + num_inputscripts = 0 + with open(list_subfolders, "r") as f: + all_subfolders = f.read().splitlines() + f.close() + for line in all_subfolders: + if len(line) > 0: + # skip subfolders + if line[0] == '#': + continue + folder = line.split()[0] + example_subfolders.append(folder) + num_inputscripts += int(line.split()[1]) + msg = f"\nThere are {len(example_subfolders)} folders with {num_inputscripts} input scripts in total listed in {list_input}." + print(msg) + logger.info(msg) - example_subfolders = folder_list - msg = f"\nThere are {num_inputscripts} input scripts listed in {list_input}." - print(msg) - logger.info(msg) + # if a list of input scripts is provided from a text file (list_input from the command-line argument) + elif len(list_input) != 0: + num_inputscripts = 0 + folder_list = [] + with open(list_input, "r") as f: + all_inputs = f.read().splitlines() + f.close() - else: - inplace_input = False + for line in all_inputs: + if len(line) > 0: + # skip input scripts + if line[0] == '#': + continue + input = line.split()[0] + folder = input.rsplit('/', 1)[0] + # unique folders in the list + if folder not in folder_list: + folder_list.append(folder) + example_inputs.append(input) + num_inputscripts += 1 + + example_subfolders = folder_list + msg = f"\nThere are {num_inputscripts} input scripts listed in {list_input}." + print(msg) + logger.info(msg) + + else: + inplace_input = False # if analyze the example folders (and split into separate lists for top-level examples), not running any test if analyze == True: From 3ea061279597bb352aed0946746ebaba72cff01b Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 1 Sep 2024 00:37:59 -0400 Subject: [PATCH 184/355] small cleanups --- .github/workflows/full-regression.yml | 1 + tools/regression-tests/get_quick_list.py | 1 + tools/regression-tests/run_tests.py | 4 +--- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index d1302836d3..821481567d 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -27,6 +27,7 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 2 + show-progress: false - name: Install extra packages run: | diff --git a/tools/regression-tests/get_quick_list.py b/tools/regression-tests/get_quick_list.py index 457137a7b9..9ebcce0aa2 100644 --- a/tools/regression-tests/get_quick_list.py +++ b/tools/regression-tests/get_quick_list.py @@ -278,3 +278,4 @@ if __name__ == "__main__": print("Minimize styles: ", styles['minimize']) print("Example input files affected: ", len(inputs)) + print("inputs: ", inputs.sort()) diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index e53fcc6126..21f79b66de 100644 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -967,15 +967,13 @@ if __name__ == "__main__": # generate list of input scripts with commands that have been changed if quick: headers = get_quick_list.changed_files_from_git(quick_branch) - print("headers ", headers) styles = get_quick_list.get_command_from_header(headers, LAMMPS_DIR) - print("styles ", styles) regex = get_quick_list.make_regex(styles) - print("regex ", regex) if regex: if not example_toplevel: example_toplevel = os.path.join(LAMMPS_DIR, 'examples') input_list = get_quick_list.get_examples_using_styles(regex, example_toplevel) msg = f"\nThere are {len(input_list)} input scripts with changed styles relative to branch {quick_branch}." + msg += "\nChanged styles: " + str(styles) print(msg) logger.info(msg) From d3d9094ad06abfe918b955155747eacc106f0b8a Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 1 Sep 2024 00:44:23 -0400 Subject: [PATCH 185/355] update settings when the actions will be triggered automatically --- .github/workflows/full-regression.yml | 1 - .github/workflows/quick-regression.yml | 4 ---- 2 files changed, 5 deletions(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index 821481567d..d13e8eb385 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -5,7 +5,6 @@ on: push: branches: - develop - - quick-regression workflow_dispatch: diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index e3feb637d7..b6ca7c5618 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -2,10 +2,6 @@ name: "Quick Regression Test" on: - push: - branches: - - develop - - quick-regression pull_request: branches: - develop From aa901b205defba9889dd89120ab6fbc8c2fd074d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 1 Sep 2024 00:59:15 -0400 Subject: [PATCH 186/355] only run quick regression if there are actual input files to process --- .github/workflows/quick-regression.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index b6ca7c5618..7fc684be5d 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -79,13 +79,16 @@ jobs: --config-file=tools/regression-tests/config_serial.yaml \ --examples-top-level=examples --quick --quick-branch=origin/develop --num-workers=4 - python3 tools/regression-tests/run_tests.py \ + if [ -f input-list-${{ matrix.idx }}.txt ] + then \ + python3 tools/regression-tests/run_tests.py \ --lmp-bin=build/lmp \ --config-file=tools/regression-tests/config_serial.yaml \ --list-input=input-list-${{ matrix.idx }}.txt \ --output-file=output-${{ matrix.idx }}.xml \ --progress-file=progress-${{ matrix.idx }}.yaml \ --log-file=run-${{ matrix.idx }}.log + fi tar -cvf quick-regression-test-${{ matrix.idx }}.tar run-${{ matrix.idx }}.log progress-${{ matrix.idx }}.yaml output-${{ matrix.idx }}.xml From 93d11c376d0eccde167e304b0aa1971bd28b518d Mon Sep 17 00:00:00 2001 From: EiPi Fun Date: Sun, 1 Sep 2024 15:47:46 +0800 Subject: [PATCH 187/355] Fix small typo and errors, unifiy example inputs --- doc/src/pair_hbond_dreiding.rst | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/doc/src/pair_hbond_dreiding.rst b/doc/src/pair_hbond_dreiding.rst index 7e73f23b08..185da8b90e 100644 --- a/doc/src/pair_hbond_dreiding.rst +++ b/doc/src/pair_hbond_dreiding.rst @@ -18,28 +18,27 @@ Syntax .. code-block:: LAMMPS - pair_style style N inner_distance_cutoff outer_distance_cutoff angle_cutof + pair_style style N inner_distance_cutoff outer_distance_cutoff angle_cutoff * style = *hbond/dreiding/lj* or *hbond/dreiding/morse* -* n = cosine angle periodicity +* N = power of angle cosine (integer) * inner_distance_cutoff = global inner cutoff for Donor-Acceptor interactions (distance units) * outer_distance_cutoff = global cutoff for Donor-Acceptor interactions (distance units) -* angle_cutoff = global angle cutoff for Acceptor-Hydrogen-Donor -* interactions (degrees) +* angle_cutoff = global angle cutoff for Acceptor-Hydrogen-Donor interactions (degrees) Examples """""""" .. code-block:: LAMMPS - pair_style hybrid/overlay lj/cut 10.0 hbond/dreiding/lj 4 9.0 11.0 90 + pair_style hybrid/overlay lj/cut 10.0 hbond/dreiding/lj 4 9.0 11.0 90.0 pair_coeff 1 2 hbond/dreiding/lj 3 i 9.5 2.75 4 9.0 11.0 90.0 - pair_style hybrid/overlay lj/cut 10.0 hbond/dreiding/morse 2 9.0 11.0 90 - pair_coeff 1 2 hbond/dreiding/morse 3 i 3.88 1.7241379 2.9 2 9 11 90 + pair_style hybrid/overlay lj/cut 10.0 hbond/dreiding/morse 2 9.0 11.0 90.0 + pair_coeff 1 2 hbond/dreiding/morse 3 i 3.88 1.7241379 2.9 2 9.0 11.0 90.0 labelmap atom 1 C 2 O 3 H - pair_coeff C O hbond/dreiding/morse H i 3.88 1.7241379 2.9 2 9 11 90 + pair_coeff C O hbond/dreiding/morse H i 3.88 1.7241379 2.9 2 9.0 11.0 90.0 Description """"""""""" @@ -65,7 +64,7 @@ force field, given by: where :math:`r_{\rm in}` is the inner spline distance cutoff, :math:`r_{\rm out}` is the outer distance cutoff, :math:`\theta_c` is -the angle cutoff, and *n* is the cosine periodicity. +the angle cutoff, and *n* is the power of angle cosine. Here, *r* is the radial distance between the donor (D) and acceptor (A) atoms and :math:`\theta` is the bond angle between the acceptor, the From a143e0a1830914236cfd2fdf28300e405eaa6b2e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 1 Sep 2024 08:15:26 -0400 Subject: [PATCH 188/355] small corrections --- doc/src/pair_hbond_dreiding.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/src/pair_hbond_dreiding.rst b/doc/src/pair_hbond_dreiding.rst index 185da8b90e..ce19ff9e38 100644 --- a/doc/src/pair_hbond_dreiding.rst +++ b/doc/src/pair_hbond_dreiding.rst @@ -21,7 +21,7 @@ Syntax pair_style style N inner_distance_cutoff outer_distance_cutoff angle_cutoff * style = *hbond/dreiding/lj* or *hbond/dreiding/morse* -* N = power of angle cosine (integer) +* N = power of cosine of angle theta (integer) * inner_distance_cutoff = global inner cutoff for Donor-Acceptor interactions (distance units) * outer_distance_cutoff = global cutoff for Donor-Acceptor interactions (distance units) * angle_cutoff = global angle cutoff for Acceptor-Hydrogen-Donor interactions (degrees) @@ -64,7 +64,8 @@ force field, given by: where :math:`r_{\rm in}` is the inner spline distance cutoff, :math:`r_{\rm out}` is the outer distance cutoff, :math:`\theta_c` is -the angle cutoff, and *n* is the power of angle cosine. +the angle cutoff, and :math:`n` is the power of the cosine of the angle +:math:`\theta`. Here, *r* is the radial distance between the donor (D) and acceptor (A) atoms and :math:`\theta` is the bond angle between the acceptor, the @@ -216,7 +217,8 @@ These pair styles do not support the :doc:`pair_modify ` tail option for adding long-range tail corrections to energy and pressure. -These pair styles do not write their information to :doc:`binary restart files `, so pair_style and pair_coeff commands need to be +These pair styles do not write their information to :doc:`binary restart +files `, so pair_style and pair_coeff commands need to be re-specified in an input script that reads a restart file. These pair styles can only be used via the *pair* keyword of the From 1b5413189dd19961a74620a473ffa796ef355f0b Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 1 Sep 2024 08:15:58 -0400 Subject: [PATCH 189/355] Add more details to documentation of GitHub CLI "gh" --- doc/src/Build_development.rst | 38 ++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/doc/src/Build_development.rst b/doc/src/Build_development.rst index 9cd938280b..f315569b24 100644 --- a/doc/src/Build_development.rst +++ b/doc/src/Build_development.rst @@ -630,11 +630,35 @@ The following target are available for both, GNU make and CMake: GitHub command line interface ----------------------------- -GitHub is developing a `tool for the command line -`_ that interacts with the GitHub website via a -command called ``gh``. This can be extremely convenient when working -with a Git repository hosted on GitHub (like LAMMPS). It is thus highly -recommended to install it when doing LAMMPS development. +GitHub has developed a `command line tool `_ +to interact with the GitHub website via a command called ``gh``. +This is extremely convenient when working with a Git repository hosted +on GitHub (like LAMMPS). It is thus highly recommended to install it +when doing LAMMPS development. To use ``gh`` you must be within a git +checkout of a repository and you must obtain an authentication token +to connect your checkout with a GitHub user. This is done with the +command: ``gh auth login`` where you then have to follow the prompts. +Here are some examples: -The capabilities of the ``gh`` command is continually expanding, so -please see the documentation at https://cli.github.com/manual/ +.. list-table:: + :header-rows: 1 + :widths: 34 66 + + * - Command + - Description + * - ``gh pr list`` + - List currently open pull requests + * - ``gh pr checks 404`` + - Shows the status of all checks for pull request #404 + * - ``gh pr view 404`` + - Shows the description and recent comments for pull request #404 + * - ``gh co 404`` + - Check out the branch from pull request #404; set up for pushing changes + * - ``gh issue list`` + - List currently open issues + * - ``gh issue view 430 --comments`` + - Shows the description and all comments for issue #430 + +The capabilities of the ``gh`` command are continually expanding, so +for more details please see the documentation at https://cli.github.com/manual/ +or use ``gh --help`` or ``gh --help`` for embedded help. From da98d30cf791ec94887ec95ee193e7e8fd265271 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 1 Sep 2024 08:31:52 -0400 Subject: [PATCH 190/355] update README --- tools/regression-tests/README | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tools/regression-tests/README b/tools/regression-tests/README index 810b96e87c..eec11c19ff 100644 --- a/tools/regression-tests/README +++ b/tools/regression-tests/README @@ -34,13 +34,13 @@ Limitations: TODO: + keep track of the testing progress to resume the testing from the last checkpoint - + distribute the input list across multiple processes via multiprocessing, or + + distribute the input list across multiple processes via multiprocessing, or split the list of input scripts into separate runs (there are 800+ input script under the top-level examples) + be able to be invoked from run_tests in the lammps-testing infrastruture The following Python packages need to be installed into an activated environment: - + python3 -m venv testing-env source testing-env/bin/activate pip install numpy pyyaml junit_xml @@ -62,18 +62,24 @@ Example uses: python3 run_tests.py --lmp-bin=/path/to/lmp_binary --config-file=/path/to/config/file/config.yaml \ --list-input=list_subfolders1.txt --output-file=output1.txt --progress-file=progress1.yaml \ --log-file=run1.log - + 4) Test a LAMMPS binary with the whole top-level /examples folder in a LAMMPS source tree python3 run_tests.py --lmp-bin=/path/to/lmp_binary --examples-top-level=/path/to/lammps/examples - 5) Analyze (dry run) the LAMMPS binary annd whole top-level /examples folder in a LAMMPS source tree + 5) Analyze (dry run) the LAMMPS binary and whole top-level /examples folder in a LAMMPS source tree and generate separate input lists for 8 workers: python3 run_tests.py --lmp-bin=/path/to/lmp_binary --examples-top-level=/path/to/lammps/examples \ - --dry-run --num-workers=8 + --analyze --num-workers=8 This is used for splitting the subfolders into separate input lists and launching different instances of run_tests.py simultaneously. + 6) Prepare (dry run) for a quick regression test run that only runs inputs with commands and styles that + have changes in the current branch versus the selected upstream branch. Curb at 40 runs and split and + write out separate lists for 4 workers: + python3 run_tests.py --lmp-bin=/path/to/lmp_binary --examples-top-level=/path/to/lammps/examples \ + --quick --quick-branch=origin/develop --quick-max= 40 --num-workers=4 + An example of the test configuration `config.yaml` is given as below. --- From 6aa6ed86be223c8122b0dc207b42d9e516e57a97 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 1 Sep 2024 08:33:12 -0400 Subject: [PATCH 191/355] Curb number of (randomly) selected tests for quick regression run --- tools/regression-tests/run_tests.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index 21f79b66de..2cc1be0618 100644 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -74,6 +74,7 @@ import datetime import fnmatch import logging import os +import random import re import subprocess import sys @@ -891,6 +892,7 @@ if __name__ == "__main__": analyze = False quick = False quick_branch = "origin/develop" + quick_max = 50 # distribute the total number of input scripts over the workers num_workers = 1 @@ -920,6 +922,8 @@ if __name__ == "__main__": help="Determine which test inputs have commands changed between a branch and the head") parser.add_argument("--quick-branch", dest="quick_branch", default=quick_branch, help="Branch to which compare the current head to for changed styles") + parser.add_argument("--quick-max", dest="quick_max", default=50, + help="Maximum number of inputs to randomly select") parser.add_argument("--skip-numerical-check",dest="skip_numerical_check", action='store_true', default=False, help="Generating reference data") @@ -945,6 +949,7 @@ if __name__ == "__main__": analyze = args.analyze quick = args.quick quick_branch = args.quick_branch + quick_max = args.quick_max skip_numerical_check = args.skip_numerical_check resume = args.resume progress_file = args.progress_file @@ -974,6 +979,11 @@ if __name__ == "__main__": input_list = get_quick_list.get_examples_using_styles(regex, example_toplevel) msg = f"\nThere are {len(input_list)} input scripts with changed styles relative to branch {quick_branch}." msg += "\nChanged styles: " + str(styles) + + if len(input_list) > quick_max: + input_list = random.sample(input_list, quick_max) + msq += "\nTesting " + str(quick_max) + " randomly selected inputs" + print(msg) logger.info(msg) From 14dc3261604954cdd38a424434e689dc60feeaed Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 1 Sep 2024 08:36:50 -0400 Subject: [PATCH 192/355] fix typo --- tools/regression-tests/run_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index 2cc1be0618..07e3aca049 100644 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -982,7 +982,7 @@ if __name__ == "__main__": if len(input_list) > quick_max: input_list = random.sample(input_list, quick_max) - msq += "\nTesting " + str(quick_max) + " randomly selected inputs" + msg += "\nTesting " + str(quick_max) + " randomly selected inputs" print(msg) logger.info(msg) From a9573551a74c11c1a868b56853b55d95f87eb689 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 1 Sep 2024 08:43:16 -0400 Subject: [PATCH 193/355] run 100 quick test inputs at the most --- .github/workflows/quick-regression.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index 7fc684be5d..618a3f87ae 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -77,7 +77,7 @@ jobs: python3 tools/regression-tests/run_tests.py \ --lmp-bin=build/lmp \ --config-file=tools/regression-tests/config_serial.yaml \ - --examples-top-level=examples --quick --quick-branch=origin/develop --num-workers=4 + --examples-top-level=examples --quick --quick-branch=origin/develop --quick-max=100 --num-workers=4 if [ -f input-list-${{ matrix.idx }}.txt ] then \ From 27d5ad1714ed98166c08a22012026c3f64e93836 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 1 Sep 2024 08:49:13 -0400 Subject: [PATCH 194/355] convert string to int --- tools/regression-tests/run_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index 07e3aca049..0b9d5c2a37 100644 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -949,7 +949,7 @@ if __name__ == "__main__": analyze = args.analyze quick = args.quick quick_branch = args.quick_branch - quick_max = args.quick_max + quick_max = int(args.quick_max) skip_numerical_check = args.skip_numerical_check resume = args.resume progress_file = args.progress_file From 7c80b00f2389350686ab699fdadff3161f741f52 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 2 Sep 2024 06:26:27 -0400 Subject: [PATCH 195/355] small corrections in the DIFFRACTION package, mostly cosmetic --- src/DIFFRACTION/compute_saed.cpp | 12 ++++++------ src/DIFFRACTION/compute_xrd.cpp | 12 ++++++------ src/DIFFRACTION/fix_saed_vtk.cpp | 5 +++-- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/DIFFRACTION/compute_saed.cpp b/src/DIFFRACTION/compute_saed.cpp index 1350257910..e2a7bb33d9 100644 --- a/src/DIFFRACTION/compute_saed.cpp +++ b/src/DIFFRACTION/compute_saed.cpp @@ -401,7 +401,7 @@ void ComputeSAED::compute_vector() // Setting up OMP #if defined(_OPENMP) - if (me == 0 && echo) utils::logmesg(lmp," using {}OMP threads\n",comm->nthreads); + if (me == 0 && echo) utils::logmesg(lmp," using {} OMP thread(s)\n",comm->nthreads); #endif if (me == 0 && echo) utils::logmesg(lmp,"\n"); @@ -478,7 +478,7 @@ void ComputeSAED::compute_vector() } } } // End of pragma omp for region - delete [] f; + delete[] f; } auto scratch = new double[2*nRows]; @@ -499,10 +499,10 @@ void ComputeSAED::compute_vector() utils::logmesg(lmp," 100% \nTime elapsed during compute_saed = {:.2f} sec " "using {:.2f} Mbytes/processor\n-----\n", t2-t0, bytes/1024.0/1024.0); - delete [] xlocal; - delete [] typelocal; - delete [] scratch; - delete [] Fvec; + delete[] xlocal; + delete[] typelocal; + delete[] scratch; + delete[] Fvec; } /* ---------------------------------------------------------------------- diff --git a/src/DIFFRACTION/compute_xrd.cpp b/src/DIFFRACTION/compute_xrd.cpp index 11e0bb9a9f..a769be7d4f 100644 --- a/src/DIFFRACTION/compute_xrd.cpp +++ b/src/DIFFRACTION/compute_xrd.cpp @@ -332,7 +332,7 @@ void ComputeXRD::compute_array() // Setting up OMP #if defined(_OPENMP) - if ((me == 0) && echo) utils::logmesg(lmp," using {} OMP threads\n",comm->nthreads); + if ((me == 0) && echo) utils::logmesg(lmp," using {} OMP thread(s)\n",comm->nthreads); #endif if ((me == 0) && echo) { @@ -482,7 +482,7 @@ void ComputeXRD::compute_array() } } // End of pragma omp for region } // End of if LP=1 check - delete [] f; + delete[] f; } // End of pragma omp parallel region auto scratch = new double[2*size_array_rows]; @@ -503,10 +503,10 @@ void ComputeXRD::compute_array() utils::logmesg(lmp," 100% \nTime elapsed during compute_xrd = {:.2f} sec " "using {:.2f} Mbytes/processor\n-----\n", t2-t0, bytes/1024.0/1024.0); - delete [] scratch; - delete [] Fvec; - delete [] xlocal; - delete [] typelocal; + delete[] scratch; + delete[] Fvec; + delete[] xlocal; + delete[] typelocal; } /* ---------------------------------------------------------------------- diff --git a/src/DIFFRACTION/fix_saed_vtk.cpp b/src/DIFFRACTION/fix_saed_vtk.cpp index b3f6693e9e..693bb925b6 100644 --- a/src/DIFFRACTION/fix_saed_vtk.cpp +++ b/src/DIFFRACTION/fix_saed_vtk.cpp @@ -114,6 +114,7 @@ FixSAEDVTK::FixSAEDVTK(LAMMPS *lmp, int narg, char **arg) : memory->create(vector_total,nrows,"saed/vtk:vector_total"); vector_flag = 1; + extvector = 0; size_vector = nrows; if (nOutput == 0) { @@ -248,8 +249,8 @@ FixSAEDVTK::FixSAEDVTK(LAMMPS *lmp, int narg, char **arg) : FixSAEDVTK::~FixSAEDVTK() { - delete [] filename; - delete [] ids; + delete[] filename; + delete[] ids; memory->destroy(vector); memory->destroy(vector_total); if (fp && comm->me == 0) fclose(fp); From 731847b4dccddc6fa22a31839e60155a294626e0 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 2 Sep 2024 21:21:14 -0400 Subject: [PATCH 196/355] report proper style name in error messages --- src/MOLECULE/pair_hbond_dreiding_lj.cpp | 10 +++++----- src/MOLECULE/pair_hbond_dreiding_morse.cpp | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/MOLECULE/pair_hbond_dreiding_lj.cpp b/src/MOLECULE/pair_hbond_dreiding_lj.cpp index 274f8bc2a3..4536cc8e05 100644 --- a/src/MOLECULE/pair_hbond_dreiding_lj.cpp +++ b/src/MOLECULE/pair_hbond_dreiding_lj.cpp @@ -396,14 +396,14 @@ void PairHbondDreidingLJ::init_style() // and computing forces on A,H which may be on different procs if (atom->molecular == Atom::ATOMIC) - error->all(FLERR,"Pair style hbond/dreiding requires molecular system"); + error->all(FLERR,"Pair style hbond/dreiding/lj requires molecular system"); if (atom->tag_enable == 0) - error->all(FLERR,"Pair style hbond/dreiding requires atom IDs"); + error->all(FLERR,"Pair style hbond/dreiding/lj requires atom IDs"); if (atom->map_style == Atom::MAP_NONE) - error->all(FLERR,"Pair style hbond/dreiding requires an atom map, " + error->all(FLERR,"Pair style hbond/dreiding/lj requires an atom map, " "see atom_modify"); if (force->newton_pair == 0) - error->all(FLERR,"Pair style hbond/dreiding requires newton pair on"); + error->all(FLERR,"Pair style hbond/dreiding/lj requires newton pair on"); // set donor[M]/acceptor[M] if any atom of type M is a donor/acceptor @@ -419,7 +419,7 @@ void PairHbondDreidingLJ::init_style() acceptor[j] = 1; } - if (!anyflag) error->all(FLERR,"No pair hbond/dreiding coefficients set"); + if (!anyflag) error->all(FLERR,"No pair hbond/dreiding/lj coefficients set"); // set additional param values // offset is for LJ only, angle term is not included diff --git a/src/MOLECULE/pair_hbond_dreiding_morse.cpp b/src/MOLECULE/pair_hbond_dreiding_morse.cpp index c8bc0a627d..d976b66460 100644 --- a/src/MOLECULE/pair_hbond_dreiding_morse.cpp +++ b/src/MOLECULE/pair_hbond_dreiding_morse.cpp @@ -323,14 +323,14 @@ void PairHbondDreidingMorse::init_style() // and computing forces on A,H which may be on different procs if (atom->molecular == Atom::ATOMIC) - error->all(FLERR,"Pair style hbond/dreiding requires molecular system"); + error->all(FLERR,"Pair style hbond/dreiding/morse requires molecular system"); if (atom->tag_enable == 0) - error->all(FLERR,"Pair style hbond/dreiding requires atom IDs"); + error->all(FLERR,"Pair style hbond/dreiding/morse requires atom IDs"); if (atom->map_style == Atom::MAP_NONE) - error->all(FLERR,"Pair style hbond/dreiding requires an atom map, " + error->all(FLERR,"Pair style hbond/dreiding/morse requires an atom map, " "see atom_modify"); if (force->newton_pair == 0) - error->all(FLERR,"Pair style hbond/dreiding requires newton pair on"); + error->all(FLERR,"Pair style hbond/dreiding/morse requires newton pair on"); // set donor[M]/acceptor[M] if any atom of type M is a donor/acceptor @@ -346,7 +346,7 @@ void PairHbondDreidingMorse::init_style() acceptor[j] = 1; } - if (!anyflag) error->all(FLERR,"No pair hbond/dreiding coefficients set"); + if (!anyflag) error->all(FLERR,"No pair hbond/dreiding/morse coefficients set"); // set additional param values // offset is for Morse only, angle term is not included From 2f7a7d1edba2f1bc010369b5e65f7a8c9514a0f6 Mon Sep 17 00:00:00 2001 From: Chris Knight Date: Tue, 3 Sep 2024 20:07:53 +0000 Subject: [PATCH 197/355] forgot shake header --- src/RIGID/fix_shake.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RIGID/fix_shake.h b/src/RIGID/fix_shake.h index d02fdd784a..2a629f6345 100644 --- a/src/RIGID/fix_shake.h +++ b/src/RIGID/fix_shake.h @@ -33,7 +33,7 @@ class FixShake : public Fix { ~FixShake() override; int setmask() override; void init() override; - void setup(int) override; + virtual void setup(int) override; void setup_pre_reverse(int, int) override; void min_setup(int) override; void pre_neighbor() override; From 42b6c0f62c18fd6582b228a9e97b836a42dbffb6 Mon Sep 17 00:00:00 2001 From: Chris Knight Date: Tue, 3 Sep 2024 20:10:29 +0000 Subject: [PATCH 198/355] enable cmake support --- cmake/Modules/Packages/KOKKOS.cmake | 12 +++++++++++- cmake/presets/kokkos-sycl-intel.cmake | 18 ++++++++++++++++++ ...kos-sycl.cmake => kokkos-sycl-nvidia.cmake} | 0 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 cmake/presets/kokkos-sycl-intel.cmake rename cmake/presets/{kokkos-sycl.cmake => kokkos-sycl-nvidia.cmake} (100%) diff --git a/cmake/Modules/Packages/KOKKOS.cmake b/cmake/Modules/Packages/KOKKOS.cmake index 3776d18a3e..fb12b8790e 100644 --- a/cmake/Modules/Packages/KOKKOS.cmake +++ b/cmake/Modules/Packages/KOKKOS.cmake @@ -127,7 +127,7 @@ if(PKG_KSPACE) ${KOKKOS_PKG_SOURCES_DIR}/grid3d_kokkos.cpp ${KOKKOS_PKG_SOURCES_DIR}/remap_kokkos.cpp) set(FFT_KOKKOS "KISS" CACHE STRING "FFT library for Kokkos-enabled KSPACE package") - set(FFT_KOKKOS_VALUES KISS FFTW3 MKL HIPFFT CUFFT) + set(FFT_KOKKOS_VALUES KISS FFTW3 MKL HIPFFT CUFFT MKL_GPU) set_property(CACHE FFT_KOKKOS PROPERTY STRINGS ${FFT_KOKKOS_VALUES}) validate_option(FFT_KOKKOS FFT_KOKKOS_VALUES) string(TOUPPER ${FFT_KOKKOS} FFT_KOKKOS) @@ -155,6 +155,16 @@ if(PKG_KSPACE) target_compile_definitions(lammps PRIVATE -DFFT_KOKKOS_HIPFFT) target_link_libraries(lammps PRIVATE hip::hipfft) endif() + elseif(FFT_KOKKOS STREQUAL "MKL_GPU") + if(NOT Kokkos_ENABLE_SYCL) + message(FATAL_ERROR "Using MKL_GPU FFT currently requires the SYCL backend of Kokkos.") + endif() + find_package(MKL REQUIRED) + target_compile_definitions(lammps PRIVATE -DFFT_KOKKOS_MKL_GPU) + target_link_libraries(lammps PRIVATE mkl_sycl_dft mkl_intel_ilp64 mkl_tbb_thread mkl_core tbb) + elseif(FFT_KOKKOS STREQUAL "MKL") + find_package(MKL REQUIRED) + target_compile_definitions(lammps PRIVATE -DFFT_KOKKOS_MKL) endif() endif() diff --git a/cmake/presets/kokkos-sycl-intel.cmake b/cmake/presets/kokkos-sycl-intel.cmake new file mode 100644 index 0000000000..3fc75e4b2d --- /dev/null +++ b/cmake/presets/kokkos-sycl-intel.cmake @@ -0,0 +1,18 @@ +# preset that enables KOKKOS and selects SYCL compilation with OpenMP +# enabled as well. Also sets some performance related compiler flags. +set(PKG_KOKKOS ON CACHE BOOL "" FORCE) +set(Kokkos_ENABLE_SERIAL ON CACHE BOOL "" FORCE) +set(Kokkos_ENABLE_OPENMP ON CACHE BOOL "" FORCE) +set(Kokkos_ENABLE_CUDA OFF CACHE BOOL "" FORCE) +set(Kokkos_ENABLE_SYCL ON CACHE BOOL "" FORCE) + +# hide deprecation warnings temporarily for stable release +set(Kokkos_ENABLE_DEPRECATION_WARNINGS OFF CACHE BOOL "" FORCE) + +set(CMAKE_CXX_COMPILER icpx CACHE STRING "" FORCE) +set(MPI_CXX_COMPILER "mpicxx" CACHE STRING "" FORCE) +set(CMAKE_CXX_STANDARD 17 CACHE STRING "" FORCE) +# Silence everything +set(CMAKE_CXX_FLAGS "-w" CACHE STRING "" FORCE) +set(CMAKE_EXE_LINKER_FLAGS "-fsycl -flink-huge-device-code -fsycl-max-parallel-link-jobs=32 -fsycl-targets=spir64_gen -Xsycl-target-backend \"-device 12.60.7\" " CACHE STRING "" FORCE) +set(CMAKE_TUNE_FLAGS "-O3 -fsycl -fsycl-device-code-split=per_kernel -fsycl-targets=spir64_gen" CACHE STRING "" FORCE) diff --git a/cmake/presets/kokkos-sycl.cmake b/cmake/presets/kokkos-sycl-nvidia.cmake similarity index 100% rename from cmake/presets/kokkos-sycl.cmake rename to cmake/presets/kokkos-sycl-nvidia.cmake From 4aefb894bba560eefbdf74f9c1dc62a5f211382a Mon Sep 17 00:00:00 2001 From: cjknight Date: Tue, 3 Sep 2024 15:56:16 -0500 Subject: [PATCH 199/355] remove comments --- src/KOKKOS/fft3d_kokkos.cpp | 1 - src/KOKKOS/fftdata_kokkos.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/KOKKOS/fft3d_kokkos.cpp b/src/KOKKOS/fft3d_kokkos.cpp index 09164731a3..42020e2247 100644 --- a/src/KOKKOS/fft3d_kokkos.cpp +++ b/src/KOKKOS/fft3d_kokkos.cpp @@ -44,7 +44,6 @@ FFT3dKokkos::FFT3dKokkos(LAMMPS *lmp, MPI_Comm comm, int nfast, int int ngpus = lmp->kokkos->ngpus; ExecutionSpace execution_space = ExecutionSpaceFromDevice::space; - // CHRIS:: what about supporting MKL on both CPU and GPU in same build?? #if defined(FFT_KOKKOS_MKL_GPU) if (ngpus > 0 && execution_space == Host) lmp->error->all(FLERR,"Cannot use the MKL library with Kokkos on the host CPUs in a GPU build"); diff --git a/src/KOKKOS/fftdata_kokkos.h b/src/KOKKOS/fftdata_kokkos.h index a967be5338..b2c436bc09 100644 --- a/src/KOKKOS/fftdata_kokkos.h +++ b/src/KOKKOS/fftdata_kokkos.h @@ -37,7 +37,7 @@ #endif // with KOKKOS in CUDA, HIP, or SYCL mode we can only have -// CUFFT/HIPFFT/oneMKL or KISS, thus undefine all other +// CUFFT/HIPFFT/MKL_GPU or KISS, thus undefine all other // FFTs here #ifdef KOKKOS_ENABLE_CUDA From e240619b9415a68ad51ae24c31256c5630bf6c88 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 3 Sep 2024 22:30:32 -0400 Subject: [PATCH 200/355] update pace plugin loader to include the two additional styles added --- examples/PACKAGES/pace/plugin/CMakeLists.txt | 8 +++-- examples/PACKAGES/pace/plugin/paceplugin.cpp | 32 ++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/examples/PACKAGES/pace/plugin/CMakeLists.txt b/examples/PACKAGES/pace/plugin/CMakeLists.txt index 0701a754c4..ede63e3d38 100644 --- a/examples/PACKAGES/pace/plugin/CMakeLists.txt +++ b/examples/PACKAGES/pace/plugin/CMakeLists.txt @@ -5,7 +5,7 @@ cmake_minimum_required(VERSION 3.16) -project(paceplugin VERSION 1.0 LANGUAGES CXX) +project(paceplugin VERSION 1.1 LANGUAGES CXX) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) include(CheckIncludeFileCXX) @@ -15,7 +15,11 @@ include(ML-PACE) ########################## # building the plugins -add_library(paceplugin MODULE paceplugin.cpp ${LAMMPS_SOURCE_DIR}/ML-PACE/pair_pace.cpp) +add_library(paceplugin MODULE paceplugin.cpp + ${LAMMPS_SOURCE_DIR}/ML-PACE/pair_pace.cpp + ${LAMMPS_SOURCE_DIR}/ML-PACE/pair_pace_extrapolation.cpp + ${LAMMPS_SOURCE_DIR}/ML-PACE/compute_pace.cpp) + target_link_libraries(paceplugin PRIVATE pace) target_link_libraries(paceplugin PRIVATE lammps) target_include_directories(paceplugin PRIVATE ${LAMMPS_SOURCE_DIR}/ML-PACE) diff --git a/examples/PACKAGES/pace/plugin/paceplugin.cpp b/examples/PACKAGES/pace/plugin/paceplugin.cpp index adf1c168f9..f231318d23 100644 --- a/examples/PACKAGES/pace/plugin/paceplugin.cpp +++ b/examples/PACKAGES/pace/plugin/paceplugin.cpp @@ -3,6 +3,8 @@ #include "version.h" #include "pair_pace.h" +#include "pair_pace_extrapolation.h" +#include "compute_pace.h" using namespace LAMMPS_NS; @@ -11,6 +13,16 @@ static Pair *pair_pace_creator(LAMMPS *lmp) return new PairPACE(lmp); } +static Pair *pair_pace_extrapolation_creator(LAMMPS *lmp) +{ + return new PairPACEExtrapolation(lmp); +} + +static Compute *compute_pace_creator(LAMMPS *lmp, int argc, char **argv) +{ + return new ComputePACE(lmp, argc, argv); +} + extern "C" void lammpsplugin_init(void *lmp, void *handle, void *regfunc) { lammpsplugin_t plugin; @@ -25,4 +37,24 @@ extern "C" void lammpsplugin_init(void *lmp, void *handle, void *regfunc) plugin.creator.v1 = (lammpsplugin_factory1 *) &pair_pace_creator; plugin.handle = handle; (*register_plugin)(&plugin, lmp); + + // register pace/extrapolation pair style + plugin.version = LAMMPS_VERSION; + plugin.style = "pair"; + plugin.name = "pace/extrapolation"; + plugin.info = "PACE plugin extrapolation pair style v1.0"; + plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; + plugin.creator.v1 = (lammpsplugin_factory1 *) &pair_pace_extrapolation_creator; + plugin.handle = handle; + (*register_plugin)(&plugin, lmp); + + // register pace compute style + plugin.version = LAMMPS_VERSION; + plugin.style = "compute"; + plugin.name = "pace"; + plugin.info = "PACE plugin compute style v1.0"; + plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; + plugin.creator.v2 = (lammpsplugin_factory2 *) &compute_pace_creator; + plugin.handle = handle; + (*register_plugin)(&plugin, lmp); } From 597c53756e1388d5a8b707bf6c5e3af1b087d2ba Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 3 Sep 2024 22:33:39 -0400 Subject: [PATCH 201/355] update plumed library to version 2.9.2 --- cmake/Modules/Packages/PLUMED.cmake | 4 ++-- lib/plumed/Install.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cmake/Modules/Packages/PLUMED.cmake b/cmake/Modules/Packages/PLUMED.cmake index 595b6824c1..8dab157a24 100644 --- a/cmake/Modules/Packages/PLUMED.cmake +++ b/cmake/Modules/Packages/PLUMED.cmake @@ -32,9 +32,9 @@ endif() # Note: must also adjust check for supported API versions in # fix_plumed.cpp when version changes from v2.n.x to v2.n+1.y -set(PLUMED_URL "https://github.com/plumed/plumed2/releases/download/v2.9.1/plumed-src-2.9.1.tgz" +set(PLUMED_URL "https://github.com/plumed/plumed2/releases/download/v2.9.2/plumed-src-2.9.2.tgz" CACHE STRING "URL for PLUMED tarball") -set(PLUMED_MD5 "c3b2d31479c1e9ce211719d40e9efbd7" CACHE STRING "MD5 checksum of PLUMED tarball") +set(PLUMED_MD5 "04862602a372c1013bdfee2d6d03bace" CACHE STRING "MD5 checksum of PLUMED tarball") mark_as_advanced(PLUMED_URL) mark_as_advanced(PLUMED_MD5) diff --git a/lib/plumed/Install.py b/lib/plumed/Install.py index 66501a74e9..485845b67a 100644 --- a/lib/plumed/Install.py +++ b/lib/plumed/Install.py @@ -19,7 +19,7 @@ parser = ArgumentParser(prog='Install.py', # Note: must also adjust check for supported API versions in # fix_plumed.cpp when version changes from v2.n.x to v2.n+1.y -version = "2.9.1" +version = "2.9.2" mode = "static" # help message @@ -51,6 +51,7 @@ checksums = { \ '2.8.4' : '9f59c4f9bda86fe5bef19543c295a981', \ '2.9.0' : '661eabeebee05cf84bbf9dc23d7d5f46', \ '2.9.1' : 'c3b2d31479c1e9ce211719d40e9efbd7', \ + '2.9.2' : '04862602a372c1013bdfee2d6d03bace', \ } # parse and process arguments From ec98481d010ed0a14e89282e1a30ff477910b801 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 4 Sep 2024 04:15:41 -0400 Subject: [PATCH 202/355] move check for libcurl to EXTRA-COMMAND.cmake package CMake module --- cmake/CMakeLists.txt | 10 +--------- cmake/Modules/Packages/EXTRA-COMMAND.cmake | 10 ++++++++++ 2 files changed, 11 insertions(+), 9 deletions(-) create mode 100644 cmake/Modules/Packages/EXTRA-COMMAND.cmake diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 9c5ba9095a..c68a925324 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -515,14 +515,6 @@ if(PKG_ATC OR PKG_AWPMD OR PKG_ML-QUIP OR PKG_ML-POD OR PKG_ELECTRODE OR BUILD_T endif() endif() -find_package(CURL QUIET COMPONENTS HTTP HTTPS) -option(WITH_CURL "Enable libcurl support" ${CURL_FOUND}) -if(WITH_CURL) - find_package(CURL REQUIRED COMPONENTS HTTP HTTPS) - target_compile_definitions(lammps PRIVATE -DLAMMPS_CURL) - target_link_libraries(lammps PRIVATE CURL::libcurl) -endif() - # tweak jpeg library names to avoid linker errors with MinGW cross-compilation set(JPEG_NAMES libjpeg libjpeg-62) find_package(JPEG QUIET) @@ -580,7 +572,7 @@ else() endif() foreach(PKG_WITH_INCL KSPACE PYTHON ML-IAP VORONOI COLVARS ML-HDNNP MDI MOLFILE NETCDF - PLUMED QMMM ML-QUIP SCAFACOS MACHDYN VTK KIM COMPRESS ML-PACE LEPTON RHEO) + PLUMED QMMM ML-QUIP SCAFACOS MACHDYN VTK KIM COMPRESS ML-PACE LEPTON RHEO EXTRA-COMMAND) if(PKG_${PKG_WITH_INCL}) include(Packages/${PKG_WITH_INCL}) endif() diff --git a/cmake/Modules/Packages/EXTRA-COMMAND.cmake b/cmake/Modules/Packages/EXTRA-COMMAND.cmake new file mode 100644 index 0000000000..13c98bafd3 --- /dev/null +++ b/cmake/Modules/Packages/EXTRA-COMMAND.cmake @@ -0,0 +1,10 @@ +# the geturl command needs libcurl + +find_package(CURL QUIET COMPONENTS HTTP HTTPS) +option(WITH_CURL "Enable libcurl support" ${CURL_FOUND}) +if(WITH_CURL) + find_package(CURL REQUIRED COMPONENTS HTTP HTTPS) + target_compile_definitions(lammps PRIVATE -DLAMMPS_CURL) + target_link_libraries(lammps PRIVATE CURL::libcurl) +endif() + From 311cac03489345531eb164071d1493983f73f182 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 4 Sep 2024 05:47:59 -0400 Subject: [PATCH 203/355] Add document describing how to implement a new command style --- doc/src/Developer_write.rst | 1 + doc/src/Developer_write_command.rst | 318 ++++++++++++++++++++++++++++ doc/src/Developer_write_pair.rst | 2 +- 3 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 doc/src/Developer_write_command.rst diff --git a/doc/src/Developer_write.rst b/doc/src/Developer_write.rst index ef4d06a5f6..54b1b6eb81 100644 --- a/doc/src/Developer_write.rst +++ b/doc/src/Developer_write.rst @@ -12,3 +12,4 @@ details are provided for writing code for LAMMPS. Developer_write_pair Developer_write_fix + Developer_write_command diff --git a/doc/src/Developer_write_command.rst b/doc/src/Developer_write_command.rst new file mode 100644 index 0000000000..15142b2dfd --- /dev/null +++ b/doc/src/Developer_write_command.rst @@ -0,0 +1,318 @@ +Writing a new command style +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Command styles allow to do system manipulations or interfaces to the +operating system. + +In the text below, we will discuss the implementation of one example. As +shown on the page for :doc:`writing or extending command styles +`, in order to implement a new command style, a new class +must be written that is either directly or indirectly derived from the +``Command`` class. There is just one method that must be implemented: +``Command::command()``. In addition, a custom constructor is needed to get +access to the members of the ``LAMMPS`` class like the ``Error`` class to +print out error messages. The ``Command::command()`` method processes the +arguments passed to the command in the input and executes it. Any other +methods would be for the convenience of implementation of the new command. + +In general, new command styles should be added to the :ref:`EXTRA-COMMAND +package `. If you feel that your contribution should be +added to a different package, please consult with the :doc:`LAMMPS +developers ` first. The contributed code needs to support +the :doc:`traditional GNU make build process ` **and** the +:doc:`CMake build process `. + +---- + +Case 1: Implementing the geturl command +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In this section, we will describe the procedure of adding a simple command +style to LAMMPS: the :doc:`geturl command ` that allows to download +files directly without having to rely on an external program like "wget" or +"curl". The complete implementation can be found in the files +``src/EXTRA-COMMAND/geturl.cpp`` and ``src/EXTRA-COMMAND/geturl.h`` of the +LAMMPS source code. + +Interfacing the *libcurl* library +""""""""""""""""""""""""""""""""" + +Rather than implementing the various protocols for downloading files, we +rely on an external library: `libcurl library `_. +This requires that the library and its headers are installed. For the +traditional GNU make build system, this simply requires edits to the machine +makefile to add compilation flags like for other libraries. For the CMake +based build system, we need to add some lines to the file +``cmake/Modules/Packages/EXTRA-COMMAND.cmake``: + +.. code-block:: cmake + + find_package(CURL QUIET COMPONENTS HTTP HTTPS) + option(WITH_CURL "Enable libcurl support" ${CURL_FOUND}) + if(WITH_CURL) + find_package(CURL REQUIRED COMPONENTS HTTP HTTPS) + target_compile_definitions(lammps PRIVATE -DLAMMPS_CURL) + target_link_libraries(lammps PRIVATE CURL::libcurl) + endif() + +The first ``find_package()`` command uses a built-in CMake module to find +an existing *libcurl* installation with development headers and support for +using the HTTP and HTTPS protocols. The "QUIET" flag ensures that there is +no screen output and no error if the search fails. The status of the search +is recorded in the "${CURL_FOUND}" variable. That variable sets the default +of the WITH_CURL option, which toggles whether support for *libcurl* is included +or not. + +The second ``find_package()`` uses the "REQUIRED" flag to produce an error +if the WITH_CURL option was set to ``True``, but no suitable *libcurl* +implementation with development support was found. This construct is used +so that the CMake script code inside the ``if(WITH_CURL)`` and ``endif()`` +block can be expanded later to download and compile *libcurl* as part of the +LAMMPS build process, if it is not found locally. The +``target_compile_definitions()`` function added the define ``-DLAMMPS_CURL`` +to the compilation flags when compiling objects for the LAMMPS library. +This allows to always compile the :doc:`geturl command `, but use +preprocessing to compile in the interface to *libcurl* only when it is +present and usable and otherwise stop with an error message about the +unavailability of *libcurl* to execute the functionality of the command. + +Header file +""""""""""" + +The first segment of any LAMMPS source should be the copyright and +license statement. Note the marker in the first line to indicate to +editors like emacs that this file is a C++ source, even though the .h +extension suggests a C source (this is a convention inherited from the +very beginning of the C++ version of LAMMPS). + +.. code-block:: c++ + + /* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. + ------------------------------------------------------------------------- */ + +Every command style must be registered in LAMMPS by including the following +lines of code in the second part of the header after the copyright +message and before the include guards for the class definition: + +.. code-block:: c++ + + #ifdef COMMAND_CLASS + // clang-format off + CommandStyle(geturl,GetURL); + // clang-format on + #else + +This block between ``#ifdef COMMAND_CLASS`` and ``#else`` will be +included by the ``Input`` class in ``input.cpp`` to build a map of +"factory functions" that will create an instance of a Command class +and call its ``command()`` method. The map connects the name of the +command ``geturl`` with the name of the class ``GetURL``. During +compilation, LAMMPS constructs a file ``style_command.h`` that contains +``#include`` statements for all "installed" command styles. Before +including ``style_command.h`` into ``input.cpp``, the ``COMMAND_CLASS`` +define is set and the ``CommandStyle(name,class)`` macro defined. The +code of the macro adds the installed command styles to the "factory map" +which enables the ``Input`` to execute the command. + +The list of header files to include in ``style_command.h`` is automatically +updated by the build system if there are new files, so the presence of the +new header file in the ``src/EXTRA-COMMAND`` folder and the enabling of the +EXTRA-COMMAND package will trigger LAMMPS to include the new command style +when it is (re-)compiled. The "// clang-format" format comments are needed +so that running :ref:`clang-format ` on the file will not +insert unwanted blanks which would break the ``CommandStyle`` macro. + +The third part of the header file is the actual class definition of the +``GetURL`` class. This has the custom constructor and the ``command()`` +method implemented by this command style. For the constructor there is +nothing to do but to pass the ``lmp`` pointer to the base class. Since the +``command()`` method is labeled "virtual" in the base class, it must be +given the "override" property. + +.. code-block:: c++ + + #ifndef LMP_GETURL_H + #define LMP_GETURL_H + + #include "command.h" + + namespace LAMMPS_NS { + + class GetURL : public Command { + public: + GetURL(class LAMMPS *lmp) : Command(lmp) {}; + void command(int, char **) override; + }; + } // namespace LAMMPS_NS + #endif + #endif + +The "override" property helps to detect unexpected mismatches because +compilation will stop with an error in case the signature of a function +is changed in the base class without also changing it in all derived +classes. + +Implementation file +""""""""""""""""""" + +We move on to the implementation of the ``GetURL`` class in the +``geturl.cpp`` file. This file also starts with a LAMMPS copyright and +license header. Below that notice is typically the space where comments may +be added with additional information about this specific file, the +author(s), affiliation(s), and email address(es). This way the contributing +author(s) can be easily contacted, when there are questions about the +implementation later. Since the file(s) may be around for a long time, it +is beneficial to use some kind of "permanent" email address, if possible. + +.. code-block:: c++ + + /* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. + ------------------------------------------------------------------------- */ + + /* ---------------------------------------------------------------------- + Contributing authors: Axel Kohlmeyer (Temple U), + ------------------------------------------------------------------------- */ + + #include "geturl.h" + + #include "comm.h" + #include "error.h" + + #if defined(LAMMPS_CURL) + #include + #endif + + using namespace LAMMPS_NS; + +The second section of the implementation file has various include +statements. The include file for the class header has to come first, +then a couple of LAMMPS classes (sorted alphabetically) followed by a +block of system headers and others, if needed. Note the standardized +C++ notation for headers of C-library functions (``cmath`` instead of +``math.h``). The final statement of this segment imports the +``LAMMPS_NS::`` namespace globally for this file. This way, all LAMMPS +specific functions and classes do not have to be prefixed with +``LAMMPS_NS::``. + +The command() function (required) +""""""""""""""""""""""""""""""""" + +Since the required custom constructor is trivial and implemented in the +header, there is only one function that must be implemented for a command +style and that is the ``command()`` function. + +.. code-block:: c++ + + void GetURL::command(int narg, char **arg) + { + #if !defined(LAMMPS_CURL) + error->all(FLERR, "LAMMPS has not been compiled with libcurl support"); + #else + if (narg < 1) utils::missing_cmd_args(FLERR, "geturl", error); + int verify = 1; + int overwrite = 1; + int verbose = 0; + + // process arguments + + std::string url = arg[0]; + + // sanity check + + if ((url.find(':') == std::string::npos) || (url.find('/') == std::string::npos)) + error->all(FLERR, "URL '{}' is not a supported URL", url); + + std::string output = url.substr(url.find_last_of('/') + 1); + if (output.empty()) error->all(FLERR, "URL '{}' must end in a file string", url); + + int iarg = 1; + while (iarg < narg) { + if (strcmp(arg[iarg], "output") == 0) { + if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "geturl output", error); + output = arg[iarg + 1]; + ++iarg; + } else if (strcmp(arg[iarg], "overwrite") == 0) { + if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "geturl overwrite", error); + overwrite = utils::logical(FLERR, arg[iarg + 1], false, lmp); + ++iarg; + } else if (strcmp(arg[iarg], "verify") == 0) { + if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "geturl verify", error); + verify = utils::logical(FLERR, arg[iarg + 1], false, lmp); + ++iarg; + } else if (strcmp(arg[iarg], "verbose") == 0) { + if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "geturl verbose", error); + verbose = utils::logical(FLERR, arg[iarg + 1], false, lmp); + ++iarg; + } else { + error->all(FLERR, "Unknown geturl keyword: {}", arg[iarg]); + } + ++iarg; + } + +.. code-block:: c++ + + // only download files from rank 0 + + if (comm->me != 0) return; + + if (!overwrite && platform::file_is_readable(output)) return; + + // open output file for writing + + FILE *out = fopen(output.c_str(), "wb"); + if (!out) + error->all(FLERR, "Cannot open output file {} for writing: {}", output, utils::getsyserror()); + +.. code-block:: c++ + + // initialize curl and perform download + + CURL *curl; + curl_global_init(CURL_GLOBAL_DEFAULT); + curl = curl_easy_init(); + if (curl) { + (void) curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + (void) curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) out); + (void) curl_easy_setopt(curl, CURLOPT_FILETIME, 1L); + (void) curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); + if (verbose && screen) { + (void) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + (void) curl_easy_setopt(curl, CURLOPT_STDERR, (void *) screen); + } + if (!verify) { + (void) curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + (void) curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + } + auto res = curl_easy_perform(curl); + if (res != CURLE_OK) { + long response = 0L; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); + error->one(FLERR, "Download of {} failed with: {} {}", output, curl_easy_strerror(res), + response); + } + curl_easy_cleanup(curl); + } + curl_global_cleanup(); + fclose(out); + #endif + } diff --git a/doc/src/Developer_write_pair.rst b/doc/src/Developer_write_pair.rst index 1433effc54..5d5e081042 100644 --- a/doc/src/Developer_write_pair.rst +++ b/doc/src/Developer_write_pair.rst @@ -160,7 +160,7 @@ message and before the include guards for the class definition: #endif -This block of between ``#ifdef PAIR_CLASS`` and ``#else`` will be +This block between ``#ifdef PAIR_CLASS`` and ``#else`` will be included by the ``Force`` class in ``force.cpp`` to build a map of "factory functions" that will create an instance of these classes and return a pointer to it. The map connects the name of the pair style, From 7d93460717109a3837bda12860ddec800e59c4a4 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 4 Sep 2024 08:23:29 -0400 Subject: [PATCH 204/355] add commented example for implementing a command style --- doc/src/Developer_write_command.rst | 46 +++++++++++++++++---- doc/utils/sphinx-config/false_positives.txt | 1 + 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/doc/src/Developer_write_command.rst b/doc/src/Developer_write_command.rst index 15142b2dfd..16ac2092f6 100644 --- a/doc/src/Developer_write_command.rst +++ b/doc/src/Developer_write_command.rst @@ -72,7 +72,7 @@ LAMMPS build process, if it is not found locally. The ``target_compile_definitions()`` function added the define ``-DLAMMPS_CURL`` to the compilation flags when compiling objects for the LAMMPS library. This allows to always compile the :doc:`geturl command `, but use -preprocessing to compile in the interface to *libcurl* only when it is +pre-processing to compile in the interface to *libcurl* only when it is present and usable and otherwise stop with an error message about the unavailability of *libcurl* to execute the functionality of the command. @@ -205,13 +205,13 @@ is beneficial to use some kind of "permanent" email address, if possible. using namespace LAMMPS_NS; The second section of the implementation file has various include -statements. The include file for the class header has to come first, -then a couple of LAMMPS classes (sorted alphabetically) followed by a -block of system headers and others, if needed. Note the standardized -C++ notation for headers of C-library functions (``cmath`` instead of -``math.h``). The final statement of this segment imports the -``LAMMPS_NS::`` namespace globally for this file. This way, all LAMMPS -specific functions and classes do not have to be prefixed with +statements. The include file for the class header has to come first, then a +couple of LAMMPS classes (sorted alphabetically) followed by the header for +the *libcurl* interface. This is wrapped into an ``#ifdef`` block so that +LAMMPS will compile this file without error when the *libcurl* header is not +available and thus the define not set. The final statement of this segment +imports the ``LAMMPS_NS::`` namespace globally for this file. This way, all +LAMMPS specific functions and classes do not have to be prefixed with ``LAMMPS_NS::``. The command() function (required) @@ -233,6 +233,13 @@ style and that is the ``command()`` function. int overwrite = 1; int verbose = 0; +This first part also has the ``#ifdef`` block depending on the LAMMPS_CURL +define. This way the command will simply print an error, if *libcurl* is +not available but will not fail to compile. Furthermore, it sets the +defaults for the following optional arguments. + +.. code-block:: c++ + // process arguments std::string url = arg[0]; @@ -245,6 +252,12 @@ style and that is the ``command()`` function. std::string output = url.substr(url.find_last_of('/') + 1); if (output.empty()) error->all(FLERR, "URL '{}' must end in a file string", url); +This block stores the positional, i.e. non-optional argument of the URL to +be downloaded and adds a couple of sanity checks on the string to make sure it is +a valid URL. Also it derives the default name of the output file from the URL. + +.. code-block:: c++ + int iarg = 1; while (iarg < narg) { if (strcmp(arg[iarg], "output") == 0) { @@ -269,6 +282,9 @@ style and that is the ``command()`` function. ++iarg; } +This block parses the optional arguments following the URL and stops with an +error if there are arguments missing or an unknown argument is encountered. + .. code-block:: c++ // only download files from rank 0 @@ -283,6 +299,10 @@ style and that is the ``command()`` function. if (!out) error->all(FLERR, "Cannot open output file {} for writing: {}", output, utils::getsyserror()); +Here all MPI ranks other than 0 will return, so that the URL download will +only happen from a single MPI rank. For that rank the output file is opened +for writing using the C library function ``fopen()``. + .. code-block:: c++ // initialize curl and perform download @@ -311,8 +331,18 @@ style and that is the ``command()`` function. response); } curl_easy_cleanup(curl); + +This block now implements the actual URL download with the selected options +via the "easy" interface of *libcurl*. For the details of what these +function calls do, please have a look at the `*libcurl documentation +`_. + + .. code-block:: c++ + } curl_global_cleanup(); fclose(out); #endif } + +Finally, the previously opened file is closed and the command is complete. diff --git a/doc/utils/sphinx-config/false_positives.txt b/doc/utils/sphinx-config/false_positives.txt index cfbddbe5f6..4ee25ffc12 100644 --- a/doc/utils/sphinx-config/false_positives.txt +++ b/doc/utils/sphinx-config/false_positives.txt @@ -1539,6 +1539,7 @@ idx ie ielement ieni +ifdef ifdefs iff ifort From 26af8878c932aeaa24310118213a459a732e2e4a Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Wed, 4 Sep 2024 11:13:35 -0600 Subject: [PATCH 205/355] More general fix --- src/KOKKOS/fix_shake_kokkos.cpp | 11 ----------- src/KOKKOS/fix_shake_kokkos.h | 1 - src/KOKKOS/verlet_kokkos.cpp | 5 ++++- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/KOKKOS/fix_shake_kokkos.cpp b/src/KOKKOS/fix_shake_kokkos.cpp index 4602546579..47f932d8f2 100644 --- a/src/KOKKOS/fix_shake_kokkos.cpp +++ b/src/KOKKOS/fix_shake_kokkos.cpp @@ -172,17 +172,6 @@ void FixShakeKokkos::init() k_angle_distance.sync(); } -/* ---------------------------------------------------------------------- - SHAKE as pre-integrator constraint -------------------------------------------------------------------------- */ - -template -void FixShakeKokkos::setup(int vflag) -{ - FixShake::setup(vflag); - atomKK->sync(Host,F_MASK); -} - /* ---------------------------------------------------------------------- run setup for minimization. ------------------------------------------------------------------------- */ diff --git a/src/KOKKOS/fix_shake_kokkos.h b/src/KOKKOS/fix_shake_kokkos.h index 519db18b5f..31a6c340be 100644 --- a/src/KOKKOS/fix_shake_kokkos.h +++ b/src/KOKKOS/fix_shake_kokkos.h @@ -52,7 +52,6 @@ class FixShakeKokkos : public FixShake, public KokkosBase { FixShakeKokkos(class LAMMPS *, int, char **); ~FixShakeKokkos() override; void init() override; - void setup(int) override; void min_setup(int) override; void pre_neighbor() override; void post_force(int) override; diff --git a/src/KOKKOS/verlet_kokkos.cpp b/src/KOKKOS/verlet_kokkos.cpp index 858df5df6c..d839362aa5 100644 --- a/src/KOKKOS/verlet_kokkos.cpp +++ b/src/KOKKOS/verlet_kokkos.cpp @@ -162,8 +162,10 @@ void VerletKokkos::setup(int flag) lmp->kokkos->auto_sync = 0; modify->setup(vflag); - output->setup(flag); lmp->kokkos->auto_sync = 1; + + atomKK->sync(Host,ALL_MASK); + output->setup(flag); update->setupflag = 0; } @@ -252,6 +254,7 @@ void VerletKokkos::setup_minimal(int flag) lmp->kokkos->auto_sync = 0; modify->setup(vflag); lmp->kokkos->auto_sync = 1; + update->setupflag = 0; } From 9e8a1f473400fc6d32b3d31cd54c39cab315ced4 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Wed, 4 Sep 2024 11:15:59 -0600 Subject: [PATCH 206/355] Remove comment --- src/KOKKOS/fft3d_kokkos.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/KOKKOS/fft3d_kokkos.cpp b/src/KOKKOS/fft3d_kokkos.cpp index 42020e2247..defccd337f 100644 --- a/src/KOKKOS/fft3d_kokkos.cpp +++ b/src/KOKKOS/fft3d_kokkos.cpp @@ -629,7 +629,7 @@ struct fft_plan_3d_kokkos* FFT3dKokkos::fft_3d_create_pl // and scaling normalization #if defined(FFT_KOKKOS_MKL_GPU) - sycl::queue queue = LMPDeviceType().sycl_queue(); // is this the correct queue? + sycl::queue queue = LMPDeviceType().sycl_queue(); plan->desc_fast = new descriptor_t (nfast); plan->desc_fast->set_value(oneapi::mkl::dft::config_param::NUMBER_OF_TRANSFORMS, plan->total1/nfast); From 37e6f3ef212b359cb3711ea8e9ac22aa16a22e72 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Fri, 23 Aug 2024 13:45:46 -0600 Subject: [PATCH 207/355] kspace: add NVPL FFT support --- cmake/Modules/Packages/KOKKOS.cmake | 10 +++++----- cmake/Modules/Packages/KSPACE.cmake | 6 +++++- src/KOKKOS/fft3d_kokkos.cpp | 19 +++++++++++-------- src/KOKKOS/fft3d_kokkos.h | 2 +- src/KOKKOS/fftdata_kokkos.h | 16 +++++++++++++++- src/KSPACE/fft3d.cpp | 28 ++++++++++++++-------------- src/KSPACE/fft3d.h | 11 ++++++++++- src/info.cpp | 4 ++++ 8 files changed, 65 insertions(+), 31 deletions(-) diff --git a/cmake/Modules/Packages/KOKKOS.cmake b/cmake/Modules/Packages/KOKKOS.cmake index 3776d18a3e..980b0595e7 100644 --- a/cmake/Modules/Packages/KOKKOS.cmake +++ b/cmake/Modules/Packages/KOKKOS.cmake @@ -127,7 +127,7 @@ if(PKG_KSPACE) ${KOKKOS_PKG_SOURCES_DIR}/grid3d_kokkos.cpp ${KOKKOS_PKG_SOURCES_DIR}/remap_kokkos.cpp) set(FFT_KOKKOS "KISS" CACHE STRING "FFT library for Kokkos-enabled KSPACE package") - set(FFT_KOKKOS_VALUES KISS FFTW3 MKL HIPFFT CUFFT) + set(FFT_KOKKOS_VALUES KISS FFTW3 MKL NVPL HIPFFT CUFFT) set_property(CACHE FFT_KOKKOS PROPERTY STRINGS ${FFT_KOKKOS_VALUES}) validate_option(FFT_KOKKOS FFT_KOKKOS_VALUES) string(TOUPPER ${FFT_KOKKOS} FFT_KOKKOS) @@ -137,10 +137,8 @@ if(PKG_KSPACE) message(FATAL_ERROR "The CUDA backend of Kokkos requires either KISS FFT or CUFFT.") elseif(FFT_KOKKOS STREQUAL "KISS") message(WARNING "Using KISS FFT with the CUDA backend of Kokkos may be sub-optimal.") - target_compile_definitions(lammps PRIVATE -DFFT_KOKKOS_KISS) elseif(FFT_KOKKOS STREQUAL "CUFFT") find_package(CUDAToolkit REQUIRED) - target_compile_definitions(lammps PRIVATE -DFFT_KOKKOS_CUFFT) target_link_libraries(lammps PRIVATE CUDA::cufft) endif() elseif(Kokkos_ENABLE_HIP) @@ -148,14 +146,16 @@ if(PKG_KSPACE) message(FATAL_ERROR "The HIP backend of Kokkos requires either KISS FFT or HIPFFT.") elseif(FFT_KOKKOS STREQUAL "KISS") message(WARNING "Using KISS FFT with the HIP backend of Kokkos may be sub-optimal.") - target_compile_definitions(lammps PRIVATE -DFFT_KOKKOS_KISS) elseif(FFT_KOKKOS STREQUAL "HIPFFT") include(DetectHIPInstallation) find_package(hipfft REQUIRED) - target_compile_definitions(lammps PRIVATE -DFFT_KOKKOS_HIPFFT) target_link_libraries(lammps PRIVATE hip::hipfft) endif() + elseif(FFT_KOKKOS STREQUAL "NVPL") + find_package(nvpl_fft REQUIRED) + target_link_libraries(lammps PRIVATE nvpl::fftw) endif() + target_compile_definitions(lammps PRIVATE -DFFT_KOKKOS_${FFT_KOKKOS}) endif() if(PKG_ML-IAP) diff --git a/cmake/Modules/Packages/KSPACE.cmake b/cmake/Modules/Packages/KSPACE.cmake index 1fdd898144..3801140fe0 100644 --- a/cmake/Modules/Packages/KSPACE.cmake +++ b/cmake/Modules/Packages/KSPACE.cmake @@ -10,7 +10,7 @@ if(${FFTW}_FOUND) else() set(FFT "KISS" CACHE STRING "FFT library for KSPACE package") endif() -set(FFT_VALUES KISS FFTW3 MKL) +set(FFT_VALUES KISS FFTW3 MKL NVPL) set_property(CACHE FFT PROPERTY STRINGS ${FFT_VALUES}) validate_option(FFT FFT_VALUES) string(TOUPPER ${FFT} FFT) @@ -41,6 +41,10 @@ elseif(FFT STREQUAL "MKL") target_compile_definitions(lammps PRIVATE -DFFT_MKL_THREADS) endif() target_link_libraries(lammps PRIVATE MKL::MKL) +elseif(FFT STREQUAL "NVPL") + find_package(nvpl_fft REQUIRED) + target_compile_definitions(lammps PRIVATE -DFFT_NVPL) + target_link_libraries(lammps PRIVATE nvpl::fftw) else() # last option is KISSFFT target_compile_definitions(lammps PRIVATE -DFFT_KISS) diff --git a/src/KOKKOS/fft3d_kokkos.cpp b/src/KOKKOS/fft3d_kokkos.cpp index 9d5347f173..687c7dc4e8 100644 --- a/src/KOKKOS/fft3d_kokkos.cpp +++ b/src/KOKKOS/fft3d_kokkos.cpp @@ -50,6 +50,9 @@ FFT3dKokkos::FFT3dKokkos(LAMMPS *lmp, MPI_Comm comm, int nfast, int #elif defined(FFT_KOKKOS_FFTW3) if (ngpus > 0 && execution_space == Device) lmp->error->all(FLERR,"Cannot use the FFTW library with Kokkos on GPUs"); +#elif defined(FFT_KOKKOS_NVPL) + if (ngpus > 0 && execution_space == Device) + lmp->error->all(FLERR,"Cannot use the NVPL FFT library with Kokkos on GPUs"); #elif defined(FFT_KOKKOS_CUFFT) if (ngpus > 0 && execution_space == Host) lmp->error->all(FLERR,"Cannot use the cuFFT library with Kokkos on the host CPUs"); @@ -151,7 +154,7 @@ public: KOKKOS_INLINE_FUNCTION void operator() (const int &i) const { -#if defined(FFT_KOKKOS_FFTW3) || defined(FFT_KOKKOS_CUFFT) || defined(FFT_KOKKOS_HIPFFT) +#if defined(FFT_KOKKOS_FFTW3) || defined(FFT_KOKKOS_CUFFT) || defined(FFT_KOKKOS_HIPFFT) || defined(FFT_KOKKOS_NVPL) FFT_SCALAR* out_ptr = (FFT_SCALAR *)(d_out.data()+i); *(out_ptr++) *= norm; *(out_ptr++) *= norm; @@ -226,7 +229,7 @@ void FFT3dKokkos::fft_3d_kokkos(typename FFT_AT::t_FFT_DATA_1d d_in, DftiComputeForward(plan->handle_fast,d_data.data()); else DftiComputeBackward(plan->handle_fast,d_data.data()); - #elif defined(FFT_KOKKOS_FFTW3) + #elif defined(FFT_KOKKOS_FFTW3) || defined(FFT_KOKKOS_NVPL) if (flag == 1) FFTW_API(execute_dft)(plan->plan_fast_forward,(FFT_KOKKOS_DATA*)d_data.data(),(FFT_KOKKOS_DATA*)d_data.data()); else @@ -272,7 +275,7 @@ void FFT3dKokkos::fft_3d_kokkos(typename FFT_AT::t_FFT_DATA_1d d_in, DftiComputeForward(plan->handle_mid,d_data.data()); else DftiComputeBackward(plan->handle_mid,d_data.data()); - #elif defined(FFT_KOKKOS_FFTW3) + #elif defined(FFT_KOKKOS_FFTW3) || defined(FFT_KOKKOS_NVPL) if (flag == 1) FFTW_API(execute_dft)(plan->plan_mid_forward,(FFT_KOKKOS_DATA*)d_data.data(),(FFT_KOKKOS_DATA*)d_data.data()); else @@ -316,7 +319,7 @@ void FFT3dKokkos::fft_3d_kokkos(typename FFT_AT::t_FFT_DATA_1d d_in, DftiComputeForward(plan->handle_slow,d_data.data()); else DftiComputeBackward(plan->handle_slow,d_data.data()); - #elif defined(FFT_KOKKOS_FFTW3) + #elif defined(FFT_KOKKOS_FFTW3) || defined(FFT_KOKKOS_NVPL) if (flag == 1) FFTW_API(execute_dft)(plan->plan_slow_forward,(FFT_KOKKOS_DATA*)d_data.data(),(FFT_KOKKOS_DATA*)d_data.data()); else @@ -647,7 +650,7 @@ struct fft_plan_3d_kokkos* FFT3dKokkos::fft_3d_create_pl #endif DftiCommitDescriptor(plan->handle_slow); -#elif defined(FFT_KOKKOS_FFTW3) +#elif defined(FFT_KOKKOS_FFTW3) || defined(FFT_KOKKOS_NVPL) #if defined (FFT_KOKKOS_FFTW_THREADS) if (nthreads > 1) { @@ -786,7 +789,7 @@ void FFT3dKokkos::fft_3d_destroy_plan_kokkos(struct fft_plan_3d_kokk DftiFreeDescriptor(&(plan->handle_fast)); DftiFreeDescriptor(&(plan->handle_mid)); DftiFreeDescriptor(&(plan->handle_slow)); -#elif defined(FFT_KOKKOS_FFTW3) +#elif defined(FFT_KOKKOS_FFTW3) || defined(FFT_KOKKOS_NVPL) FFTW_API(destroy_plan)(plan->plan_slow_forward); FFTW_API(destroy_plan)(plan->plan_slow_backward); FFTW_API(destroy_plan)(plan->plan_mid_forward); @@ -857,7 +860,7 @@ void FFT3dKokkos::fft_3d_1d_only_kokkos(typename FFT_AT::t_FFT_DATA_ // fftw3 and Dfti in MKL encode the number of transforms // into the plan, so we cannot operate on a smaller data set -#if defined(FFT_KOKKOS_MKL) || defined(FFT_KOKKOS_FFTW3) +#if defined(FFT_KOKKOS_MKL) || defined(FFT_KOKKOS_FFTW3) || defined(FFT_KOKKOS_NVPL) if ((total1 > nsize) || (total2 > nsize) || (total3 > nsize)) return; #endif @@ -878,7 +881,7 @@ void FFT3dKokkos::fft_3d_1d_only_kokkos(typename FFT_AT::t_FFT_DATA_ DftiComputeBackward(plan->handle_mid,d_data.data()); DftiComputeBackward(plan->handle_slow,d_data.data()); } -#elif defined(FFT_KOKKOS_FFTW3) +#elif defined(FFT_KOKKOS_FFTW3) || defined(FFT_KOKKOS_NVPL) if (flag == -1) { FFTW_API(execute_dft)(plan->plan_fast_forward,(FFT_KOKKOS_DATA*)d_data.data(),(FFT_KOKKOS_DATA*)d_data.data()); FFTW_API(execute_dft)(plan->plan_mid_forward,(FFT_KOKKOS_DATA*)d_data.data(),(FFT_KOKKOS_DATA*)d_data.data()); diff --git a/src/KOKKOS/fft3d_kokkos.h b/src/KOKKOS/fft3d_kokkos.h index 48b0fd76de..dd771ef6c8 100644 --- a/src/KOKKOS/fft3d_kokkos.h +++ b/src/KOKKOS/fft3d_kokkos.h @@ -49,7 +49,7 @@ struct fft_plan_3d_kokkos { DFTI_DESCRIPTOR *handle_fast; DFTI_DESCRIPTOR *handle_mid; DFTI_DESCRIPTOR *handle_slow; -#elif defined(FFT_KOKKOS_FFTW3) +#elif defined(FFT_KOKKOS_FFTW3) || defined(FFT_KOKKOS_NVPL) FFTW_API(plan) plan_fast_forward; FFTW_API(plan) plan_fast_backward; FFTW_API(plan) plan_mid_forward; diff --git a/src/KOKKOS/fftdata_kokkos.h b/src/KOKKOS/fftdata_kokkos.h index 0cb59f49cb..88016aa7cf 100644 --- a/src/KOKKOS/fftdata_kokkos.h +++ b/src/KOKKOS/fftdata_kokkos.h @@ -60,6 +60,9 @@ # if defined(FFT_KOKKOS_FFTW3) # undef FFT_KOKKOS_FFTW3 # endif +# if defined(FFT_KOKKOS_NVPL) +# undef FFT_KOKKOS_NVPL +# endif # if defined(FFT_KOKKOS_MKL) # undef FFT_KOKKOS_MKL # endif @@ -85,6 +88,8 @@ #define LMP_FFT_KOKKOS_LIB "FFTW3" #elif defined(FFT_KOKKOS_MKL) #define LMP_FFT_KOKKOS_LIB "MKL FFT" +#elif defined(FFT_KOKKOS_NVPL) +#define LMP_FFT_KOKKOS_LIB "NVPL FFT" #else #define LMP_FFT_KOKKOS_LIB "KISS FFT" #endif @@ -108,6 +113,15 @@ typedef fftw_complex FFT_KOKKOS_DATA; #define FFTW_API(function) fftw_ ## function #endif +#elif defined(FFT_KOKKOS_NVPL) + #include "nvpl_fftw.h" + #if defined(FFT_SINGLE) + typedef fftwf_complex FFT_KOKKOS_DATA; + #define FFTW_API(function) fftwf_ ## function + #else + typedef fftw_complex FFT_KOKKOS_DATA; + #define FFTW_API(function) fftw_ ## function + #endif #elif defined(FFT_KOKKOS_CUFFT) #include "cufft.h" #if defined(FFT_SINGLE) @@ -146,7 +160,7 @@ #endif // (double[2]*) is not a 1D pointer -#if defined(FFT_KOKKOS_FFTW3) +#if defined(FFT_KOKKOS_FFTW3) || defined(FFT_KOKKOS_NVPL) typedef FFT_SCALAR* FFT_KOKKOS_DATA_POINTER; #else typedef FFT_KOKKOS_DATA* FFT_KOKKOS_DATA_POINTER; diff --git a/src/KSPACE/fft3d.cpp b/src/KSPACE/fft3d.cpp index a9956f4397..9ee798b50d 100644 --- a/src/KSPACE/fft3d.cpp +++ b/src/KSPACE/fft3d.cpp @@ -72,14 +72,14 @@ void fft_3d(FFT_DATA *in, FFT_DATA *out, int flag, struct fft_plan_3d *plan) { FFT_SCALAR norm; -#if defined(FFT_FFTW3) +#if defined(FFT_FFTW3) || defined(FFT_NVPL) FFT_SCALAR *out_ptr; #endif FFT_DATA *data,*copy; // system specific constants -#if defined(FFT_FFTW3) +#if defined(FFT_FFTW3) || defined(FFT_NVPL) FFTW_API(plan) theplan; #else // nothing to do for other FFTs @@ -105,7 +105,7 @@ void fft_3d(FFT_DATA *in, FFT_DATA *out, int flag, struct fft_plan_3d *plan) DftiComputeForward(plan->handle_fast,data); else DftiComputeBackward(plan->handle_fast,data); -#elif defined(FFT_FFTW3) +#elif defined(FFT_FFTW3) || defined(FFT_NVPL) if (flag == 1) theplan=plan->plan_fast_forward; else @@ -139,7 +139,7 @@ void fft_3d(FFT_DATA *in, FFT_DATA *out, int flag, struct fft_plan_3d *plan) DftiComputeForward(plan->handle_mid,data); else DftiComputeBackward(plan->handle_mid,data); -#elif defined(FFT_FFTW3) +#elif defined(FFT_FFTW3) || defined(FFT_NVPL) if (flag == 1) theplan=plan->plan_mid_forward; else @@ -173,7 +173,7 @@ void fft_3d(FFT_DATA *in, FFT_DATA *out, int flag, struct fft_plan_3d *plan) DftiComputeForward(plan->handle_slow,data); else DftiComputeBackward(plan->handle_slow,data); -#elif defined(FFT_FFTW3) +#elif defined(FFT_FFTW3) || defined(FFT_NVPL) if (flag == 1) theplan=plan->plan_slow_forward; else @@ -203,11 +203,11 @@ void fft_3d(FFT_DATA *in, FFT_DATA *out, int flag, struct fft_plan_3d *plan) if (flag == -1 && plan->scaled) { norm = plan->norm; const int num = plan->normnum; -#if defined(FFT_FFTW3) +#if defined(FFT_FFTW3) || defined(FFT_NVPL) out_ptr = (FFT_SCALAR *)out; #endif for (int i = 0; i < num; i++) { -#if defined(FFT_FFTW3) +#if defined(FFT_FFTW3) || defined(FFT_NVPL) *(out_ptr++) *= norm; *(out_ptr++) *= norm; #elif defined(FFT_MKL) @@ -515,7 +515,7 @@ struct fft_plan_3d *fft_3d_create_plan( #endif DftiCommitDescriptor(plan->handle_slow); -#elif defined(FFT_FFTW3) +#elif defined(FFT_FFTW3) || defined(FFT_NVPL) #if defined(FFT_FFTW_THREADS) if (nthreads > 1) { FFTW_API(init_threads)(); @@ -613,7 +613,7 @@ void fft_3d_destroy_plan(struct fft_plan_3d *plan) DftiFreeDescriptor(&(plan->handle_fast)); DftiFreeDescriptor(&(plan->handle_mid)); DftiFreeDescriptor(&(plan->handle_slow)); -#elif defined(FFT_FFTW3) +#elif defined(FFT_FFTW3) || defined(FFT_NVPL) FFTW_API(destroy_plan)(plan->plan_slow_forward); FFTW_API(destroy_plan)(plan->plan_slow_backward); FFTW_API(destroy_plan)(plan->plan_mid_forward); @@ -714,7 +714,7 @@ void fft_1d_only(FFT_DATA *data, int nsize, int flag, struct fft_plan_3d *plan) { int i,num; FFT_SCALAR norm; -#if defined(FFT_FFTW3) +#if defined(FFT_FFTW3) || defined(FFT_NVPL) FFT_SCALAR *data_ptr; #endif @@ -733,7 +733,7 @@ void fft_1d_only(FFT_DATA *data, int nsize, int flag, struct fft_plan_3d *plan) // fftw3 and Dfti in MKL encode the number of transforms // into the plan, so we cannot operate on a smaller data set -#if defined(FFT_MKL) || defined(FFT_FFTW3) +#if defined(FFT_MKL) || defined(FFT_FFTW3) || defined(FFT_NVPL) if ((total1 > nsize) || (total2 > nsize) || (total3 > nsize)) return; #endif @@ -754,7 +754,7 @@ void fft_1d_only(FFT_DATA *data, int nsize, int flag, struct fft_plan_3d *plan) DftiComputeBackward(plan->handle_mid,data); DftiComputeBackward(plan->handle_slow,data); } -#elif defined(FFT_FFTW3) +#elif defined(FFT_FFTW3) || defined(FFT_NVPL) FFTW_API(plan) theplan; if (flag == 1) theplan=plan->plan_fast_forward; @@ -795,11 +795,11 @@ void fft_1d_only(FFT_DATA *data, int nsize, int flag, struct fft_plan_3d *plan) if (flag == -1 && plan->scaled) { norm = plan->norm; num = MIN(plan->normnum,nsize); -#if defined(FFT_FFTW3) +#if defined(FFT_FFTW3) || defined(FFT_NVPL) data_ptr = (FFT_SCALAR *)data; #endif for (i = 0; i < num; i++) { -#if defined(FFT_FFTW3) +#if defined(FFT_FFTW3) || defined(FFT_NVPL) *(data_ptr++) *= norm; *(data_ptr++) *= norm; #elif defined(FFT_MKL) diff --git a/src/KSPACE/fft3d.h b/src/KSPACE/fft3d.h index f4ddeebc4d..34b9c3d4fc 100644 --- a/src/KSPACE/fft3d.h +++ b/src/KSPACE/fft3d.h @@ -31,6 +31,10 @@ typedef MKL_Complex8 FFT_DATA; typedef fftwf_complex FFT_DATA; #define FFTW_API(function) fftwf_##function +#elif defined(FFT_NVPL) +#include "nvpl_fftw.h" +typedef fftwf_complex FFT_DATA; +#define FFTW_API(function) fftwf_##function #else /* use a stripped down version of kiss fft as default fft */ @@ -64,6 +68,11 @@ typedef MKL_Complex16 FFT_DATA; typedef fftw_complex FFT_DATA; #define FFTW_API(function) fftw_##function +#elif defined(FFT_NVPL) +#include "nvpl_fftw.h" +typedef fftw_complex FFT_DATA; +#define FFTW_API(function) fftw_##function + #else /* use a stripped down version of kiss fft as default fft */ @@ -108,7 +117,7 @@ struct fft_plan_3d { DFTI_DESCRIPTOR *handle_fast; DFTI_DESCRIPTOR *handle_mid; DFTI_DESCRIPTOR *handle_slow; -#elif defined(FFT_FFTW3) +#elif defined(FFT_FFTW3) || defined(FFT_NVPL) FFTW_API(plan) plan_fast_forward; FFTW_API(plan) plan_fast_backward; FFTW_API(plan) plan_mid_forward; diff --git a/src/info.cpp b/src/info.cpp index 98ed06f498..9122280550 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -1316,6 +1316,8 @@ std::string Info::get_fft_info() #else fft_info += "FFT library = MKL\n"; #endif +#elif defined(FFT_NVPL) + fft_info += "FFT library = NVPL\n"; #elif defined(FFT_FFTW3) #if defined(FFT_FFTW_THREADS) fft_info += "FFT library = FFTW3 with threads\n"; @@ -1338,6 +1340,8 @@ std::string Info::get_fft_info() #else fft_info += "KOKKOS FFT library = FFTW3\n"; #endif +#elif defined(FFT_KOKKOS_NVPL) + fft_info += "KOKKOS FFT library = NVPL\n"; #elif defined(FFT_KOKKOS_MKL) #if defined(FFT_KOKKOS_MKL_THREADS) fft_info += "KOKKOS FFT library = MKL with threads\n"; From 18514f404f826a573bba0baa3e5d2f3b637d38ef Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Wed, 4 Sep 2024 11:59:01 -0600 Subject: [PATCH 208/355] docs: mentiond build options for NVPL FFT --- doc/src/Build_settings.rst | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/doc/src/Build_settings.rst b/doc/src/Build_settings.rst index 9d31f6b431..e5ebfa0027 100644 --- a/doc/src/Build_settings.rst +++ b/doc/src/Build_settings.rst @@ -67,9 +67,9 @@ libraries and better pipelining for packing and communication. .. code-block:: bash - -D FFT=value # FFTW3 or MKL or KISS, default is FFTW3 if found, - # else KISS - -D FFT_KOKKOS=value # FFTW3 or MKL or KISS or CUFFT or HIPFFT, + -D FFT=value # FFTW3 or MKL or NVPL or KISS, + # default is FFTW3 if found, else KISS + -D FFT_KOKKOS=value # FFTW3 or MKL or NVPL or KISS or CUFFT or HIPFFT, # default is KISS -D FFT_SINGLE=value # yes or no (default), no = double precision -D FFT_PACK=value # array (default) or pointer or memcpy @@ -103,6 +103,8 @@ libraries and better pipelining for packing and communication. -D FFT_HEFFTE_BACKEND=value # FFTW or MKL or empty/undefined for the stock # heFFTe back end -D Heffte_ROOT=path # path to an existing heFFTe installation + -D nvpl_fft_INCLUDE_DIR=path # path to NVPL FFT include files + -D nvpl_fft_LIBRARY_DIR=path # path to NVPL FFT libraries .. note:: @@ -121,9 +123,9 @@ libraries and better pipelining for packing and communication. .. code-block:: make FFT_INC = -DFFT_ # where is KISS (default), FFTW3, - # FFTW (same as FFTW3), or MKL + # FFTW (same as FFTW3), NVPL, or MKL FFT_INC = -DFFT_KOKKOS_ # where is KISS (default), FFTW3, - # FFTW (same as FFTW3), MKL, CUFFT, or HIPFFT + # FFTW (same as FFTW3), MKL, NVPL, CUFFT, or HIPFFT FFT_INC = -DFFT_SINGLE # do not specify for double precision FFT_INC = -DFFT_FFTW_THREADS # enable using threaded FFTW3 libraries FFT_INC = -DFFT_MKL_THREADS # enable using threaded FFTs with MKL libraries @@ -165,6 +167,10 @@ libraries and better pipelining for packing and communication. # MKL with automatic runtime selection of interface libs FFT_LIB = -lmkl_rt + # threaded NVPL FFT + FFT_LIB = -lnvpl_fftw + + As with CMake, you do not need to set paths in ``FFT_INC`` or ``FFT_PATH``, if the compiler can find the FFT header and library files in its default search path. You must specify ``FFT_LIB`` @@ -218,6 +224,10 @@ The Intel MKL math library is part of the Intel compiler suite. It can be used with the Intel or GNU compiler (see the ``FFT_LIB`` setting above). +The NVIDIA Performance Libraries (NVPL) FFT library is optimized for NVIDIA +Grace Armv9.0 architecture. You can download it from +`https://docs.nvidia.com/nvpl/`_. + The cuFFT and hipFFT FFT libraries are packaged with NVIDIA's CUDA and AMD's HIP installations, respectively. These FFT libraries require the Kokkos acceleration package to be enabled and the Kokkos back end to be From 4ed5dfe88d891f1c8034550c55576484a45356ee Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Wed, 4 Sep 2024 15:32:20 -0500 Subject: [PATCH 209/355] reported the total wall time of each run in the progress.yaml file --- tools/regression-tests/run_tests.py | 80 ++++++++++++++++++++--------- 1 file changed, 56 insertions(+), 24 deletions(-) diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index 0b9d5c2a37..ce03f63188 100644 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 ''' -UPDATE: August 28, 2024: +UPDATE: September 4, 2024: Launching the LAMMPS binary under testing using a configuration defined in a yaml file (e.g. config.yaml). Comparing the output thermo with that in the existing log file (with the same nprocs) + data in the log files are extracted and converted into yaml data structure @@ -20,6 +20,20 @@ With the current features, users can: + distribute the input list across multiple processes via multiprocessing, or split the list of input scripts into separate runs (there are 800+ input script under the top-level examples) + +Input arguments: + + the path to a LAMMPS binary (can be relative to the working directory) + + a test configuration file (see tools/regression-tests/config.yaml for an example) + + a text file that lists of folders where the input scripts reside and how many of them line by line, or + a text file that list of input scripts, or + the path to the top-level examples + +Output: + + progress.yaml: testing results of individual input scripts that were tested + with the status (completed or failed) with error messages (for failed runs), and walltime + + output.xml: testing results in the JUnit XML format + + run.log: screen output and error of individual runs + Limitations: - input scripts use thermo style multi (e.g., examples/peptide) do not work with the expected thermo output format - input scripts that require partition runs (e.g. examples/neb) need a separate config file, e.g. args: "--partition 3x1" @@ -34,17 +48,17 @@ The following Python packages need to be installed into an activated environment source testing-env/bin/activate pip install numpy pyyaml junit_xml -Example usage: +Example usage (aka, tests for this script): 1) Simple use (using the provided tools/regression-tests/config.yaml and the examples/ folder at the top level) - python3 run_tests.py --lmp-bin=/path/to/lmp_binary + python3 run_tests.py --lmp-bin=build/lmp --config-file=tools/regression-tests/config.yaml 2) Use a custom testing configuration python3 run_tests.py --lmp-bin=/path/to/lmp_binary --config-file=/path/to/config/file/config.yaml 3) Specify a list of example folders python3 run_tests.py --lmp-bin=/path/to/lmp_binary --config-file=/path/to/config/file/config.yaml \ - --example-folders="/path/to/examples/folder1;/path/to/examples/folder2" + --example-folders="/path/to/examples/melt;/path/to/examples/rigid" The example subfolders can also be loaded from a text file list_subfolders1.txt: python3 run_tests.py --lmp-bin=/path/to/lmp_binary --config-file=/path/to/config/file/config.yaml \ @@ -57,6 +71,7 @@ Example usage: 5) Test a LAMMPS binary with the whole top-level /examples folder in a LAMMPS source tree python3 run_tests.py --lmp-bin=/path/to/lmp_binary --examples-top-level=/path/to/lammps/examples + --config-file=tools/regression-tests/config_serial.yaml 6) Analyze the LAMMPS binary and whole top-level /examples folder in a LAMMPS source tree and generate separate input lists for 8 workers: @@ -153,13 +168,18 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file else: progress = open(progress_file, "w") + # walltime = -2: skipped tests + # -1: failed tests + # >= 0: walltime in seconds (e.g. in.melt walltime = 0.2 seconds) + walltime = -2 + # skip the input file if listed in the config file or matched with a pattern if 'skip' in config: if input in config['skip']: msg = " + " + input + f" ({test_id+1}/{num_tests}): skipped as specified in {configFileName}" print(msg) logger.info(msg) - progress.write(f"{input}: {{ folder: {input_folder}, status: \"skipped\" }}\n") + progress.write(f"{input}: {{ folder: {input_folder}, status: \"skipped\", walltime: {walltime} }}\n") progress.close() num_skipped = num_skipped + 1 test_id = test_id + 1 @@ -177,7 +197,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file msg = " + " + input + f" ({test_id+1}/{num_tests}): skipped as specified in {configFileName}" print(msg) logger.info(msg) - progress.write(f"{input}: {{ folder: {input_folder}, status: \"skipped\" }}\n") + progress.write(f"{input}: {{ folder: {input_folder}, status: \"skipped\", walltime: {walltime} }}\n") progress.close() num_skipped = num_skipped + 1 test_id = test_id + 1 @@ -288,6 +308,9 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file config['mpiexec_numproc_flag'] = "" nprocs = 1 + # default walltime value of failed tests + walltime = -1 + result = TestResult(name=input, output="", time="", status="passed") # run the LAMMPS binary with the input script @@ -307,11 +330,11 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file logger.info(f" The run terminated with {input_test} gives the following output:") logger.info(f" {error_line}") if "Unrecognized" in output: - result.status = f"error, unrecognized command, package not installed, {error_line}" + result.status = f"failed, unrecognized command, package not installed, {error_line}" elif "Unknown" in output: - result.status = f"error, unknown command, package not installed, {error_line}" + result.status = f"failed, unknown command, package not installed, {error_line}" else: - result.status = f"error, {error_line}." + result.status = f"failed, {error_line}." logger.info(f" Output:") logger.info(f" {output}") @@ -319,7 +342,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file num_error = num_error + 1 results.append(result) - progress.write(f"{input}: {{ folder: {input_folder}, status: \"{result.status}\" }}\n") + progress.write(f"{input}: {{ folder: {input_folder}, status: \"{result.status}\", walltime: {walltime} }}\n") progress.close() test_id = test_id + 1 @@ -331,7 +354,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file logger.info(f" Output:") logger.info(f" {output}") logger.info(f" Error:\n{error}") - progress.write(f"{input}: {{ folder: {input_folder}, status: \"error, no log.lammps\" }}\n") + progress.write(f"{input}: {{ folder: {input_folder}, status: \"failed, no log.lammps\", walltime: {walltime} }}\n") progress.close() num_error = num_error + 1 test_id = test_id + 1 @@ -352,7 +375,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file num_memleak = num_memleak + 1 result.status = msg results.append(result) - progress.write(f"{input}: {{ folder: {input_folder}, status: \"{msg}\" }}\n") + progress.write(f"{input}: {{ folder: {input_folder}, status: \"{msg}\", walltime: {walltime} }}\n") progress.close() # count the number of completed runs @@ -366,19 +389,28 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file logger.info(f"\n{input_test}:") logger.info(f"\n Output:\n{output}") logger.info(f"\n Error:\n{error}") - progress.write(f"{input}: {{ folder: {input_folder}, status: \"error, no Total wall time in the output.\" }}\n") + progress.write(f"{input}: {{ folder: {input_folder}, status: \"failed, no Total wall time in the output.\", walltime: {walltime} }}\n") progress.close() num_error = num_error + 1 test_id = test_id + 1 continue + for line in output.split('\n'): + if "Total wall time" in line: + walltime_str = line.split('time:')[1] + hours = int(walltime_str.split(':')[0]) + minutes = int(walltime_str.split(':')[1]) + seconds = int(walltime_str.split(':')[2]) + walltime = int(hours * 3600.0 + minutes * 60.0 + seconds) + break + # if there is no Step or no Loop printed out if "Step" not in output or "Loop" not in output: logger.info(f" ERROR: no Step nor Loop in the output.\n") logger.info(f"\n{input_test}:") logger.info(f"\n Output:\n{output}") logger.info(f"\n Error:\n{error}") - progress.write(f"{input}: {{ folder: {input_folder}, status: \"error, no Step nor Loop in the output.\" }}\n") + progress.write(f"{input}: {{ folder: {input_folder}, status: \"failed, no Step nor Loop in the output.\", walltime: {walltime} }}\n") progress.close() num_error = num_error + 1 test_id = test_id + 1 @@ -403,7 +435,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file result.status = msg + ", error parsing log.lammps into YAML" results.append(result) - progress.write(f"{input}: {{ folder: {input_folder}, status: \"{result.status}\" }}\n") + progress.write(f"{input}: {{ folder: {input_folder}, status: \"{result.status}\", walltime: {walltime} }}\n") progress.close() num_completed = num_completed + 1 @@ -422,7 +454,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file logger.info(f" ERROR: Error parsing the reference log file {thermo_ref_file}.") result.status = "skipped numerical checks due to parsing the reference log file" results.append(result) - progress.write(f"{input}: {{ folder: {input_folder}, status: \"completed, numerical checks skipped, unsupported log file format\" }}\n") + progress.write(f"{input}: {{ folder: {input_folder}, status: \"completed, numerical checks skipped, unsupported log file format\", walltime: {walltime} }}\n") progress.close() num_error = num_error + 1 test_id = test_id + 1 @@ -442,7 +474,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file logger.info(f" {thermo_ref_file} also does not exist in the working directory.") result.status = "skipped due to missing the reference log file" results.append(result) - progress.write(f"{input}: {{ folder: {input_folder}, status: \"completed, numerical checks skipped, missing the reference log file\" }}\n") + progress.write(f"{input}: {{ folder: {input_folder}, status: \"completed, numerical checks skipped, missing the reference log file\", walltime: {walltime} }}\n") progress.close() num_error = num_error + 1 test_id = test_id + 1 @@ -455,9 +487,9 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file if num_runs != num_runs_ref: logger.info(f" ERROR: Number of runs in log.lammps ({num_runs}) is different from that in the reference log ({num_runs_ref})." " Check README in the folder, possibly due to using mpirun with partitions or parsing the wrong reference log file.") - result.status = "error, incomplete runs" + result.status = "failed, incomplete runs" results.append(result) - progress.write(f"{input}: {{ folder: {input_folder}, status: \"{result.status}\" }}\n") + progress.write(f"{input}: {{ folder: {input_folder}, status: \"{result.status}\", walltime: {walltime} }}\n") progress.close() num_error = num_error + 1 test_id = test_id + 1 @@ -471,9 +503,9 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file if num_fields != num_fields_ref: logger.info(f" ERROR: Number of thermo colums in log.lammps ({num_fields}) is different from that in the reference log ({num_fields_ref}) in the first run.") logger.info(f" Check both log files for more details.") - result.status = "error, mismatched columns in the log files" + result.status = "failed, mismatched columns in the log files" results.append(result) - progress.write(f"{input}: {{ folder: {input_folder}, status: \"{result.status}\" }}\n") + progress.write(f"{input}: {{ folder: {input_folder}, status: \"{result.status}\", walltime: {walltime} }}\n") progress.close() num_error = num_error + 1 test_id = test_id + 1 @@ -603,7 +635,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file msg += ", memory leaks detected" num_memleak = num_memleak + 1 - progress.write(f"{input}: {{ folder: {input_folder}, status: \"{msg}\" }}\n") + progress.write(f"{input}: {{ folder: {input_folder}, status: \"{msg}\", walltime: {walltime} }}\n") progress.close() # count the number of completed runs @@ -1263,9 +1295,9 @@ if __name__ == "__main__": if passed_tests <= completed_tests: msg += f" - numerical tests passed: {passed_tests}\n" msg += "\nOutput:\n" + msg += f" - Status of the tested inputs : {progress_file}\n" msg += f" - Running log with screen output: {log_file}\n" - msg += f" - Progress with the input list : {progress_file}\n" - msg += f" - Regression test results : {output_file}\n" + msg += f" - Testing result in JUnit XML : {output_file}\n" print(msg) From 29cd430da6b8da20ae9b10685866f17bbcb9931c Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 4 Sep 2024 23:09:10 -0400 Subject: [PATCH 210/355] no longer need the warnings module since the deprecated functions were removed --- python/lammps/numpy_wrapper.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python/lammps/numpy_wrapper.py b/python/lammps/numpy_wrapper.py index a980a972fd..add4268b52 100644 --- a/python/lammps/numpy_wrapper.py +++ b/python/lammps/numpy_wrapper.py @@ -16,7 +16,6 @@ # Written by Richard Berger ################################################################################ -import warnings from ctypes import POINTER, c_void_p, c_char_p, c_double, c_int, c_int32, c_int64, cast From e3ced6d26fab730e340da5bb92bf0448e123109f Mon Sep 17 00:00:00 2001 From: cjknight Date: Wed, 4 Sep 2024 23:44:44 -0500 Subject: [PATCH 211/355] docs --- doc/src/Build_extras.rst | 38 ++++++++++++++++++++++++++++++++------ doc/src/Build_settings.rst | 12 +++++++++--- doc/src/Howto_cmake.rst | 2 +- 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/doc/src/Build_extras.rst b/doc/src/Build_extras.rst index 1dd64b3160..a7c22493a3 100644 --- a/doc/src/Build_extras.rst +++ b/doc/src/Build_extras.rst @@ -751,14 +751,27 @@ This list was last updated for version 4.3.0 of the Kokkos library. platform-appropriate vendor library: rocFFT on AMD GPUs or cuFFT on NVIDIA GPUs. - To simplify compilation, five preset files are included in the + For Intel GPUs using SYCL, set these variables: + + .. code-block:: bash + + -D Kokkos_ARCH_HOSTARCH=yes # HOSTARCH = HOST from list above + -D Kokkos_ARCH_GPUARCH=yes # GPUARCH = GPU from list above + -D Kokkos_ENABLE_SYCL=yes + -D Kokkos_ENABLE_OPENMP=yes + -D FFT_KOKKOS=MKL_GPU + + This will enable FFTs on the GPU using the oneMKL library. + + To simplify compilation, six preset files are included in the ``cmake/presets`` folder, ``kokkos-serial.cmake``, ``kokkos-openmp.cmake``, ``kokkos-cuda.cmake``, - ``kokkos-hip.cmake``, and ``kokkos-sycl.cmake``. They will enable - the KOKKOS package and enable some hardware choices. For GPU - support those preset files must be customized to match the - hardware used. So to compile with CUDA device parallelization with - some common packages enabled, you can do the following: + ``kokkos-hip.cmake``, ``kokkos-sycl-nvidia.cmake``, and + ``kokkos-sycl-intel.cmake``. They will enable the KOKKOS + package and enable some hardware choices. For GPU support those + preset files must be customized to match the hardware used. So + to compile with CUDA device parallelization with some common + packages enabled, you can do the following: .. code-block:: bash @@ -830,6 +843,19 @@ This list was last updated for version 4.3.0 of the Kokkos library. FFT_INC = -DFFT_HIPFFT # enable use of hipFFT (optional) FFT_LIB = -lhipfft # link to hipFFT library + For Intel GPUs using SYCL: + + .. code-block:: make + + KOKKOS_DEVICES = SYCL + KOKKOS_ARCH = HOSTARCH,GPUARCH # HOSTARCH = HOST from list above that is + # hosting the GPU + # GPUARCH = GPU from list above + FFT_INC = -DFFT_KOKKOS_MKL_GPU # enable use of hipFFT (optional) + # link to hipFFT library + FFT_LIB = -lmkl_sycl_dft -lmkl_intel_ilp64 -lmkl_tbb_thread + -mkl_core -ltbb + Advanced KOKKOS compilation settings ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/doc/src/Build_settings.rst b/doc/src/Build_settings.rst index 9d31f6b431..a1a900a385 100644 --- a/doc/src/Build_settings.rst +++ b/doc/src/Build_settings.rst @@ -69,7 +69,7 @@ libraries and better pipelining for packing and communication. -D FFT=value # FFTW3 or MKL or KISS, default is FFTW3 if found, # else KISS - -D FFT_KOKKOS=value # FFTW3 or MKL or KISS or CUFFT or HIPFFT, + -D FFT_KOKKOS=value # FFTW3 or MKL or KISS or CUFFT or HIPFFT or MKL_GPU, # default is KISS -D FFT_SINGLE=value # yes or no (default), no = double precision -D FFT_PACK=value # array (default) or pointer or memcpy @@ -123,7 +123,8 @@ libraries and better pipelining for packing and communication. FFT_INC = -DFFT_ # where is KISS (default), FFTW3, # FFTW (same as FFTW3), or MKL FFT_INC = -DFFT_KOKKOS_ # where is KISS (default), FFTW3, - # FFTW (same as FFTW3), MKL, CUFFT, or HIPFFT + # FFTW (same as FFTW3), MKL, CUFFT, HIPFFT + # or MKL_GPU FFT_INC = -DFFT_SINGLE # do not specify for double precision FFT_INC = -DFFT_FFTW_THREADS # enable using threaded FFTW3 libraries FFT_INC = -DFFT_MKL_THREADS # enable using threaded FFTs with MKL libraries @@ -141,6 +142,9 @@ libraries and better pipelining for packing and communication. # cuFFT either precision FFT_LIB = -lcufft + # MKL_GPU either precision + FFT_LIB = -lmkl_sycl_dft -lmkl_intel_ilp64 -lmkl_tbb_thread -lmkl_core -ltbb + # FFTW3 double precision FFT_LIB = -lfftw3 @@ -221,7 +225,9 @@ above). The cuFFT and hipFFT FFT libraries are packaged with NVIDIA's CUDA and AMD's HIP installations, respectively. These FFT libraries require the Kokkos acceleration package to be enabled and the Kokkos back end to be -GPU-resident (i.e., HIP or CUDA). +GPU-resident (i.e., HIP or CUDA). Similarly, GPU offload of FFTs on +Intel GPUs with oneMKL currently requires the Kokkos acceleration +package to be enabled with the SYCL backend. Performing 3d FFTs in parallel can be time-consuming due to data access and required communication. This cost can be reduced by performing diff --git a/doc/src/Howto_cmake.rst b/doc/src/Howto_cmake.rst index 55e5b171a6..43aa519293 100644 --- a/doc/src/Howto_cmake.rst +++ b/doc/src/Howto_cmake.rst @@ -348,7 +348,7 @@ Some common LAMMPS specific variables * - ``FFT`` - select which FFT library to use: ``FFTW3``, ``MKL``, ``KISS`` (default, unless FFTW3 is found) * - ``FFT_KOKKOS`` - - select which FFT library to use in Kokkos-enabled styles: ``FFTW3``, ``MKL``, ``HIPFFT``, ``CUFFT``, ``KISS`` (default) + - select which FFT library to use in Kokkos-enabled styles: ``FFTW3``, ``MKL``, ``HIPFFT``, ``CUFFT``, ``MKL_GPU``, ``KISS`` (default) * - ``FFT_SINGLE`` - select whether to use single precision FFTs (default: ``off``) * - ``WITH_JPEG`` From 04ff7b47369d63616929fe1c269d49935c4d0269 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 5 Sep 2024 04:18:23 -0400 Subject: [PATCH 212/355] add missing import --- python/lammps/core.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/lammps/core.py b/python/lammps/core.py index 6eba38b1e6..249b4719e0 100644 --- a/python/lammps/core.py +++ b/python/lammps/core.py @@ -19,7 +19,8 @@ from __future__ import print_function import os import sys from ctypes import CDLL, POINTER, RTLD_GLOBAL, CFUNCTYPE, py_object, byref, cast, sizeof, \ - create_string_buffer, c_int, c_int32, c_int64, c_double, c_void_p, c_char_p, pythonapi + create_string_buffer, c_int, c_int32, c_int64, c_double, c_void_p, c_char_p, pythonapi, \ + pointer from os.path import dirname, abspath, join from inspect import getsourcefile From dfd0772affd377f3d6eef33449edf93eeab46415 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Thu, 5 Sep 2024 10:38:32 -0500 Subject: [PATCH 213/355] list the failed tests (including crashed, with error, or numerical checks) into a separate file --- tools/regression-tests/run_tests.py | 90 +++++++++++++++++++++-------- 1 file changed, 67 insertions(+), 23 deletions(-) diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index ce03f63188..791e5739fe 100644 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -29,10 +29,11 @@ Input arguments: the path to the top-level examples Output: - + progress.yaml: testing results of individual input scripts that were tested - with the status (completed or failed) with error messages (for failed runs), and walltime - + output.xml: testing results in the JUnit XML format - + run.log: screen output and error of individual runs + + failure.yaml : list of the failed runs and reasons + + progress.yaml: full testing results of the tested input scripts with the status (completed, failed or skipped) + with error messages (for failed runs), and walltime (in seconds) + + output.xml : testing results in the JUnit XML format + + run.log : screen output and error of individual runs Limitations: - input scripts use thermo style multi (e.g., examples/peptide) do not work with the expected thermo output format @@ -139,9 +140,10 @@ class TestResult: results : a list of TestResult objects stat : a dictionary that lists the number of passed, skipped, failed tests progress_file: yaml file that stores the tested input script and status + failure_file : file that reports the failed runs (a subset of progress_file) last_progress: the dictionary that shows the status of the last tests ''' -def iterate(lmp_binary, input_folder, input_list, config, results, progress_file, last_progress=None, output_buf=None): +def iterate(lmp_binary, input_folder, input_list, config, results, progress_file, failure_file, last_progress=None, output_buf=None): num_tests = len(input_list) num_completed = 0 @@ -159,6 +161,9 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file if 'valgrind' in config['mpiexec']: use_valgrind = True + # record all the failed runs + failure = open(failure_file, "a") + # iterate over the input scripts for input in input_list: @@ -342,8 +347,10 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file num_error = num_error + 1 results.append(result) - progress.write(f"{input}: {{ folder: {input_folder}, status: \"{result.status}\", walltime: {walltime} }}\n") + msg = f"{input}: {{ folder: {input_folder}, status: \"{result.status}\", walltime: {walltime} }}\n" + progress.write(msg) progress.close() + failure.write(msg) test_id = test_id + 1 continue @@ -354,8 +361,12 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file logger.info(f" Output:") logger.info(f" {output}") logger.info(f" Error:\n{error}") - progress.write(f"{input}: {{ folder: {input_folder}, status: \"failed, no log.lammps\", walltime: {walltime} }}\n") + + msg = f"{input}: {{ folder: {input_folder}, status: \"failed, no log.lammps\", walltime: {walltime} }}\n" + progress.write(msg) progress.close() + failure.write(msg) + num_error = num_error + 1 test_id = test_id + 1 continue @@ -375,8 +386,11 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file num_memleak = num_memleak + 1 result.status = msg results.append(result) - progress.write(f"{input}: {{ folder: {input_folder}, status: \"{msg}\", walltime: {walltime} }}\n") + + msg = f"{input}: {{ folder: {input_folder}, status: \"{msg}\", walltime: {walltime} }}\n" + progress.write(msg) progress.close() + failure.write(msg) # count the number of completed runs num_completed = num_completed + 1 @@ -389,29 +403,39 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file logger.info(f"\n{input_test}:") logger.info(f"\n Output:\n{output}") logger.info(f"\n Error:\n{error}") - progress.write(f"{input}: {{ folder: {input_folder}, status: \"failed, no Total wall time in the output.\", walltime: {walltime} }}\n") + + msg = f"{input}: {{ folder: {input_folder}, status: \"failed, no Total wall time in the output.\", walltime: {walltime} }}\n" + progress.write(msg) progress.close() + failure.write(msg) + num_error = num_error + 1 test_id = test_id + 1 continue + # NOTE: Total wall time could be 00:00:00 whereas Loop time is non-zero seonds for line in output.split('\n'): if "Total wall time" in line: walltime_str = line.split('time:')[1] - hours = int(walltime_str.split(':')[0]) - minutes = int(walltime_str.split(':')[1]) - seconds = int(walltime_str.split(':')[2]) - walltime = int(hours * 3600.0 + minutes * 60.0 + seconds) + hms = walltime_str.split(':') + hours = float(hms[0]) + minutes = float(hms[1]) + seconds = float(hms[2]) + walltime = hours * 3600.0 + minutes * 60.0 + seconds break # if there is no Step or no Loop printed out if "Step" not in output or "Loop" not in output: - logger.info(f" ERROR: no Step nor Loop in the output.\n") + logger.info(f" completed, but no Step nor Loop in the output.\n") logger.info(f"\n{input_test}:") logger.info(f"\n Output:\n{output}") logger.info(f"\n Error:\n{error}") - progress.write(f"{input}: {{ folder: {input_folder}, status: \"failed, no Step nor Loop in the output.\", walltime: {walltime} }}\n") + + msg = f"{input}: {{ folder: {input_folder}, status: \"completed, but no Step nor Loop in the output.\", walltime: {walltime} }}\n" + progress.write(msg) progress.close() + failure.write(msg) + num_error = num_error + 1 test_id = test_id + 1 continue @@ -442,7 +466,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file test_id = test_id + 1 continue - # At this point, the run completed without trivial errors, proceed with numerical checks + # At this point, the run completed without trivial errors, proceed with numerical checks for thermo output # check if there is a reference log file for this input if ref_logfile_exist: # parse the thermo output in reference log file @@ -474,8 +498,11 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file logger.info(f" {thermo_ref_file} also does not exist in the working directory.") result.status = "skipped due to missing the reference log file" results.append(result) - progress.write(f"{input}: {{ folder: {input_folder}, status: \"completed, numerical checks skipped, missing the reference log file\", walltime: {walltime} }}\n") + + msg = f"{input}: {{ folder: {input_folder}, status: \"completed, numerical checks skipped due to missing the reference log file\", walltime: {walltime} }}\n" + progress.write(msg) progress.close() + failure.write(msg) num_error = num_error + 1 test_id = test_id + 1 continue @@ -595,17 +622,18 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file "{abs_diff_check.rjust(20)} {rel_diff_check.rjust(20)}") # after all runs completed, or are interrupted in one of the runs (mismatched_columns = True) + if mismatched_columns == True: msg = f" mismatched log files after the first run. Check both log files for more details." print(msg) logger.info(msg) - result.status = "failed" + result.status = "thermo checks failed" if num_abs_failed > 0: msg = f" {num_abs_failed} abs diff thermo checks failed." print(msg) logger.info(msg) - result.status = "failed" + result.status = f"{num_abs_failed} abs thermo checks failed" if verbose == True: for i in failed_abs_output: print(f"- {i}") @@ -613,7 +641,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file msg = f" {num_rel_failed} rel diff thermo checks failed." print(msg) logger.info(msg) - result.status = "failed" + result.status = f"{num_rel_failed} rel thermo checks failed" if verbose == True: for i in failed_rel_output: print(f"- {i}") @@ -621,13 +649,13 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file msg = f" all {num_checks} thermo checks passed." print(msg) logger.info(msg) - result.status = "passed" + result.status = "thermo checks passed" num_passed = num_passed + 1 results.append(result) # check if memleak detects from valgrind run (need to replace "mpirun" -> valgrind --leak-check=yes mpirun") - msg = "completed" + msg = "completed, " + result.status if use_valgrind == True: if "All heap blocks were freed" in error: msg += ", no memory leak" @@ -638,10 +666,17 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file progress.write(f"{input}: {{ folder: {input_folder}, status: \"{msg}\", walltime: {walltime} }}\n") progress.close() + # write to failure if there is any numerical failed check + if num_abs_failed > 0 or num_rel_failed > 0: + failure.write(f"{input}: {{ folder: {input_folder}, status: \"{msg}\", walltime: {walltime} }}\n") + # count the number of completed runs num_completed = num_completed + 1 test_id = test_id + 1 + # close the failure file + failure.close() + stat = { 'num_completed': num_completed, 'num_passed': num_passed, 'num_skipped': num_skipped, @@ -918,6 +953,7 @@ if __name__ == "__main__": verbose = False output_file = "output.xml" progress_file = "progress.yaml" + failure_file = "failure.yaml" log_file = "run.log" list_input = "" list_subfolders = "" @@ -947,6 +983,7 @@ if __name__ == "__main__": parser.add_argument("--output-file",dest="output", default=output_file, help="Output file") parser.add_argument("--log-file",dest="logfile", default=log_file, help="Log file") parser.add_argument("--progress-file",dest="progress_file", default=progress_file, help="Progress file") + parser.add_argument("--failure-file",dest="failure_file", default=failure_file, help="Failure file") analyze = parser.add_mutually_exclusive_group() analyze.add_argument("--analyze",dest="analyze", action='store_true', default=False, help="Analyze the testing folders and report statistics, not running the tests") @@ -985,6 +1022,7 @@ if __name__ == "__main__": skip_numerical_check = args.skip_numerical_check resume = args.resume progress_file = args.progress_file + failure_file = args.failure_file # logging logger = logging.getLogger(__name__) @@ -1203,6 +1241,11 @@ if __name__ == "__main__": except Exception: print(f" Cannot open progress file {progress_file_abs} to resume, rerun all the tests") + # record all the failure cases (overwrite if the file exists) + failure_file_abs = pwd + "/" + failure_file + failure = open(failure_file_abs, "w") + failure.close() + # initialize all the counters total_tests = 0 completed_tests = 0 @@ -1255,7 +1298,7 @@ if __name__ == "__main__": # iterate through the input scripts results = [] - stat = iterate(lmp_binary, directory, input_list, config, results, progress_file_abs, last_progress) + stat = iterate(lmp_binary, directory, input_list, config, results, progress_file_abs, failure_file_abs, last_progress) completed_tests += stat['num_completed'] skipped_tests += stat['num_skipped'] @@ -1295,6 +1338,7 @@ if __name__ == "__main__": if passed_tests <= completed_tests: msg += f" - numerical tests passed: {passed_tests}\n" msg += "\nOutput:\n" + msg += f" - Failed inputs and reasons : {failure_file}\n" msg += f" - Status of the tested inputs : {progress_file}\n" msg += f" - Running log with screen output: {log_file}\n" msg += f" - Testing result in JUnit XML : {output_file}\n" From 688cff71e65eef7759f74bded2b60ff360061e11 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Thu, 5 Sep 2024 11:22:00 -0500 Subject: [PATCH 214/355] print out more info for failed tests if verbose is True, indent output --- tools/regression-tests/run_tests.py | 57 +++++++++++++++++------------ 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index 791e5739fe..eb2cf02816 100644 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -143,7 +143,7 @@ class TestResult: failure_file : file that reports the failed runs (a subset of progress_file) last_progress: the dictionary that shows the status of the last tests ''' -def iterate(lmp_binary, input_folder, input_list, config, results, progress_file, failure_file, last_progress=None, output_buf=None): +def iterate(lmp_binary, input_folder, input_list, config, results, progress_file, failure_file, verbose=False, last_progress=None, output_buf=None): num_tests = len(input_list) num_completed = 0 @@ -347,6 +347,8 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file num_error = num_error + 1 results.append(result) + print(f"{result.status}") + msg = f"{input}: {{ folder: {input_folder}, status: \"{result.status}\", walltime: {walltime} }}\n" progress.write(msg) progress.close() @@ -357,7 +359,9 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file # check if a log.lammps file exists in the current folder if os.path.isfile("log.lammps") == False: - logger.info(f" ERROR: No log.lammps generated with {input_test} with return code {returncode}.\n") + msg = f" failed, no log.lammps generated with {input_test} with return code {returncode}.\n" + print(msg) + logger.info(msg) logger.info(f" Output:") logger.info(f" {output}") logger.info(f" Error:\n{error}") @@ -377,7 +381,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file # if skip numerical checks, then skip the rest if skip_numerical_check == True: - msg = "completed, skipping numerical checks" + msg = "completed, skipping numerical checks" if use_valgrind == True: if "All heap blocks were freed" in error: msg += ", no memory leak" @@ -399,7 +403,9 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file # if there is no ERROR in the output, but there is no Total wall time printed out if "Total wall time" not in output: - logger.info(f" ERROR: no Total wall time in the output.\n") + msg = f" failed, no Total wall time in the output.\n" + print(msg) + logger.info(msg) logger.info(f"\n{input_test}:") logger.info(f"\n Output:\n{output}") logger.info(f"\n Error:\n{error}") @@ -413,7 +419,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file test_id = test_id + 1 continue - # NOTE: Total wall time could be 00:00:00 whereas Loop time is non-zero seonds + # NOTE: Total wall time could be 00:00:00 whereas Loop time is non-zero seconds for line in output.split('\n'): if "Total wall time" in line: walltime_str = line.split('time:')[1] @@ -426,7 +432,9 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file # if there is no Step or no Loop printed out if "Step" not in output or "Loop" not in output: - logger.info(f" completed, but no Step nor Loop in the output.\n") + msg = f" completed, but no Step nor Loop in the output.\n" + print(msg) + logger.info(msg) logger.info(f"\n{input_test}:") logger.info(f"\n Output:\n{output}") logger.info(f"\n Error:\n{error}") @@ -462,6 +470,9 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file progress.write(f"{input}: {{ folder: {input_folder}, status: \"{result.status}\", walltime: {walltime} }}\n") progress.close() + if verbose == True: + print(result.status) + num_completed = num_completed + 1 test_id = test_id + 1 continue @@ -474,8 +485,8 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file if thermo_ref: num_runs_ref = len(thermo_ref) else: - # dictionary is empty - logger.info(f" ERROR: Error parsing the reference log file {thermo_ref_file}.") + # thhe thermo_ref dictionary is empty + logger.info(f" failed, error parsing the reference log file {thermo_ref_file}.") result.status = "skipped numerical checks due to parsing the reference log file" results.append(result) progress.write(f"{input}: {{ folder: {input_folder}, status: \"completed, numerical checks skipped, unsupported log file format\", walltime: {walltime} }}\n") @@ -484,7 +495,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file test_id = test_id + 1 continue else: - msg = f" Cannot find the reference log file for {input_test} with the expected format log.[date].{basename}.*.[nprocs]" + msg = f" failed, cannot find the reference log file for {input_test} with the expected format log.[date].{basename}.*.[nprocs]" logger.info(msg) print(msg) # attempt to read in the thermo yaml output from the working directory (the following section will be deprecated) @@ -528,7 +539,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file num_fields = len(thermo[0]['keywords']) num_fields_ref = len(thermo_ref[0]['keywords']) if num_fields != num_fields_ref: - logger.info(f" ERROR: Number of thermo colums in log.lammps ({num_fields}) is different from that in the reference log ({num_fields_ref}) in the first run.") + logger.info(f" failed, number of thermo colums in log.lammps ({num_fields}) is different from that in the reference log ({num_fields_ref}) in the first run.") logger.info(f" Check both log files for more details.") result.status = "failed, mismatched columns in the log files" results.append(result) @@ -541,7 +552,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file # comparing output vs reference values width = 20 if verbose == True: - print("Quantities".ljust(width) + "Output".center(width) + "Reference".center(width) + + print(" Quantities".ljust(width) + "Output".center(width) + "Reference".center(width) + "Abs Diff Check".center(width) + "Rel Diff Check".center(width)) # check if overrides for this input scipt is specified @@ -563,7 +574,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file num_fields = len(thermo[irun]['keywords']) num_fields_ref = len(thermo_ref[irun]['keywords']) if num_fields != num_fields_ref: - logger.info(f" ERROR: Number of thermo columns in log.lammps ({num_fields})") + logger.info(f" failed: Number of thermo columns in log.lammps ({num_fields})") logger.info(f" is different from that in the reference log ({num_fields_ref}) in run {irun}.") mismatched_columns = True continue @@ -618,35 +629,34 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file rel_diff_check = "N/A" if verbose == True and abs_diff_check != "N/A" and rel_diff_check != "N/A": - print(f"{thermo[irun]['keywords'][i].ljust(width)} {str(val).rjust(20)} {str(ref).rjust(20)} " - "{abs_diff_check.rjust(20)} {rel_diff_check.rjust(20)}") + print(f" {thermo[irun]['keywords'][i].ljust(width)} {str(val).rjust(20)} {str(ref).rjust(20)} {abs_diff_check.rjust(20)} {rel_diff_check.rjust(20)}") # after all runs completed, or are interrupted in one of the runs (mismatched_columns = True) if mismatched_columns == True: - msg = f" mismatched log files after the first run. Check both log files for more details." + msg = f" mismatched log files after the first run. Check both log files for more details." print(msg) logger.info(msg) result.status = "thermo checks failed" if num_abs_failed > 0: - msg = f" {num_abs_failed} abs diff thermo checks failed." + msg = f" {num_abs_failed} abs diff thermo checks failed." print(msg) logger.info(msg) result.status = f"{num_abs_failed} abs thermo checks failed" if verbose == True: - for i in failed_abs_output: - print(f"- {i}") + for out in failed_abs_output: + print(f" - {out}") if num_rel_failed > 0: - msg = f" {num_rel_failed} rel diff thermo checks failed." + msg = f" {num_rel_failed} rel diff thermo checks failed." print(msg) logger.info(msg) result.status = f"{num_rel_failed} rel thermo checks failed" if verbose == True: - for i in failed_rel_output: - print(f"- {i}") + for out in failed_rel_output: + print(f" - {out}") if num_abs_failed == 0 and num_rel_failed == 0: - msg = f" all {num_checks} thermo checks passed." + msg = f" all {num_checks} thermo checks passed." print(msg) logger.info(msg) result.status = "thermo checks passed" @@ -1298,7 +1308,8 @@ if __name__ == "__main__": # iterate through the input scripts results = [] - stat = iterate(lmp_binary, directory, input_list, config, results, progress_file_abs, failure_file_abs, last_progress) + stat = iterate(lmp_binary, directory, input_list, config, + results, progress_file_abs, failure_file_abs, verbose, last_progress) completed_tests += stat['num_completed'] skipped_tests += stat['num_skipped'] From 5881a60f50991b3eba5105a96917a5892a85e641 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Thu, 5 Sep 2024 14:42:51 -0600 Subject: [PATCH 215/355] Revert change to fix shake that is no longer needed --- src/RIGID/fix_shake.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RIGID/fix_shake.h b/src/RIGID/fix_shake.h index 2a629f6345..d02fdd784a 100644 --- a/src/RIGID/fix_shake.h +++ b/src/RIGID/fix_shake.h @@ -33,7 +33,7 @@ class FixShake : public Fix { ~FixShake() override; int setmask() override; void init() override; - virtual void setup(int) override; + void setup(int) override; void setup_pre_reverse(int, int) override; void min_setup(int) override; void pre_neighbor() override; From 3357889d57da53651819baf4190c43d62fa7195e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 5 Sep 2024 17:21:42 -0400 Subject: [PATCH 216/355] install MPI --- .github/workflows/full-regression.yml | 3 ++- .github/workflows/quick-regression.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index d13e8eb385..106bda9d2e 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -31,7 +31,8 @@ jobs: - name: Install extra packages run: | sudo apt-get install -y ccache ninja-build libeigen3-dev \ - libgsl-dev libcurl4-openssl-dev python3-dev + libgsl-dev libcurl4-openssl-dev python3-dev \ + mpi-default-bin mpi-default-dev - name: Create Build Environment run: mkdir build diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index 618a3f87ae..0d432044b0 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -31,7 +31,8 @@ jobs: - name: Install extra packages run: | sudo apt-get install -y ccache ninja-build libeigen3-dev \ - libgsl-dev libcurl4-openssl-dev python3-dev + libgsl-dev libcurl4-openssl-dev python3-dev \ + mpi-default-bin mpi-default-dev - name: Create Build Environment run: mkdir build From 03631aab00a2d43ef9d07d29dda4f32082788194 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 5 Sep 2024 21:46:32 -0400 Subject: [PATCH 217/355] whitespace and spelling fixes --- doc/src/Build_extras.rst | 9 ++++----- doc/src/Build_settings.rst | 8 ++++---- doc/utils/sphinx-config/false_positives.txt | 1 + src/KOKKOS/fft3d_kokkos.cpp | 14 +++++++------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/doc/src/Build_extras.rst b/doc/src/Build_extras.rst index a7c22493a3..e6df3fb025 100644 --- a/doc/src/Build_extras.rst +++ b/doc/src/Build_extras.rst @@ -759,7 +759,7 @@ This list was last updated for version 4.3.0 of the Kokkos library. -D Kokkos_ARCH_GPUARCH=yes # GPUARCH = GPU from list above -D Kokkos_ENABLE_SYCL=yes -D Kokkos_ENABLE_OPENMP=yes - -D FFT_KOKKOS=MKL_GPU + -D FFT_KOKKOS=MKL_GPU This will enable FFTs on the GPU using the oneMKL library. @@ -851,10 +851,9 @@ This list was last updated for version 4.3.0 of the Kokkos library. KOKKOS_ARCH = HOSTARCH,GPUARCH # HOSTARCH = HOST from list above that is # hosting the GPU # GPUARCH = GPU from list above - FFT_INC = -DFFT_KOKKOS_MKL_GPU # enable use of hipFFT (optional) - # link to hipFFT library - FFT_LIB = -lmkl_sycl_dft -lmkl_intel_ilp64 -lmkl_tbb_thread - -mkl_core -ltbb + FFT_INC = -DFFT_KOKKOS_MKL_GPU # enable use of oneMKL for Intel GPUs (optional) + # link to oneMKL FFT library + FFT_LIB = -lmkl_sycl_dft -lmkl_intel_ilp64 -lmkl_tbb_thread -mkl_core -ltbb Advanced KOKKOS compilation settings ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/doc/src/Build_settings.rst b/doc/src/Build_settings.rst index a1a900a385..6db19c3379 100644 --- a/doc/src/Build_settings.rst +++ b/doc/src/Build_settings.rst @@ -124,7 +124,7 @@ libraries and better pipelining for packing and communication. # FFTW (same as FFTW3), or MKL FFT_INC = -DFFT_KOKKOS_ # where is KISS (default), FFTW3, # FFTW (same as FFTW3), MKL, CUFFT, HIPFFT - # or MKL_GPU + # or MKL_GPU FFT_INC = -DFFT_SINGLE # do not specify for double precision FFT_INC = -DFFT_FFTW_THREADS # enable using threaded FFTW3 libraries FFT_INC = -DFFT_MKL_THREADS # enable using threaded FFTs with MKL libraries @@ -142,8 +142,8 @@ libraries and better pipelining for packing and communication. # cuFFT either precision FFT_LIB = -lcufft - # MKL_GPU either precision - FFT_LIB = -lmkl_sycl_dft -lmkl_intel_ilp64 -lmkl_tbb_thread -lmkl_core -ltbb + # MKL_GPU either precision + FFT_LIB = -lmkl_sycl_dft -lmkl_intel_ilp64 -lmkl_tbb_thread -lmkl_core -ltbb # FFTW3 double precision FFT_LIB = -lfftw3 @@ -227,7 +227,7 @@ AMD's HIP installations, respectively. These FFT libraries require the Kokkos acceleration package to be enabled and the Kokkos back end to be GPU-resident (i.e., HIP or CUDA). Similarly, GPU offload of FFTs on Intel GPUs with oneMKL currently requires the Kokkos acceleration -package to be enabled with the SYCL backend. +package to be enabled with the SYCL back end. Performing 3d FFTs in parallel can be time-consuming due to data access and required communication. This cost can be reduced by performing diff --git a/doc/utils/sphinx-config/false_positives.txt b/doc/utils/sphinx-config/false_positives.txt index cfbddbe5f6..44659030ec 100644 --- a/doc/utils/sphinx-config/false_positives.txt +++ b/doc/utils/sphinx-config/false_positives.txt @@ -2725,6 +2725,7 @@ OMP oneAPI onebody onelevel +oneMKL oneway onlysalt ons diff --git a/src/KOKKOS/fft3d_kokkos.cpp b/src/KOKKOS/fft3d_kokkos.cpp index defccd337f..2f678f1af3 100644 --- a/src/KOKKOS/fft3d_kokkos.cpp +++ b/src/KOKKOS/fft3d_kokkos.cpp @@ -223,7 +223,7 @@ void FFT3dKokkos::fft_3d_kokkos(typename FFT_AT::t_FFT_DATA_1d d_in, total = plan->total1; length = plan->length1; - + #if defined(FFT_KOKKOS_MKL_GPU) if (flag == 1) oneapi::mkl::dft::compute_forward(*(plan->desc_fast), (FFT_SCALAR*)d_data.data()); @@ -274,7 +274,7 @@ void FFT3dKokkos::fft_3d_kokkos(typename FFT_AT::t_FFT_DATA_1d d_in, total = plan->total2; length = plan->length2; - + #if defined(FFT_KOKKOS_MKL_GPU) if (flag == 1) oneapi::mkl::dft::compute_forward(*(plan->desc_mid), (FFT_SCALAR*)d_data.data()); @@ -630,25 +630,25 @@ struct fft_plan_3d_kokkos* FFT3dKokkos::fft_3d_create_pl #if defined(FFT_KOKKOS_MKL_GPU) sycl::queue queue = LMPDeviceType().sycl_queue(); - + plan->desc_fast = new descriptor_t (nfast); plan->desc_fast->set_value(oneapi::mkl::dft::config_param::NUMBER_OF_TRANSFORMS, plan->total1/nfast); plan->desc_fast->set_value(oneapi::mkl::dft::config_param::FWD_DISTANCE, plan->length1); plan->desc_fast->set_value(oneapi::mkl::dft::config_param::BWD_DISTANCE, plan->length1); plan->desc_fast->commit(queue); - + plan->desc_mid = new descriptor_t (nmid); plan->desc_mid->set_value(oneapi::mkl::dft::config_param::NUMBER_OF_TRANSFORMS, plan->total2/nmid); plan->desc_mid->set_value(oneapi::mkl::dft::config_param::FWD_DISTANCE, plan->length2); plan->desc_mid->set_value(oneapi::mkl::dft::config_param::BWD_DISTANCE, plan->length2); plan->desc_mid->commit(queue); - + plan->desc_slow = new descriptor_t (nslow); plan->desc_slow->set_value(oneapi::mkl::dft::config_param::NUMBER_OF_TRANSFORMS, plan->total3/nslow); plan->desc_slow->set_value(oneapi::mkl::dft::config_param::FWD_DISTANCE, plan->length3); plan->desc_slow->set_value(oneapi::mkl::dft::config_param::BWD_DISTANCE, plan->length3); plan->desc_slow->commit(queue); - + #elif defined(FFT_KOKKOS_MKL) DftiCreateDescriptor( &(plan->handle_fast), FFT_KOKKOS_MKL_PREC, DFTI_COMPLEX, 1, (MKL_LONG)nfast); @@ -910,7 +910,7 @@ void FFT3dKokkos::fft_3d_1d_only_kokkos(typename FFT_AT::t_FFT_DATA_ // perform 1d FFTs in each of 3 dimensions // data is just an array of 0.0 - + #if defined(FFT_KOKKOS_MKL_GPU) if (flag == -1) { oneapi::mkl::dft::compute_forward(*(plan->desc_fast), (FFT_SCALAR*)d_data.data()); From d67e0e6a4168e7a60350e82d4c6ac7bcb7122728 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 5 Sep 2024 21:47:35 -0400 Subject: [PATCH 218/355] consolidate spelling --- doc/src/Build_extras.rst | 6 ++++-- doc/utils/sphinx-config/false_positives.txt | 1 - 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/src/Build_extras.rst b/doc/src/Build_extras.rst index 1dd64b3160..6af3b28dc8 100644 --- a/doc/src/Build_extras.rst +++ b/doc/src/Build_extras.rst @@ -7,6 +7,8 @@ in addition to .. list-table:: :align: center :header-rows: 1 + :widths: 50 50 + :width: 80% * - CMake build - Traditional make @@ -115,7 +117,7 @@ GPU package To build with this package, you must choose options for precision and which GPU hardware to build for. The GPU package currently supports -three different types of backends: OpenCL, CUDA and HIP. +three different types of back ends: OpenCL, CUDA and HIP. CMake build ^^^^^^^^^^^ @@ -205,7 +207,7 @@ necessary for ``hipcc`` and the linker to work correctly. .. versionadded:: 3Aug2022 Using the CHIP-SPV implementation of HIP is supported. It allows one to -run HIP code on Intel GPUs via the OpenCL or Level Zero backends. To use +run HIP code on Intel GPUs via the OpenCL or Level Zero back ends. To use CHIP-SPV, you must set ``-DHIP_USE_DEVICE_SORT=OFF`` in your CMake command line as CHIP-SPV does not yet support hipCUB. As of Summer 2022, the use of HIP for Intel GPUs is experimental. You should only use this diff --git a/doc/utils/sphinx-config/false_positives.txt b/doc/utils/sphinx-config/false_positives.txt index 4ee25ffc12..c4164d9f1d 100644 --- a/doc/utils/sphinx-config/false_positives.txt +++ b/doc/utils/sphinx-config/false_positives.txt @@ -215,7 +215,6 @@ ba Babadi Babaei backcolor -backends Baczewski Bagchi Bagi From 360fdabc19b67e89906f6a27cbfd9acfcd16264b Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 5 Sep 2024 23:05:25 -0400 Subject: [PATCH 219/355] small clarification --- doc/src/Howto_pylammps.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/src/Howto_pylammps.rst b/doc/src/Howto_pylammps.rst index 5ef3248e1d..645434bbab 100644 --- a/doc/src/Howto_pylammps.rst +++ b/doc/src/Howto_pylammps.rst @@ -39,16 +39,18 @@ lammps.lammps * interface modeled after the LAMMPS :ref:`C language library interface API ` * requires knowledge of how LAMMPS internally works (C pointers, etc) * full support for running Python with MPI using `mpi4py `_ +* no overhead from creating a more Python-like interface lammps.PyLammps """"""""""""""" -* higher-level abstraction built on *top* of original :py:class:`ctypes based interface ` +* higher-level abstraction built on *top* of the original :py:class:`ctypes based interface ` * manipulation of Python objects * communication with LAMMPS is hidden from API user * shorter, more concise Python * better IPython integration, designed for quick prototyping * designed for serial execution +* additional overhead from capturing and parsing the LAMMPS screen output Quick Start ----------- From 3e2f929f3196534207210c80135dbc2370e31868 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 5 Sep 2024 23:22:04 -0400 Subject: [PATCH 220/355] must call Error::all() from all MPI ranks. --- src/MC/fix_charge_regulation.cpp | 2 +- src/MC/fix_gcmc.cpp | 4 ++-- src/MC/fix_widom.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/MC/fix_charge_regulation.cpp b/src/MC/fix_charge_regulation.cpp index cd035781c8..ddf14f6804 100644 --- a/src/MC/fix_charge_regulation.cpp +++ b/src/MC/fix_charge_regulation.cpp @@ -218,7 +218,7 @@ void FixChargeRegulation::init() { int flagall = flag; MPI_Allreduce(&flag, &flagall, 1, MPI_INT, MPI_SUM, world); - if (flagall && comm->me == 0) + if (flagall) error->all(FLERR, "fix charge/regulation cannot exchange " "individual atoms (ions) belonging to a molecule"); } diff --git a/src/MC/fix_gcmc.cpp b/src/MC/fix_gcmc.cpp index e0f1cd243b..8fb778207b 100644 --- a/src/MC/fix_gcmc.cpp +++ b/src/MC/fix_gcmc.cpp @@ -564,7 +564,7 @@ void FixGCMC::init() if (molecule[i]) flag = 1; int flagall; MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_SUM,world); - if (flagall && comm->me == 0) + if (flagall) error->all(FLERR, "Fix gcmc cannot exchange individual atoms belonging to a molecule"); } @@ -579,7 +579,7 @@ void FixGCMC::init() if (molecule[i] == 0) flag = 1; int flagall; MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_SUM,world); - if (flagall && comm->me == 0) + if (flagall) error->all(FLERR, "All mol IDs should be set for fix gcmc group atoms"); } diff --git a/src/MC/fix_widom.cpp b/src/MC/fix_widom.cpp index 2c76050430..c72beb5051 100644 --- a/src/MC/fix_widom.cpp +++ b/src/MC/fix_widom.cpp @@ -356,7 +356,7 @@ void FixWidom::init() if (molecule[i] == 0) flag = 1; int flagall; MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_SUM,world); - if (flagall && comm->me == 0) + if (flagall) error->all(FLERR, "All mol IDs should be set for fix widom group atoms"); } From 4554c0367b47676b84c639f9178c0a5215c3e4eb Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 6 Sep 2024 07:24:07 -0400 Subject: [PATCH 221/355] use memcpy() instead of strncpy() to avoid issues with adding a terminal zero and array boundaries --- tools/msi2lmp/src/GetParameters.c | 120 ++++++++++++++---------------- tools/msi2lmp/src/MakeLists.c | 4 +- tools/msi2lmp/src/SearchAndFill.c | 4 +- tools/msi2lmp/src/msi2lmp.c | 4 + tools/msi2lmp/src/msi2lmp.h | 2 +- 5 files changed, 66 insertions(+), 68 deletions(-) diff --git a/tools/msi2lmp/src/GetParameters.c b/tools/msi2lmp/src/GetParameters.c index 921e37491f..b7796de939 100644 --- a/tools/msi2lmp/src/GetParameters.c +++ b/tools/msi2lmp/src/GetParameters.c @@ -44,7 +44,7 @@ void GetParameters() for (i=0; i < no_atom_types; i++) { backwards = -1; - strncpy(potential_types[0],atomtypes[i].potential,5); + memcpy(potential_types[0],atomtypes[i].potential,5); k = find_match(1,potential_types,ff_atomtypes,&backwards); if (k < 0) { printf(" Unable to find mass for %s\n",atomtypes[i].potential); @@ -63,7 +63,7 @@ void GetParameters() for (i=0; i < no_atom_types; i++) { backwards = 0; for (j=0; j < 2; j++) atomtypes[i].params[j] = 0.0; - strncpy(potential_types[0],atomtypes[i].potential,5); + memcpy(potential_types[0],atomtypes[i].potential,5); k = find_match(1,potential_types,ff_vdw,&backwards); if (k < 0) { get_equivs(1,potential_types,equiv_types); @@ -101,7 +101,7 @@ void GetParameters() printf("\n Atom Types, Masses and VDW Parameters\n"); for (i=0; i < no_atom_types; i++) { printf(" %3s %8.4f %8.4f %8.4f\n", - atomtypes[i].potential,atomtypes[i].mass, atomtypes[i].params[0],atomtypes[i].params[1]); + atomtypes[i].potential,atomtypes[i].mass,atomtypes[i].params[0],atomtypes[i].params[1]); } } @@ -115,8 +115,7 @@ void GetParameters() backwards = 0; for (j=0; j < 4; j++) bondtypes[i].params[j] = 0.0; for (j=0; j < 2; j++) - strncpy(potential_types[j], - atomtypes[bondtypes[i].types[j]].potential,5); + memcpy(potential_types[j],atomtypes[bondtypes[i].types[j]].potential,5); k = find_match(2,potential_types,ff_bond,&backwards); if (k < 0) { get_equivs(2,potential_types,equiv_types); @@ -172,7 +171,7 @@ void GetParameters() backwards = 0; for (j=0; j < 4; j++) angletypes[i].params[j] = 0.0; for (j=0; j < 3; j++) - strncpy(potential_types[j],atomtypes[angletypes[i].types[j]].potential,5); + memcpy(potential_types[j],atomtypes[angletypes[i].types[j]].potential,5); k = find_match(3,potential_types,ff_ang,&backwards); if (k < 0) { get_equivs(3,potential_types,equiv_types); @@ -295,8 +294,7 @@ void GetParameters() for (j=0; j < 6; j++) dihedraltypes[i].params[j] = 0.0; for (j=0; j < 4; j++) - strncpy(potential_types[j], - atomtypes[dihedraltypes[i].types[j]].potential,5); + memcpy(potential_types[j],atomtypes[dihedraltypes[i].types[j]].potential,5); backwards = 0; k = find_match(4,potential_types,ff_tor,&backwards); @@ -614,8 +612,7 @@ void GetParameters() for (i=0; i < no_oop_types; i++) { for (j=0; j < 3; j++) ooptypes[i].params[j] = 0.0; for (j=0; j < 4; j++) - strncpy(potential_types[j], - atomtypes[ooptypes[i].types[j]].potential,5); + memcpy(potential_types[j],atomtypes[ooptypes[i].types[j]].potential,5); k = find_improper_body_data(potential_types,ff_oop,&rearrange); if (k < 0) { @@ -658,8 +655,7 @@ void GetParameters() for (j=0; j < 3; j++) ooptypes[i].params[j] = 0.0; for (j=0; j < 4; j++) - strncpy(potential_types[j], - atomtypes[ooptypes[i].types[j]].potential,5); + memcpy(potential_types[j],atomtypes[ooptypes[i].types[j]].potential,5); k = find_trigonal_body_data(potential_types,ff_oop); if (k < 0) { get_equivs(5,potential_types,equiv_types); @@ -715,8 +711,7 @@ void GetParameters() for (j=0; j < 6; j++) ooptypes[i].angleangle_params[j] = 0.0; for (j=0; j < 4; j++) - strncpy(potential_types[j], - atomtypes[ooptypes[i].types[j]].potential,5); + memcpy(potential_types[j],atomtypes[ooptypes[i].types[j]].potential,5); tabc = get_t0(ooptypes[i].types[0], @@ -763,8 +758,7 @@ void GetParameters() for (i=0; i < no_angleangle_types; i++) { for (j=0; j < 6; j++) angleangletypes[i].params[j] = 0.0; for (j=0; j < 4; j++) - strncpy(potential_types[j], - atomtypes[angleangletypes[i].types[j]].potential,5); + memcpy(potential_types[j],atomtypes[angleangletypes[i].types[j]].potential,5); tabc = get_t0(angleangletypes[i].types[0], angleangletypes[i].types[1], @@ -841,44 +835,44 @@ int find_improper_body_data(char types1[][5],struct FrcFieldItem item, /* a b d c */ *rearrange_ptr = 1; - strncpy(mirror_types[0],types1[0],5); - strncpy(mirror_types[1],types1[1],5); - strncpy(mirror_types[2],types1[3],5); - strncpy(mirror_types[3],types1[2],5); + memcpy(mirror_types[0],types1[0],5); + memcpy(mirror_types[1],types1[1],5); + memcpy(mirror_types[2],types1[3],5); + memcpy(mirror_types[3],types1[2],5); k = find_match(4,mirror_types,item,&backwards); if (k >= 0) return k; /* d b a c */ *rearrange_ptr = 2; - strncpy(mirror_types[0],types1[3],5); - strncpy(mirror_types[2],types1[0],5); - strncpy(mirror_types[3],types1[2],5); + memcpy(mirror_types[0],types1[3],5); + memcpy(mirror_types[2],types1[0],5); + memcpy(mirror_types[3],types1[2],5); k = find_match(4,mirror_types,item,&backwards); if (k >= 0) return k; /* d b c a */ *rearrange_ptr = 3; - strncpy(mirror_types[2],types1[2],5); - strncpy(mirror_types[3],types1[0],5); + memcpy(mirror_types[2],types1[2],5); + memcpy(mirror_types[3],types1[0],5); k = find_match(4,mirror_types,item,&backwards); if (k >= 0) return k; /* c b a d */ *rearrange_ptr = 4; - strncpy(mirror_types[0],types1[2],5); - strncpy(mirror_types[2],types1[0],5); - strncpy(mirror_types[3],types1[3],5); + memcpy(mirror_types[0],types1[2],5); + memcpy(mirror_types[2],types1[0],5); + memcpy(mirror_types[3],types1[3],5); k = find_match(4,mirror_types,item,&backwards); if (k >= 0) return k; /* c b d a */ *rearrange_ptr = 5; - strncpy(mirror_types[2],types1[3],5); - strncpy(mirror_types[3],types1[0],5); + memcpy(mirror_types[2],types1[3],5); + memcpy(mirror_types[3],types1[0],5); k = find_match(4,mirror_types,item,&backwards); return k; } @@ -973,39 +967,39 @@ int find_trigonal_body_data(char types1[][5],struct FrcFieldItem item) /* a b d c */ - strncpy(mirror_types[0],types1[0],5); - strncpy(mirror_types[1],types1[1],5); - strncpy(mirror_types[2],types1[3],5); - strncpy(mirror_types[3],types1[2],5); + memcpy(mirror_types[0],types1[0],5); + memcpy(mirror_types[1],types1[1],5); + memcpy(mirror_types[2],types1[3],5); + memcpy(mirror_types[3],types1[2],5); k = find_match(4,mirror_types,item,&backwards); if (k >= 0) return k; /* d b a c */ - strncpy(mirror_types[0],types1[3],5); - strncpy(mirror_types[2],types1[0],5); - strncpy(mirror_types[3],types1[2],5); + memcpy(mirror_types[0],types1[3],5); + memcpy(mirror_types[2],types1[0],5); + memcpy(mirror_types[3],types1[2],5); k = find_match(4,mirror_types,item,&backwards); if (k >= 0) return k; /* d b c a */ - strncpy(mirror_types[2],types1[2],5); - strncpy(mirror_types[3],types1[0],5); + memcpy(mirror_types[2],types1[2],5); + memcpy(mirror_types[3],types1[0],5); k = find_match(4,mirror_types,item,&backwards); if (k >= 0) return k; /* c b a d */ - strncpy(mirror_types[0],types1[2],5); - strncpy(mirror_types[2],types1[0],5); - strncpy(mirror_types[3],types1[3],5); + memcpy(mirror_types[0],types1[2],5); + memcpy(mirror_types[2],types1[0],5); + memcpy(mirror_types[3],types1[3],5); k = find_match(4,mirror_types,item,&backwards); if (k >= 0) return k; /* c b d a */ - strncpy(mirror_types[2],types1[3],5); - strncpy(mirror_types[3],types1[0],5); + memcpy(mirror_types[2],types1[3],5); + memcpy(mirror_types[3],types1[0],5); k = find_match(4,mirror_types,item,&backwards); return k; } @@ -1015,41 +1009,41 @@ int find_angleangle_data(char types1[][5],struct FrcFieldItem item,int kloc[3]) int k,backwards = -1; char mirror_types[4][5]; - strncpy(mirror_types[1],types1[1],5); + memcpy(mirror_types[1],types1[1],5); /* go for first parameter a b c d or d b c a */ k = find_match(4,types1,item,&backwards); if (k < 0) { - strncpy(mirror_types[0],types1[3],5); - strncpy(mirror_types[2],types1[2],5); - strncpy(mirror_types[3],types1[0],5); + memcpy(mirror_types[0],types1[3],5); + memcpy(mirror_types[2],types1[2],5); + memcpy(mirror_types[3],types1[0],5); k = find_match(4,mirror_types,item,&backwards); } kloc[0] = k; /* go for second parameter d b a c or c b a d */ - strncpy(mirror_types[0],types1[3],5); - strncpy(mirror_types[2],types1[0],5); - strncpy(mirror_types[3],types1[2],5); + memcpy(mirror_types[0],types1[3],5); + memcpy(mirror_types[2],types1[0],5); + memcpy(mirror_types[3],types1[2],5); k = find_match(4,mirror_types,item,&backwards); if (k < 0) { - strncpy(mirror_types[0],types1[2],5); - strncpy(mirror_types[3],types1[3],5); + memcpy(mirror_types[0],types1[2],5); + memcpy(mirror_types[3],types1[3],5); k = find_match(4,mirror_types,item,&backwards); } kloc[1] = k; /* go for third parameter a b d c or c b d a */ - strncpy(mirror_types[0],types1[0],5); - strncpy(mirror_types[2],types1[3],5); - strncpy(mirror_types[3],types1[2],5); + memcpy(mirror_types[0],types1[0],5); + memcpy(mirror_types[2],types1[3],5); + memcpy(mirror_types[3],types1[2],5); k = find_match(4,mirror_types,item,&backwards); if (k < 0) { - strncpy(mirror_types[0],types1[2],5); - strncpy(mirror_types[3],types1[0],5); + memcpy(mirror_types[0],types1[2],5); + memcpy(mirror_types[3],types1[0],5); k = find_match(4,mirror_types,item,&backwards); } kloc[2] = k; @@ -1250,25 +1244,25 @@ void get_equivs(int ic,char potential_types[][5],char equiv_types[][5]) switch (ic) { case 1: k = find_equiv_type(potential_types[0]); - if (k > -1) strncpy(equiv_types[0],equivalence.data[k].ff_types[1],5); + if (k > -1) memcpy(equiv_types[0],equivalence.data[k].ff_types[1],5); break; case 2: for (i=0; i < 2; i++) { k = find_equiv_type(potential_types[i]); - if (k > -1) strncpy(equiv_types[i],equivalence.data[k].ff_types[2],5); + if (k > -1) memcpy(equiv_types[i],equivalence.data[k].ff_types[2],5); } break; case 3: for (i=0; i < 3; i++) { k = find_equiv_type(potential_types[i]); - if (k > -1) strncpy(equiv_types[i],equivalence.data[k].ff_types[3],5); + if (k > -1) memcpy(equiv_types[i],equivalence.data[k].ff_types[3],5); } break; case 4: for (i=0; i < 4; i++) { k = find_equiv_type(potential_types[i]); - if (k > -1) strncpy(equiv_types[i],equivalence.data[k].ff_types[4],5); + if (k > -1) memcpy(equiv_types[i],equivalence.data[k].ff_types[4],5); } break; @@ -1276,7 +1270,7 @@ void get_equivs(int ic,char potential_types[][5],char equiv_types[][5]) for (i=0; i < 4; i++) { k = find_equiv_type(potential_types[i]); if (k > -1) - strncpy(equiv_types[i],equivalence.data[k].ff_types[5],5); + memcpy(equiv_types[i],equivalence.data[k].ff_types[5],5); } break; default: diff --git a/tools/msi2lmp/src/MakeLists.c b/tools/msi2lmp/src/MakeLists.c index 18b261a561..4f9a9f1548 100644 --- a/tools/msi2lmp/src/MakeLists.c +++ b/tools/msi2lmp/src/MakeLists.c @@ -476,7 +476,7 @@ void build_atomtypes_list() { int j,k,n,match,atom_type=0; - strncpy(atomtypes[0].potential,atoms[0].potential,5); + memcpy(atomtypes[0].potential,atoms[0].potential,5); atoms[0].type = 0; atomtypes[0].no_connect = atoms[0].no_connect; @@ -497,7 +497,7 @@ void build_atomtypes_list() if (match == 0) { atom_type = n; atomtypes[n].no_connect = atoms[j].no_connect; - strncpy(atomtypes[n++].potential,atoms[j].potential,5); + memcpy(atomtypes[n++].potential,atoms[j].potential,5); } if (n >= MAX_ATOM_TYPES) { fprintf(stderr,"Too many atom types (> 100) - error\n"); diff --git a/tools/msi2lmp/src/SearchAndFill.c b/tools/msi2lmp/src/SearchAndFill.c index 35de0c81fe..a26554aeaa 100644 --- a/tools/msi2lmp/src/SearchAndFill.c +++ b/tools/msi2lmp/src/SearchAndFill.c @@ -212,7 +212,7 @@ void SearchAndFill(struct FrcFieldItem *item) item->data[replace].ver = version; item->data[replace].ref = reference; for (i=0; i < item->number_of_members; i++) { - strncpy(item->data[replace].ff_types[i],atom_types[i],5); + memcpy(item->data[replace].ff_types[i],atom_types[i],5); } for (i=0; i < item->number_of_parameters; i++) { item->data[replace].ff_param[i] = parameters[i]; @@ -230,7 +230,7 @@ void SearchAndFill(struct FrcFieldItem *item) item->data[ctr].ver = version; item->data[ctr].ref = reference; for (i=0; i < item->number_of_members; i++) { - strncpy(item->data[ctr].ff_types[i],atom_types[i],5); + memcpy(item->data[ctr].ff_types[i],atom_types[i],5); } for (i=0; i < item->number_of_parameters; i++) { item->data[ctr].ff_param[i] = parameters[i]; diff --git a/tools/msi2lmp/src/msi2lmp.c b/tools/msi2lmp/src/msi2lmp.c index 68aaf566b2..8228fd0f7f 100644 --- a/tools/msi2lmp/src/msi2lmp.c +++ b/tools/msi2lmp/src/msi2lmp.c @@ -2,6 +2,10 @@ * * msi2lmp.exe * +* v3.9.11 AK - Replace call to strncpy() with memcpy() when copying atom type strings +* to avoid problems with fixed array sizes +* - update tests for newer LAMMPS versions +* * v3.9.10 AK - Substitute UTF-8 characters in .frc files with known ASCII equivalents * - add help message output * diff --git a/tools/msi2lmp/src/msi2lmp.h b/tools/msi2lmp/src/msi2lmp.h index 3e1de85cbe..ca745bdc6e 100644 --- a/tools/msi2lmp/src/msi2lmp.h +++ b/tools/msi2lmp/src/msi2lmp.h @@ -36,7 +36,7 @@ #include /* IWYU pragma: export */ -#define MSI2LMP_VERSION "v3.9.10 / 10 Mar 2023" +#define MSI2LMP_VERSION "v3.9.11 / 6 Sep 2024" #define PI_180 0.01745329251994329576 From 683643c2489189b3b8600ba62658092b9d48d396 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 6 Sep 2024 07:25:01 -0400 Subject: [PATCH 222/355] sort lists of bonds, angles, dihedrals and impropers by atom IDs before comparing --- tools/msi2lmp/test/data-compare.pl | 115 ++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 1 deletion(-) diff --git a/tools/msi2lmp/test/data-compare.pl b/tools/msi2lmp/test/data-compare.pl index 7411f2a017..38bbed051d 100755 --- a/tools/msi2lmp/test/data-compare.pl +++ b/tools/msi2lmp/test/data-compare.pl @@ -2,11 +2,12 @@ # Tool to validate and compare two LAMMPS data files # with "inexact" floating point comparisons # July 2013 by Axel Kohlmeyer +# last update September 2024 by Axel Kohlmeyer use strict; use warnings; -my $version = 'v0.3'; +my $version = 'v0.4'; # delta for floating point comparisons. my $small = 1.0e-4; @@ -596,6 +597,26 @@ sub read_data { last; } + # apply sort + if ($data->{nbonds} > 1) { + my ($did_swap, $num) = (1, $data->{nbonds}); + while ($did_swap) { + $did_swap = 0; + for ($i=0; $i < $num-1; ++$i) { + $j = $i+1; + if (($data->{bond1}[$i] > $data->{bond1}[$j]) + or (($data->{bond1}[$i] == $data->{bond1}[$j]) + and ($data->{bond2}[$i] > $data->{bond2}[$j]))) { + $did_swap = 1; + my @tmp = ($data->{bondt}[$i], $data->{bond1}[$i], $data->{bond2}[$i]); + ($data->{bondt}[$i], $data->{bond1}[$i], $data->{bond2}[$i]) = + ($data->{bondt}[$j], $data->{bond1}[$j], $data->{bond2}[$j]); + ($data->{bondt}[$j], $data->{bond1}[$j], $data->{bond2}[$j]) = @tmp; + } + } + --$num; + } + } } elsif ($1 eq "Angles") { $data->{anglet} = []; $data->{angle1} = []; @@ -638,6 +659,33 @@ sub read_data { last; } + # apply sort + if ($data->{nangles} > 1) { + my ($did_swap, $num) = (1, $data->{nangles}); + while ($did_swap) { + $did_swap = 0; + for ($i=0; $i < $num-1; ++$i) { + $j = $i+1; + if (($data->{angle1}[$i] > $data->{angle1}[$j]) + or (($data->{angle1}[$i] == $data->{angle1}[$j]) + and ($data->{angle2}[$i] > $data->{angle2}[$j])) + or (($data->{angle1}[$i] == $data->{angle1}[$j]) + and ($data->{angle2}[$i] == $data->{angle2}[$j]) + and ($data->{angle3}[$i] > $data->{angle3}[$j]))) { + $did_swap = 1; + my @tmp = ($data->{anglet}[$i], $data->{angle1}[$i], + $data->{angle2}[$i], $data->{angle3}[$i]); + ($data->{anglet}[$i], $data->{angle1}[$i], + $data->{angle2}[$i], $data->{angle3}[$i]) = + ($data->{anglet}[$j], $data->{angle1}[$j], + $data->{angle2}[$j], $data->{angle3}[$j]); + ($data->{anglet}[$j], $data->{angle1}[$j], + $data->{angle2}[$j], $data->{angle3}[$j]) = @tmp; + } + } + --$num; + } + } } elsif ($1 eq "Dihedrals") { $data->{dihedralt} = []; $data->{dihedral1} = []; @@ -684,6 +732,38 @@ sub read_data { last; } + # apply sort + if ($data->{ndihedrals} > 1) { + my ($did_swap, $num) = (1, $data->{ndihedrals}); + while ($did_swap) { + $did_swap = 0; + for ($i=0; $i < $num-1; ++$i) { + $j = $i+1; + if (($data->{dihedral1}[$i] > $data->{dihedral1}[$j]) + or (($data->{dihedral1}[$i] == $data->{dihedral1}[$j]) + and ($data->{dihedral2}[$i] > $data->{dihedral2}[$j])) + or (($data->{dihedral1}[$i] == $data->{dihedral1}[$j]) + and ($data->{dihedral2}[$i] == $data->{dihedral2}[$j]) + and ($data->{dihedral3}[$i] > $data->{dihedral3}[$j])) + or (($data->{dihedral1}[$i] == $data->{dihedral1}[$j]) + and ($data->{dihedral2}[$i] == $data->{dihedral2}[$j]) + and ($data->{dihedral3}[$i] == $data->{dihedral3}[$j]) + and ($data->{dihedral4}[$i] > $data->{dihedral4}[$j]))) { + $did_swap = 1; + my @tmp = ($data->{dihedralt}[$i], $data->{dihedral1}[$i], + $data->{dihedral2}[$i], $data->{dihedral3}[$i], + $data->{dihedral4}[$i]); + ($data->{dihedralt}[$i], $data->{dihedral1}[$i], $data->{dihedral2}[$i], + $data->{dihedral3}[$i], $data->{dihedral4}[$i]) = + ($data->{dihedralt}[$j], $data->{dihedral1}[$j], + $data->{dihedral2}[$j], $data->{dihedral3}[$j], $data->{dihedral4}[$j]); + ($data->{dihedralt}[$j], $data->{dihedral1}[$j], $data->{dihedral2}[$j], + $data->{dihedral3}[$j], $data->{dihedral4}[$j]) = @tmp; + } + } + --$num; + } + } } elsif ($1 eq "Impropers") { $data->{impropert} = []; $data->{improper1} = []; @@ -730,6 +810,39 @@ sub read_data { last; } + # apply sort + if ($data->{nimpropers} > 1) { + my ($did_swap, $num) = (1, $data->{nimpropers}); + while ($did_swap) { + $did_swap = 0; + for ($i=0; $i < $num-1; ++$i) { + $j = $i+1; + if (($data->{improper1}[$i] > $data->{improper1}[$j]) + or (($data->{improper1}[$i] == $data->{improper1}[$j]) + and ($data->{improper2}[$i] > $data->{improper2}[$j])) + or (($data->{improper1}[$i] == $data->{improper1}[$j]) + and ($data->{improper2}[$i] == $data->{improper2}[$j]) + and ($data->{improper3}[$i] > $data->{improper3}[$j])) + or (($data->{improper1}[$i] == $data->{improper1}[$j]) + and ($data->{improper2}[$i] == $data->{improper2}[$j]) + and ($data->{improper3}[$i] == $data->{improper3}[$j]) + and ($data->{improper4}[$i] > $data->{improper4}[$j]))) { + $did_swap = 1; + my @tmp = ($data->{impropert}[$i], $data->{improper1}[$i], + $data->{improper2}[$i], $data->{improper3}[$i], + $data->{improper4}[$i]); + ($data->{impropert}[$i], $data->{improper1}[$i], $data->{improper2}[$i], + $data->{improper3}[$i], $data->{improper4}[$i]) = + ($data->{impropert}[$j], $data->{improper1}[$j], + $data->{improper2}[$j], $data->{improper3}[$j], $data->{improper4}[$j]); + ($data->{impropert}[$j], $data->{improper1}[$j], $data->{improper2}[$j], + $data->{improper3}[$j], $data->{improper4}[$j]) = @tmp; + } + } + --$num; + } + } + } else { die "Bad data: $_"; } From 93de00f6498c5998fa74e5a216b2e601ddc52fe3 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 6 Sep 2024 07:25:52 -0400 Subject: [PATCH 223/355] move kspace style definition after reading the data file to comply with needs of recent LAMMPS versions --- tools/msi2lmp/test/in.PyAC_bulk-clayff | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/msi2lmp/test/in.PyAC_bulk-clayff b/tools/msi2lmp/test/in.PyAC_bulk-clayff index 1c6663a679..923891b920 100644 --- a/tools/msi2lmp/test/in.PyAC_bulk-clayff +++ b/tools/msi2lmp/test/in.PyAC_bulk-clayff @@ -5,9 +5,9 @@ atom_style full pair_style lj/cut/coul/long 15.0 pair_modify mix geometric bond_style harmonic -kspace_style pppm 1.0e-5 read_data PyAC_bulk-clayff.data +kspace_style pppm 1.0e-5 thermo_style multi minimize 0.0 0.0 100 1000 From 513fc95f81b593a00f1ff1cd84dae1f9c9276cce Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 6 Sep 2024 07:30:50 -0400 Subject: [PATCH 224/355] update msi2lmp manpage --- doc/msi2lmp.1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/msi2lmp.1 b/doc/msi2lmp.1 index 5cb0754e4f..075e25e3b9 100644 --- a/doc/msi2lmp.1 +++ b/doc/msi2lmp.1 @@ -1,4 +1,4 @@ -.TH MSI2LMP "1" "v3.9.10" "2023-03-10" +.TH MSI2LMP "1" "v3.9.11" "2024-09-06" .SH NAME .B MSI2LMP \- Converter for Materials Studio files to LAMMPS @@ -101,7 +101,7 @@ msi2lmp decane -c 0 -f oplsaa .SH COPYRIGHT -© 2003--2022 Sandia Corporation +© 2003--2024 Sandia Corporation This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as From f179f2a80dc5280e3b9ffe964115cb564bc4b2aa Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 6 Sep 2024 07:32:19 -0400 Subject: [PATCH 225/355] update a few reference data files for current LAMMPS --- .../test/reference/PyAC_bulk-clayff.data | 708 +-- .../test/reference/PyAC_bulk-clayff.data2 | 5388 ++++++++--------- .../test/reference/ethane-oplsaa.data2 | 42 +- tools/msi2lmp/test/runtests.sh | 4 +- 4 files changed, 3071 insertions(+), 3071 deletions(-) diff --git a/tools/msi2lmp/test/reference/PyAC_bulk-clayff.data b/tools/msi2lmp/test/reference/PyAC_bulk-clayff.data index 6b6602d69d..0a304ea296 100644 --- a/tools/msi2lmp/test/reference/PyAC_bulk-clayff.data +++ b/tools/msi2lmp/test/reference/PyAC_bulk-clayff.data @@ -1,4 +1,4 @@ -LAMMPS data file. msi2lmp v3.9.8 / 06 Oct 2016 / CGCMM for PyAC_bulk-clayff +LAMMPS data file. msi2lmp v3.9.10 / 10 Mar 2023 / CGCMM for PyAC_bulk-clayff 1280 atoms 128 bonds @@ -11,7 +11,7 @@ LAMMPS data file. msi2lmp v3.9.8 / 06 Oct 2016 / CGCMM for PyAC_bulk-clayff -10.320000000 10.320000000 xlo xhi -17.931646038 17.931646038 ylo yhi - -9.196614681 9.196614681 zlo zhi + -9.189871922 9.189871922 zlo zhi 0.225338675 -3.393877748 -0.363656523 xy xz yz Masses @@ -676,27 +676,27 @@ Atoms # full 638 1 3 -1.050000 -0.590722660 -5.885450799 -3.293650032 1 1 0 # ob 639 1 3 -1.050000 0.277898824 -8.390728187 -3.055632561 1 1 0 # ob 640 1 5 0.425000 -4.694842406 -6.184112754 -1.194847627 1 1 0 # ho - 641 1 1 1.575000 0.889889112 1.315464043 9.189872068 0 0 0 # ao - 642 1 2 2.100000 5.101961261 9.067732250 -6.523590700 0 0 1 # st - 643 1 2 2.100000 5.138292081 3.048848378 -6.517157818 0 0 1 # st - 644 1 3 -1.050000 4.852463935 0.176965538 -8.141927041 0 0 1 # ob - 645 1 3 -1.050000 5.291802919 2.921349398 -8.139170101 0 0 1 # ob - 646 1 4 -0.950000 2.692063605 1.889886595 -8.209932150 0 0 1 # oh - 647 1 3 -1.050000 1.366795516 3.605171499 -5.905112111 0 0 1 # ob - 648 1 3 -1.050000 4.839493963 1.584366125 -5.909707262 0 0 1 # ob - 649 1 3 -1.050000 3.970872479 4.089643514 -6.147724732 0 0 1 # ob - 650 1 5 0.425000 3.783613862 1.883028080 -8.008509666 0 0 1 # ho - 651 1 1 1.575000 3.498056417 5.798375353 9.189872068 0 0 0 # ao - 652 1 2 2.100000 2.493793956 4.584820940 -6.523590700 0 0 1 # st - 653 1 2 2.100000 2.586459538 7.531759689 -6.517157818 0 0 1 # st - 654 1 3 -1.050000 2.300631392 4.659876849 -8.141927041 0 0 1 # ob - 655 1 3 -1.050000 2.739970376 7.404260709 -8.139170101 0 0 1 # ob - 656 1 4 -0.950000 5.300230909 6.372797905 -8.209932150 0 0 1 # oh - 657 1 3 -1.050000 3.974962821 8.088082810 -5.905112111 0 0 1 # ob - 658 1 3 -1.050000 2.287661420 6.067277436 -5.909707262 0 0 1 # ob - 659 1 3 -1.050000 1.419039936 8.572554825 -6.147724732 0 0 1 # ob - 660 1 5 0.425000 6.391781167 6.365939391 -8.008509666 0 0 1 # ho - 661 1 1 1.575000 0.932567998 7.286701985 9.189872068 0 0 0 # ao + 641 1 1 1.575000 4.283766860 1.679120566 -9.189871776 0 0 1 # ao + 642 1 2 2.100000 5.101961261 9.067732250 -6.510105182 0 0 1 # st + 643 1 2 2.100000 5.138292081 3.048848378 -6.503672300 0 0 1 # st + 644 1 3 -1.050000 4.852463935 0.176965538 -8.128441523 0 0 1 # ob + 645 1 3 -1.050000 5.291802919 2.921349398 -8.125684583 0 0 1 # ob + 646 1 4 -0.950000 2.692063605 1.889886595 -8.196446632 0 0 1 # oh + 647 1 3 -1.050000 1.366795516 3.605171499 -5.891626593 0 0 1 # ob + 648 1 3 -1.050000 4.839493963 1.584366125 -5.896221744 0 0 1 # ob + 649 1 3 -1.050000 3.970872479 4.089643514 -6.134239214 0 0 1 # ob + 650 1 5 0.425000 3.783613862 1.883028080 -7.995024148 0 0 1 # ho + 651 1 1 1.575000 6.891934165 6.162031876 -9.189871776 0 0 1 # ao + 652 1 2 2.100000 2.493793956 4.584820940 -6.510105182 0 0 1 # st + 653 1 2 2.100000 2.586459538 7.531759689 -6.503672300 0 0 1 # st + 654 1 3 -1.050000 2.300631392 4.659876849 -8.128441523 0 0 1 # ob + 655 1 3 -1.050000 2.739970376 7.404260709 -8.125684583 0 0 1 # ob + 656 1 4 -0.950000 5.300230909 6.372797905 -8.196446632 0 0 1 # oh + 657 1 3 -1.050000 3.974962821 8.088082810 -5.891626593 0 0 1 # ob + 658 1 3 -1.050000 2.287661420 6.067277436 -5.896221744 0 0 1 # ob + 659 1 3 -1.050000 1.419039936 8.572554825 -6.134239214 0 0 1 # ob + 660 1 5 0.425000 6.391781167 6.365939391 -7.995024148 0 0 1 # ho + 661 1 1 1.575000 4.326445746 7.650358508 -9.189871776 0 0 1 # ao 662 1 2 2.100000 0.114373597 -0.101909699 6.510105475 0 0 0 # st 663 1 2 2.100000 0.078042778 5.916974173 6.503672592 0 0 0 # st 664 1 3 -1.050000 0.363870923 8.788857013 8.128441816 0 0 0 # ob @@ -706,7 +706,7 @@ Atoms # full 668 1 3 -1.050000 0.376840895 7.381456426 5.896222036 0 0 0 # ob 669 1 3 -1.050000 1.245462379 4.876179037 6.134239507 0 0 0 # ob 670 1 5 0.425000 1.432720996 7.082794471 7.995024441 0 0 0 # ho - 671 1 1 1.575000 -1.675599307 2.803790675 9.189872068 0 0 0 # ao + 671 1 1 1.575000 1.718278441 3.167447198 -9.189871776 0 0 1 # ao 672 1 2 2.100000 2.722540902 4.381001611 6.510105475 0 0 0 # st 673 1 2 2.100000 2.629875320 1.434062862 6.503672592 0 0 0 # st 674 1 3 -1.050000 2.915703466 4.305945702 8.128441816 0 0 0 # ob @@ -716,27 +716,27 @@ Atoms # full 678 1 3 -1.050000 2.928673438 2.898545115 5.896222036 0 0 0 # ob 679 1 3 -1.050000 3.797294922 0.393267726 6.134239507 0 0 0 # ob 680 1 5 0.425000 -1.175446309 2.599883160 7.995024441 0 0 0 # ho - 681 1 1 1.575000 6.049888960 1.315464043 9.189872068 0 0 0 # ao - 682 1 2 2.100000 -10.378038892 9.067732250 -6.523590700 1 0 1 # st - 683 1 2 2.100000 -10.341708072 3.048848378 -6.517157818 1 0 1 # st - 684 1 3 -1.050000 10.012463782 0.176965538 -8.141927041 0 0 1 # ob - 685 1 3 -1.050000 -10.188197233 2.921349398 -8.139170101 1 0 1 # ob - 686 1 4 -0.950000 7.852063452 1.889886595 -8.209932150 0 0 1 # oh - 687 1 3 -1.050000 6.526795364 3.605171499 -5.905112111 0 0 1 # ob - 688 1 3 -1.050000 -10.640506189 1.584366125 -5.909707262 1 0 1 # ob - 689 1 3 -1.050000 9.130872326 4.089643514 -6.147724732 0 0 1 # ob - 690 1 5 0.425000 8.943613710 1.883028080 -8.008509666 0 0 1 # ho - 691 1 1 1.575000 -11.981943736 5.798375353 9.189872068 1 0 0 # ao - 692 1 2 2.100000 7.653793804 4.584820940 -6.523590700 0 0 1 # st - 693 1 2 2.100000 7.746459385 7.531759689 -6.517157818 0 0 1 # st - 694 1 3 -1.050000 7.460631240 4.659876849 -8.141927041 0 0 1 # ob - 695 1 3 -1.050000 7.899970224 7.404260709 -8.139170101 0 0 1 # ob - 696 1 4 -0.950000 -10.179769243 6.372797905 -8.209932150 1 0 1 # oh - 697 1 3 -1.050000 9.134962668 8.088082810 -5.905112111 0 0 1 # ob - 698 1 3 -1.050000 7.447661268 6.067277436 -5.909707262 0 0 1 # ob - 699 1 3 -1.050000 6.579039784 8.572554825 -6.147724732 0 0 1 # ob - 700 1 5 0.425000 -9.088218986 6.365939391 -8.008509666 1 0 1 # ho - 701 1 1 1.575000 6.092567845 7.286701985 9.189872068 0 0 0 # ao + 681 1 1 1.575000 9.443766708 1.679120566 -9.189871776 0 0 1 # ao + 682 1 2 2.100000 -10.378038892 9.067732250 -6.510105182 1 0 1 # st + 683 1 2 2.100000 -10.341708072 3.048848378 -6.503672300 1 0 1 # st + 684 1 3 -1.050000 10.012463782 0.176965538 -8.128441523 0 0 1 # ob + 685 1 3 -1.050000 -10.188197233 2.921349398 -8.125684583 1 0 1 # ob + 686 1 4 -0.950000 7.852063452 1.889886595 -8.196446632 0 0 1 # oh + 687 1 3 -1.050000 6.526795364 3.605171499 -5.891626593 0 0 1 # ob + 688 1 3 -1.050000 -10.640506189 1.584366125 -5.896221744 1 0 1 # ob + 689 1 3 -1.050000 9.130872326 4.089643514 -6.134239214 0 0 1 # ob + 690 1 5 0.425000 8.943613710 1.883028080 -7.995024148 0 0 1 # ho + 691 1 1 1.575000 -8.588065988 6.162031876 -9.189871776 1 0 1 # ao + 692 1 2 2.100000 7.653793804 4.584820940 -6.510105182 0 0 1 # st + 693 1 2 2.100000 7.746459385 7.531759689 -6.503672300 0 0 1 # st + 694 1 3 -1.050000 7.460631240 4.659876849 -8.128441523 0 0 1 # ob + 695 1 3 -1.050000 7.899970224 7.404260709 -8.125684583 0 0 1 # ob + 696 1 4 -0.950000 -10.179769243 6.372797905 -8.196446632 1 0 1 # oh + 697 1 3 -1.050000 9.134962668 8.088082810 -5.891626593 0 0 1 # ob + 698 1 3 -1.050000 7.447661268 6.067277436 -5.896221744 0 0 1 # ob + 699 1 3 -1.050000 6.579039784 8.572554825 -6.134239214 0 0 1 # ob + 700 1 5 0.425000 -9.088218986 6.365939391 -7.995024148 1 0 1 # ho + 701 1 1 1.575000 9.486445593 7.650358508 -9.189871776 0 0 1 # ao 702 1 2 2.100000 5.274373445 -0.101909699 6.510105475 0 0 0 # st 703 1 2 2.100000 5.238042625 5.916974173 6.503672592 0 0 0 # st 704 1 3 -1.050000 5.523870771 8.788857013 8.128441816 0 0 0 # ob @@ -746,7 +746,7 @@ Atoms # full 708 1 3 -1.050000 5.536840742 7.381456426 5.896222036 0 0 0 # ob 709 1 3 -1.050000 6.405462227 4.876179037 6.134239507 0 0 0 # ob 710 1 5 0.425000 6.592720843 7.082794471 7.995024441 0 0 0 # ho - 711 1 1 1.575000 3.484400541 2.803790675 9.189872068 0 0 0 # ao + 711 1 1 1.575000 6.878278289 3.167447198 -9.189871776 0 0 1 # ao 712 1 2 2.100000 -12.757459251 4.381001611 6.510105475 1 0 0 # st 713 1 2 2.100000 -12.850124832 1.434062862 6.503672592 1 0 0 # st 714 1 3 -1.050000 -12.564296687 4.305945702 8.128441816 1 0 0 # ob @@ -756,27 +756,27 @@ Atoms # full 718 1 3 -1.050000 -12.551326715 2.898545115 5.896222036 1 0 0 # ob 719 1 3 -1.050000 -11.682705231 0.393267726 6.134239507 1 0 0 # ob 720 1 5 0.425000 3.984553539 2.599883160 7.995024441 0 0 0 # ho - 721 1 1 1.575000 -9.430111193 1.315464043 9.189872068 1 0 0 # ao - 722 1 2 2.100000 -5.218039044 9.067732250 -6.523590700 1 0 1 # st - 723 1 2 2.100000 -5.181708225 3.048848378 -6.517157818 1 0 1 # st - 724 1 3 -1.050000 -5.467536370 0.176965538 -8.141927041 1 0 1 # ob - 725 1 3 -1.050000 -5.028197386 2.921349398 -8.139170101 1 0 1 # ob - 726 1 4 -0.950000 -7.627936701 1.889886595 -8.209932150 1 0 1 # oh - 727 1 3 -1.050000 -8.953204789 3.605171499 -5.905112111 1 0 1 # ob - 728 1 3 -1.050000 -5.480506342 1.584366125 -5.909707262 1 0 1 # ob - 729 1 3 -1.050000 -6.349127826 4.089643514 -6.147724732 1 0 1 # ob - 730 1 5 0.425000 -6.536386443 1.883028080 -8.008509666 1 0 1 # ho - 731 1 1 1.575000 -6.821943888 5.798375353 9.189872068 1 0 0 # ao - 732 1 2 2.100000 -7.826206349 4.584820940 -6.523590700 1 0 1 # st - 733 1 2 2.100000 -7.733540767 7.531759689 -6.517157818 1 0 1 # st - 734 1 3 -1.050000 -8.019368913 4.659876849 -8.141927041 1 0 1 # ob - 735 1 3 -1.050000 -7.580029929 7.404260709 -8.139170101 1 0 1 # ob - 736 1 4 -0.950000 -5.019769396 6.372797905 -8.209932150 1 0 1 # oh - 737 1 3 -1.050000 -6.345037484 8.088082810 -5.905112111 1 0 1 # ob - 738 1 3 -1.050000 -8.032338885 6.067277436 -5.909707262 1 0 1 # ob - 739 1 3 -1.050000 -8.900960369 8.572554825 -6.147724732 1 0 1 # ob - 740 1 5 0.425000 -3.928219138 6.365939391 -8.008509666 1 0 1 # ho - 741 1 1 1.575000 -9.387432307 7.286701985 9.189872068 1 0 0 # ao + 721 1 1 1.575000 -6.036233445 1.679120566 -9.189871776 1 0 1 # ao + 722 1 2 2.100000 -5.218039044 9.067732250 -6.510105182 1 0 1 # st + 723 1 2 2.100000 -5.181708225 3.048848378 -6.503672300 1 0 1 # st + 724 1 3 -1.050000 -5.467536370 0.176965538 -8.128441523 1 0 1 # ob + 725 1 3 -1.050000 -5.028197386 2.921349398 -8.125684583 1 0 1 # ob + 726 1 4 -0.950000 -7.627936701 1.889886595 -8.196446632 1 0 1 # oh + 727 1 3 -1.050000 -8.953204789 3.605171499 -5.891626593 1 0 1 # ob + 728 1 3 -1.050000 -5.480506342 1.584366125 -5.896221744 1 0 1 # ob + 729 1 3 -1.050000 -6.349127826 4.089643514 -6.134239214 1 0 1 # ob + 730 1 5 0.425000 -6.536386443 1.883028080 -7.995024148 1 0 1 # ho + 731 1 1 1.575000 -3.428066140 6.162031876 -9.189871776 1 0 1 # ao + 732 1 2 2.100000 -7.826206349 4.584820940 -6.510105182 1 0 1 # st + 733 1 2 2.100000 -7.733540767 7.531759689 -6.503672300 1 0 1 # st + 734 1 3 -1.050000 -8.019368913 4.659876849 -8.128441523 1 0 1 # ob + 735 1 3 -1.050000 -7.580029929 7.404260709 -8.125684583 1 0 1 # ob + 736 1 4 -0.950000 -5.019769396 6.372797905 -8.196446632 1 0 1 # oh + 737 1 3 -1.050000 -6.345037484 8.088082810 -5.891626593 1 0 1 # ob + 738 1 3 -1.050000 -8.032338885 6.067277436 -5.896221744 1 0 1 # ob + 739 1 3 -1.050000 -8.900960369 8.572554825 -6.134239214 1 0 1 # ob + 740 1 5 0.425000 -3.928219138 6.365939391 -7.995024148 1 0 1 # ho + 741 1 1 1.575000 -5.993554559 7.650358508 -9.189871776 1 0 1 # ao 742 1 2 2.100000 -10.205626708 -0.101909699 6.510105475 1 0 0 # st 743 1 2 2.100000 -10.241957528 5.916974173 6.503672592 1 0 0 # st 744 1 3 -1.050000 -9.956129382 8.788857013 8.128441816 1 0 0 # ob @@ -786,7 +786,7 @@ Atoms # full 748 1 3 -1.050000 -9.943159410 7.381456426 5.896222036 1 0 0 # ob 749 1 3 -1.050000 -9.074537926 4.876179037 6.134239507 1 0 0 # ob 750 1 5 0.425000 -8.887279309 7.082794471 7.995024441 1 0 0 # ho - 751 1 1 1.575000 -11.995599612 2.803790675 9.189872068 1 0 0 # ao + 751 1 1 1.575000 -8.601721864 3.167447198 -9.189871776 1 0 1 # ao 752 1 2 2.100000 -7.597459403 4.381001611 6.510105475 1 0 0 # st 753 1 2 2.100000 -7.690124985 1.434062862 6.503672592 1 0 0 # st 754 1 3 -1.050000 -7.404296839 4.305945702 8.128441816 1 0 0 # ob @@ -796,27 +796,27 @@ Atoms # full 758 1 3 -1.050000 -7.391326867 2.898545115 5.896222036 1 0 0 # ob 759 1 3 -1.050000 -6.522705383 0.393267726 6.134239507 1 0 0 # ob 760 1 5 0.425000 -11.495446614 2.599883160 7.995024441 1 0 0 # ho - 761 1 1 1.575000 -4.270111345 1.315464043 9.189872068 1 0 0 # ao - 762 1 2 2.100000 -0.058039197 9.067732250 -6.523590700 1 0 1 # st - 763 1 2 2.100000 -0.021708377 3.048848378 -6.517157818 1 0 1 # st - 764 1 3 -1.050000 -0.307536523 0.176965538 -8.141927041 1 0 1 # ob - 765 1 3 -1.050000 0.131802461 2.921349398 -8.139170101 1 0 1 # ob - 766 1 4 -0.950000 -2.467936853 1.889886595 -8.209932150 1 0 1 # oh - 767 1 3 -1.050000 -3.793204941 3.605171499 -5.905112111 1 0 1 # ob - 768 1 3 -1.050000 -0.320506495 1.584366125 -5.909707262 1 0 1 # ob - 769 1 3 -1.050000 -1.189127979 4.089643514 -6.147724732 1 0 1 # ob - 770 1 5 0.425000 -1.376386596 1.883028080 -8.008509666 1 0 1 # ho - 771 1 1 1.575000 -1.661944041 5.798375353 9.189872068 1 0 0 # ao - 772 1 2 2.100000 -2.666206502 4.584820940 -6.523590700 1 0 1 # st - 773 1 2 2.100000 -2.573540920 7.531759689 -6.517157818 1 0 1 # st - 774 1 3 -1.050000 -2.859369065 4.659876849 -8.141927041 1 0 1 # ob - 775 1 3 -1.050000 -2.420030081 7.404260709 -8.139170101 1 0 1 # ob - 776 1 4 -0.950000 0.140230451 6.372797905 -8.209932150 1 0 1 # oh - 777 1 3 -1.050000 -1.185037637 8.088082810 -5.905112111 1 0 1 # ob - 778 1 3 -1.050000 -2.872339037 6.067277436 -5.909707262 1 0 1 # ob - 779 1 3 -1.050000 -3.740960522 8.572554825 -6.147724732 1 0 1 # ob - 780 1 5 0.425000 1.231780709 6.365939391 -8.008509666 1 0 1 # ho - 781 1 1 1.575000 -4.227432460 7.286701985 9.189872068 1 0 0 # ao + 761 1 1 1.575000 -0.876233597 1.679120566 -9.189871776 1 0 1 # ao + 762 1 2 2.100000 -0.058039197 9.067732250 -6.510105182 1 0 1 # st + 763 1 2 2.100000 -0.021708377 3.048848378 -6.503672300 1 0 1 # st + 764 1 3 -1.050000 -0.307536523 0.176965538 -8.128441523 1 0 1 # ob + 765 1 3 -1.050000 0.131802461 2.921349398 -8.125684583 1 0 1 # ob + 766 1 4 -0.950000 -2.467936853 1.889886595 -8.196446632 1 0 1 # oh + 767 1 3 -1.050000 -3.793204941 3.605171499 -5.891626593 1 0 1 # ob + 768 1 3 -1.050000 -0.320506495 1.584366125 -5.896221744 1 0 1 # ob + 769 1 3 -1.050000 -1.189127979 4.089643514 -6.134239214 1 0 1 # ob + 770 1 5 0.425000 -1.376386596 1.883028080 -7.995024148 1 0 1 # ho + 771 1 1 1.575000 1.731933707 6.162031876 -9.189871776 1 0 1 # ao + 772 1 2 2.100000 -2.666206502 4.584820940 -6.510105182 1 0 1 # st + 773 1 2 2.100000 -2.573540920 7.531759689 -6.503672300 1 0 1 # st + 774 1 3 -1.050000 -2.859369065 4.659876849 -8.128441523 1 0 1 # ob + 775 1 3 -1.050000 -2.420030081 7.404260709 -8.125684583 1 0 1 # ob + 776 1 4 -0.950000 0.140230451 6.372797905 -8.196446632 1 0 1 # oh + 777 1 3 -1.050000 -1.185037637 8.088082810 -5.891626593 1 0 1 # ob + 778 1 3 -1.050000 -2.872339037 6.067277436 -5.896221744 1 0 1 # ob + 779 1 3 -1.050000 -3.740960522 8.572554825 -6.134239214 1 0 1 # ob + 780 1 5 0.425000 1.231780709 6.365939391 -7.995024148 1 0 1 # ho + 781 1 1 1.575000 -0.833554712 7.650358508 -9.189871776 1 0 1 # ao 782 1 2 2.100000 -5.045626860 -0.101909699 6.510105475 1 0 0 # st 783 1 2 2.100000 -5.081957680 5.916974173 6.503672592 1 0 0 # st 784 1 3 -1.050000 -4.796129535 8.788857013 8.128441816 1 0 0 # ob @@ -826,7 +826,7 @@ Atoms # full 788 1 3 -1.050000 -4.783159563 7.381456426 5.896222036 1 0 0 # ob 789 1 3 -1.050000 -3.914538079 4.876179037 6.134239507 1 0 0 # ob 790 1 5 0.425000 -3.727279462 7.082794471 7.995024441 1 0 0 # ho - 791 1 1 1.575000 -6.835599765 2.803790675 9.189872068 1 0 0 # ao + 791 1 1 1.575000 -3.441722017 3.167447198 -9.189871776 1 0 1 # ao 792 1 2 2.100000 -2.437459556 4.381001611 6.510105475 1 0 0 # st 793 1 2 2.100000 -2.530125137 1.434062862 6.503672592 1 0 0 # st 794 1 3 -1.050000 -2.244296992 4.305945702 8.128441816 1 0 0 # ob @@ -836,27 +836,27 @@ Atoms # full 798 1 3 -1.050000 -2.231327020 2.898545115 5.896222036 1 0 0 # ob 799 1 3 -1.050000 -1.362705536 0.393267726 6.134239507 1 0 0 # ob 800 1 5 0.425000 -6.335446766 2.599883160 7.995024441 1 0 0 # ho - 801 1 1 1.575000 0.946223874 10.281286664 9.189872068 0 0 0 # ao - 802 1 2 2.100000 4.932957348 -17.829737203 -6.523590700 0 1 1 # st - 803 1 2 2.100000 5.194626842 12.014671000 -6.517157818 0 0 1 # st - 804 1 3 -1.050000 4.908798697 9.142788159 -8.141927041 0 0 1 # ob - 805 1 3 -1.050000 5.348137681 11.887172019 -8.139170101 0 0 1 # ob - 806 1 4 -0.950000 2.748398366 10.855709216 -8.209932150 0 0 1 # oh - 807 1 3 -1.050000 1.423130278 12.570994121 -5.905112111 0 0 1 # ob - 808 1 3 -1.050000 4.895828725 10.550188747 -5.909707262 0 0 1 # ob - 809 1 3 -1.050000 4.027207241 13.055466135 -6.147724732 0 0 1 # ob - 810 1 5 0.425000 3.839948624 10.848850702 -8.008509666 0 0 1 # ho - 811 1 1 1.575000 3.554391179 14.764197975 9.189872068 0 0 0 # ao - 812 1 2 2.100000 2.550128718 13.550643561 -6.523590700 0 0 1 # st - 813 1 2 2.100000 2.642794300 16.497582311 -6.517157818 0 0 1 # st - 814 1 3 -1.050000 2.356966154 13.625699470 -8.141927041 0 0 1 # ob - 815 1 3 -1.050000 2.796305138 16.370083330 -8.139170101 0 0 1 # ob - 816 1 4 -0.950000 5.356565671 15.338620527 -8.209932150 0 0 1 # oh - 817 1 3 -1.050000 4.031297583 17.053905432 -5.905112111 0 0 1 # ob - 818 1 3 -1.050000 2.343996182 15.033100058 -5.909707262 0 0 1 # ob - 819 1 3 -1.050000 1.475374698 17.538377446 -6.147724732 0 0 1 # ob - 820 1 5 0.425000 6.448115929 15.331762013 -8.008509666 0 0 1 # ho - 821 1 1 1.575000 0.988902760 16.252524607 9.189872068 0 0 0 # ao + 801 1 1 1.575000 4.340101622 10.644943187 -9.189871776 0 0 1 # ao + 802 1 2 2.100000 4.932957348 -17.829737203 -6.510105182 0 1 1 # st + 803 1 2 2.100000 5.194626842 12.014671000 -6.503672300 0 0 1 # st + 804 1 3 -1.050000 4.908798697 9.142788159 -8.128441523 0 0 1 # ob + 805 1 3 -1.050000 5.348137681 11.887172019 -8.125684583 0 0 1 # ob + 806 1 4 -0.950000 2.748398366 10.855709216 -8.196446632 0 0 1 # oh + 807 1 3 -1.050000 1.423130278 12.570994121 -5.891626593 0 0 1 # ob + 808 1 3 -1.050000 4.895828725 10.550188747 -5.896221744 0 0 1 # ob + 809 1 3 -1.050000 4.027207241 13.055466135 -6.134239214 0 0 1 # ob + 810 1 5 0.425000 3.839948624 10.848850702 -7.995024148 0 0 1 # ho + 811 1 1 1.575000 6.948268927 15.127854498 -9.189871776 0 0 1 # ao + 812 1 2 2.100000 2.550128718 13.550643561 -6.510105182 0 0 1 # st + 813 1 2 2.100000 2.642794300 16.497582311 -6.503672300 0 0 1 # st + 814 1 3 -1.050000 2.356966154 13.625699470 -8.128441523 0 0 1 # ob + 815 1 3 -1.050000 2.796305138 16.370083330 -8.125684583 0 0 1 # ob + 816 1 4 -0.950000 5.356565671 15.338620527 -8.196446632 0 0 1 # oh + 817 1 3 -1.050000 4.031297583 17.053905432 -5.891626593 0 0 1 # ob + 818 1 3 -1.050000 2.343996182 15.033100058 -5.896221744 0 0 1 # ob + 819 1 3 -1.050000 1.475374698 17.538377446 -6.134239214 0 0 1 # ob + 820 1 5 0.425000 6.448115929 15.331762013 -7.995024148 0 0 1 # ho + 821 1 1 1.575000 4.382780508 16.616181130 -9.189871776 0 0 1 # ao 822 1 2 2.100000 0.170708359 8.863912922 6.510105475 0 0 0 # st 823 1 2 2.100000 0.134377539 14.882796794 6.503672592 0 0 0 # st 824 1 3 -1.050000 0.194867010 -18.108612440 8.128441816 0 1 0 # ob @@ -866,7 +866,7 @@ Atoms # full 828 1 3 -1.050000 0.433175657 16.347279047 5.896222036 0 0 0 # ob 829 1 3 -1.050000 1.301797141 13.842001659 6.134239507 0 0 0 # ob 830 1 5 0.425000 1.489055758 16.048617092 7.995024441 0 0 0 # ho - 831 1 1 1.575000 -1.619264545 11.769613296 9.189872068 0 0 0 # ao + 831 1 1 1.575000 1.774613203 12.133269819 -9.189871776 0 0 1 # ao 832 1 2 2.100000 2.778875664 13.346824233 6.510105475 0 0 0 # st 833 1 2 2.100000 2.686210082 10.399885483 6.503672592 0 0 0 # st 834 1 3 -1.050000 2.972038228 13.271768324 8.128441816 0 0 0 # ob @@ -876,27 +876,27 @@ Atoms # full 838 1 3 -1.050000 2.985008200 11.864367737 5.896222036 0 0 0 # ob 839 1 3 -1.050000 3.853629684 9.359090348 6.134239507 0 0 0 # ob 840 1 5 0.425000 -1.119111547 11.565705782 7.995024441 0 0 0 # ho - 841 1 1 1.575000 6.106223722 10.281286664 9.189872068 0 0 0 # ao - 842 1 2 2.100000 -10.547042805 -17.829737203 -6.523590700 1 1 1 # st - 843 1 2 2.100000 -10.285373310 12.014671000 -6.517157818 1 0 1 # st - 844 1 3 -1.050000 10.068798544 9.142788159 -8.141927041 0 0 1 # ob - 845 1 3 -1.050000 -10.131862472 11.887172019 -8.139170101 1 0 1 # ob - 846 1 4 -0.950000 7.908398214 10.855709216 -8.209932150 0 0 1 # oh - 847 1 3 -1.050000 6.583130126 12.570994121 -5.905112111 0 0 1 # ob - 848 1 3 -1.050000 -10.584171428 10.550188747 -5.909707262 1 0 1 # ob - 849 1 3 -1.050000 9.187207088 13.055466135 -6.147724732 0 0 1 # ob - 850 1 5 0.425000 8.999948471 10.848850702 -8.008509666 0 0 1 # ho - 851 1 1 1.575000 -11.925608974 14.764197975 9.189872068 1 0 0 # ao - 852 1 2 2.100000 7.710128565 13.550643561 -6.523590700 0 0 1 # st - 853 1 2 2.100000 7.802794147 16.497582311 -6.517157818 0 0 1 # st - 854 1 3 -1.050000 7.516966002 13.625699470 -8.141927041 0 0 1 # ob - 855 1 3 -1.050000 7.956304986 16.370083330 -8.139170101 0 0 1 # ob - 856 1 4 -0.950000 -10.123434482 15.338620527 -8.209932150 1 0 1 # oh - 857 1 3 -1.050000 9.191297430 17.053905432 -5.905112111 0 0 1 # ob - 858 1 3 -1.050000 7.503996030 15.033100058 -5.909707262 0 0 1 # ob - 859 1 3 -1.050000 6.635374545 17.538377446 -6.147724732 0 0 1 # ob - 860 1 5 0.425000 -9.031884224 15.331762013 -8.008509666 1 0 1 # ho - 861 1 1 1.575000 6.148902607 16.252524607 9.189872068 0 0 0 # ao + 841 1 1 1.575000 9.500101470 10.644943187 -9.189871776 0 0 1 # ao + 842 1 2 2.100000 -10.547042805 -17.829737203 -6.510105182 1 1 1 # st + 843 1 2 2.100000 -10.285373310 12.014671000 -6.503672300 1 0 1 # st + 844 1 3 -1.050000 10.068798544 9.142788159 -8.128441523 0 0 1 # ob + 845 1 3 -1.050000 -10.131862472 11.887172019 -8.125684583 1 0 1 # ob + 846 1 4 -0.950000 7.908398214 10.855709216 -8.196446632 0 0 1 # oh + 847 1 3 -1.050000 6.583130126 12.570994121 -5.891626593 0 0 1 # ob + 848 1 3 -1.050000 -10.584171428 10.550188747 -5.896221744 1 0 1 # ob + 849 1 3 -1.050000 9.187207088 13.055466135 -6.134239214 0 0 1 # ob + 850 1 5 0.425000 8.999948471 10.848850702 -7.995024148 0 0 1 # ho + 851 1 1 1.575000 -8.531731226 15.127854498 -9.189871776 1 0 1 # ao + 852 1 2 2.100000 7.710128565 13.550643561 -6.510105182 0 0 1 # st + 853 1 2 2.100000 7.802794147 16.497582311 -6.503672300 0 0 1 # st + 854 1 3 -1.050000 7.516966002 13.625699470 -8.128441523 0 0 1 # ob + 855 1 3 -1.050000 7.956304986 16.370083330 -8.125684583 0 0 1 # ob + 856 1 4 -0.950000 -10.123434482 15.338620527 -8.196446632 1 0 1 # oh + 857 1 3 -1.050000 9.191297430 17.053905432 -5.891626593 0 0 1 # ob + 858 1 3 -1.050000 7.503996030 15.033100058 -5.896221744 0 0 1 # ob + 859 1 3 -1.050000 6.635374545 17.538377446 -6.134239214 0 0 1 # ob + 860 1 5 0.425000 -9.031884224 15.331762013 -7.995024148 1 0 1 # ho + 861 1 1 1.575000 9.542780355 16.616181130 -9.189871776 0 0 1 # ao 862 1 2 2.100000 5.330708207 8.863912922 6.510105475 0 0 0 # st 863 1 2 2.100000 5.294377387 14.882796794 6.503672592 0 0 0 # st 864 1 3 -1.050000 5.354866857 -18.108612440 8.128441816 0 1 0 # ob @@ -906,7 +906,7 @@ Atoms # full 868 1 3 -1.050000 5.593175504 16.347279047 5.896222036 0 0 0 # ob 869 1 3 -1.050000 6.461796988 13.842001659 6.134239507 0 0 0 # ob 870 1 5 0.425000 6.649055605 16.048617092 7.995024441 0 0 0 # ho - 871 1 1 1.575000 3.540735302 11.769613296 9.189872068 0 0 0 # ao + 871 1 1 1.575000 6.934613050 12.133269819 -9.189871776 0 0 1 # ao 872 1 2 2.100000 -12.701124489 13.346824233 6.510105475 1 0 0 # st 873 1 2 2.100000 -12.793790070 10.399885483 6.503672592 1 0 0 # st 874 1 3 -1.050000 -12.507961925 13.271768324 8.128441816 1 0 0 # ob @@ -916,27 +916,27 @@ Atoms # full 878 1 3 -1.050000 -12.494991953 11.864367737 5.896222036 1 0 0 # ob 879 1 3 -1.050000 -11.626370469 9.359090348 6.134239507 1 0 0 # ob 880 1 5 0.425000 4.040888301 11.565705782 7.995024441 0 0 0 # ho - 881 1 1 1.575000 -9.373776431 10.281286664 9.189872068 1 0 0 # ao - 882 1 2 2.100000 -5.387042958 -17.829737203 -6.523590700 1 1 1 # st - 883 1 2 2.100000 -5.125373463 12.014671000 -6.517157818 1 0 1 # st - 884 1 3 -1.050000 -5.411201608 9.142788159 -8.141927041 1 0 1 # ob - 885 1 3 -1.050000 -4.971862624 11.887172019 -8.139170101 1 0 1 # ob - 886 1 4 -0.950000 -7.571601939 10.855709216 -8.209932150 1 0 1 # oh - 887 1 3 -1.050000 -8.896870027 12.570994121 -5.905112111 1 0 1 # ob - 888 1 3 -1.050000 -5.424171580 10.550188747 -5.909707262 1 0 1 # ob - 889 1 3 -1.050000 -6.292793064 13.055466135 -6.147724732 1 0 1 # ob - 890 1 5 0.425000 -6.480051681 10.848850702 -8.008509666 1 0 1 # ho - 891 1 1 1.575000 -6.765609126 14.764197975 9.189872068 1 0 0 # ao - 892 1 2 2.100000 -7.769871587 13.550643561 -6.523590700 1 0 1 # st - 893 1 2 2.100000 -7.677206006 16.497582311 -6.517157818 1 0 1 # st - 894 1 3 -1.050000 -7.963034151 13.625699470 -8.141927041 1 0 1 # ob - 895 1 3 -1.050000 -7.523695167 16.370083330 -8.139170101 1 0 1 # ob - 896 1 4 -0.950000 -4.963434634 15.338620527 -8.209932150 1 0 1 # oh - 897 1 3 -1.050000 -6.288702722 17.053905432 -5.905112111 1 0 1 # ob - 898 1 3 -1.050000 -7.976004123 15.033100058 -5.909707262 1 0 1 # ob - 899 1 3 -1.050000 -8.844625607 17.538377446 -6.147724732 1 0 1 # ob - 900 1 5 0.425000 -3.871884377 15.331762013 -8.008509666 1 0 1 # ho - 901 1 1 1.575000 -9.331097546 16.252524607 9.189872068 1 0 0 # ao + 881 1 1 1.575000 -5.979898683 10.644943187 -9.189871776 1 0 1 # ao + 882 1 2 2.100000 -5.387042958 -17.829737203 -6.510105182 1 1 1 # st + 883 1 2 2.100000 -5.125373463 12.014671000 -6.503672300 1 0 1 # st + 884 1 3 -1.050000 -5.411201608 9.142788159 -8.128441523 1 0 1 # ob + 885 1 3 -1.050000 -4.971862624 11.887172019 -8.125684583 1 0 1 # ob + 886 1 4 -0.950000 -7.571601939 10.855709216 -8.196446632 1 0 1 # oh + 887 1 3 -1.050000 -8.896870027 12.570994121 -5.891626593 1 0 1 # ob + 888 1 3 -1.050000 -5.424171580 10.550188747 -5.896221744 1 0 1 # ob + 889 1 3 -1.050000 -6.292793064 13.055466135 -6.134239214 1 0 1 # ob + 890 1 5 0.425000 -6.480051681 10.848850702 -7.995024148 1 0 1 # ho + 891 1 1 1.575000 -3.371731378 15.127854498 -9.189871776 1 0 1 # ao + 892 1 2 2.100000 -7.769871587 13.550643561 -6.510105182 1 0 1 # st + 893 1 2 2.100000 -7.677206006 16.497582311 -6.503672300 1 0 1 # st + 894 1 3 -1.050000 -7.963034151 13.625699470 -8.128441523 1 0 1 # ob + 895 1 3 -1.050000 -7.523695167 16.370083330 -8.125684583 1 0 1 # ob + 896 1 4 -0.950000 -4.963434634 15.338620527 -8.196446632 1 0 1 # oh + 897 1 3 -1.050000 -6.288702722 17.053905432 -5.891626593 1 0 1 # ob + 898 1 3 -1.050000 -7.976004123 15.033100058 -5.896221744 1 0 1 # ob + 899 1 3 -1.050000 -8.844625607 17.538377446 -6.134239214 1 0 1 # ob + 900 1 5 0.425000 -3.871884377 15.331762013 -7.995024148 1 0 1 # ho + 901 1 1 1.575000 -5.937219798 16.616181130 -9.189871776 1 0 1 # ao 902 1 2 2.100000 -10.149291946 8.863912922 6.510105475 1 0 0 # st 903 1 2 2.100000 -10.185622766 14.882796794 6.503672592 1 0 0 # st 904 1 3 -1.050000 -10.125133295 -18.108612440 8.128441816 1 1 0 # ob @@ -946,7 +946,7 @@ Atoms # full 908 1 3 -1.050000 -9.886824648 16.347279047 5.896222036 1 0 0 # ob 909 1 3 -1.050000 -9.018203164 13.842001659 6.134239507 1 0 0 # ob 910 1 5 0.425000 -8.830944547 16.048617092 7.995024441 1 0 0 # ho - 911 1 1 1.575000 -11.939264850 11.769613296 9.189872068 1 0 0 # ao + 911 1 1 1.575000 -8.545387102 12.133269819 -9.189871776 1 0 1 # ao 912 1 2 2.100000 -7.541124641 13.346824233 6.510105475 1 0 0 # st 913 1 2 2.100000 -7.633790223 10.399885483 6.503672592 1 0 0 # st 914 1 3 -1.050000 -7.347962077 13.271768324 8.128441816 1 0 0 # ob @@ -956,27 +956,27 @@ Atoms # full 918 1 3 -1.050000 -7.334992106 11.864367737 5.896222036 1 0 0 # ob 919 1 3 -1.050000 -6.466370621 9.359090348 6.134239507 1 0 0 # ob 920 1 5 0.425000 -11.439111852 11.565705782 7.995024441 1 0 0 # ho - 921 1 1 1.575000 -4.213776583 10.281286664 9.189872068 1 0 0 # ao - 922 1 2 2.100000 -0.227043110 -17.829737203 -6.523590700 1 1 1 # st - 923 1 2 2.100000 0.034626385 12.014671000 -6.517157818 1 0 1 # st - 924 1 3 -1.050000 -0.251201761 9.142788159 -8.141927041 1 0 1 # ob - 925 1 3 -1.050000 0.188137223 11.887172019 -8.139170101 1 0 1 # ob - 926 1 4 -0.950000 -2.411602091 10.855709216 -8.209932150 1 0 1 # oh - 927 1 3 -1.050000 -3.736870180 12.570994121 -5.905112111 1 0 1 # ob - 928 1 3 -1.050000 -0.264171733 10.550188747 -5.909707262 1 0 1 # ob - 929 1 3 -1.050000 -1.132793217 13.055466135 -6.147724732 1 0 1 # ob - 930 1 5 0.425000 -1.320051834 10.848850702 -8.008509666 1 0 1 # ho - 931 1 1 1.575000 -1.605609279 14.764197975 9.189872068 1 0 0 # ao - 932 1 2 2.100000 -2.609871740 13.550643561 -6.523590700 1 0 1 # st - 933 1 2 2.100000 -2.517206158 16.497582311 -6.517157818 1 0 1 # st - 934 1 3 -1.050000 -2.803034304 13.625699470 -8.141927041 1 0 1 # ob - 935 1 3 -1.050000 -2.363695320 16.370083330 -8.139170101 1 0 1 # ob - 936 1 4 -0.950000 0.196565213 15.338620527 -8.209932150 1 0 1 # oh - 937 1 3 -1.050000 -1.128702875 17.053905432 -5.905112111 1 0 1 # ob - 938 1 3 -1.050000 -2.816004275 15.033100058 -5.909707262 1 0 1 # ob - 939 1 3 -1.050000 -3.684625760 17.538377446 -6.147724732 1 0 1 # ob - 940 1 5 0.425000 1.288115471 15.331762013 -8.008509666 1 0 1 # ho - 941 1 1 1.575000 -4.171097698 16.252524607 9.189872068 1 0 0 # ao + 921 1 1 1.575000 -0.819898835 10.644943187 -9.189871776 1 0 1 # ao + 922 1 2 2.100000 -0.227043110 -17.829737203 -6.510105182 1 1 1 # st + 923 1 2 2.100000 0.034626385 12.014671000 -6.503672300 1 0 1 # st + 924 1 3 -1.050000 -0.251201761 9.142788159 -8.128441523 1 0 1 # ob + 925 1 3 -1.050000 0.188137223 11.887172019 -8.125684583 1 0 1 # ob + 926 1 4 -0.950000 -2.411602091 10.855709216 -8.196446632 1 0 1 # oh + 927 1 3 -1.050000 -3.736870180 12.570994121 -5.891626593 1 0 1 # ob + 928 1 3 -1.050000 -0.264171733 10.550188747 -5.896221744 1 0 1 # ob + 929 1 3 -1.050000 -1.132793217 13.055466135 -6.134239214 1 0 1 # ob + 930 1 5 0.425000 -1.320051834 10.848850702 -7.995024148 1 0 1 # ho + 931 1 1 1.575000 1.788268469 15.127854498 -9.189871776 1 0 1 # ao + 932 1 2 2.100000 -2.609871740 13.550643561 -6.510105182 1 0 1 # st + 933 1 2 2.100000 -2.517206158 16.497582311 -6.503672300 1 0 1 # st + 934 1 3 -1.050000 -2.803034304 13.625699470 -8.128441523 1 0 1 # ob + 935 1 3 -1.050000 -2.363695320 16.370083330 -8.125684583 1 0 1 # ob + 936 1 4 -0.950000 0.196565213 15.338620527 -8.196446632 1 0 1 # oh + 937 1 3 -1.050000 -1.128702875 17.053905432 -5.891626593 1 0 1 # ob + 938 1 3 -1.050000 -2.816004275 15.033100058 -5.896221744 1 0 1 # ob + 939 1 3 -1.050000 -3.684625760 17.538377446 -6.134239214 1 0 1 # ob + 940 1 5 0.425000 1.288115471 15.331762013 -7.995024148 1 0 1 # ho + 941 1 1 1.575000 -0.777219950 16.616181130 -9.189871776 1 0 1 # ao 942 1 2 2.100000 -4.989292099 8.863912922 6.510105475 1 0 0 # st 943 1 2 2.100000 -5.025622918 14.882796794 6.503672592 1 0 0 # st 944 1 3 -1.050000 -4.965133448 -18.108612440 8.128441816 1 1 0 # ob @@ -986,7 +986,7 @@ Atoms # full 948 1 3 -1.050000 -4.726824801 16.347279047 5.896222036 1 0 0 # ob 949 1 3 -1.050000 -3.858203317 13.842001659 6.134239507 1 0 0 # ob 950 1 5 0.425000 -3.670944700 16.048617092 7.995024441 1 0 0 # ho - 951 1 1 1.575000 -6.779265003 11.769613296 9.189872068 1 0 0 # ao + 951 1 1 1.575000 -3.385387255 12.133269819 -9.189871776 1 0 1 # ao 952 1 2 2.100000 -2.381124794 13.346824233 6.510105475 1 0 0 # st 953 1 2 2.100000 -2.473790376 10.399885483 6.503672592 1 0 0 # st 954 1 3 -1.050000 -2.187962230 13.271768324 8.128441816 1 0 0 # ob @@ -996,27 +996,27 @@ Atoms # full 958 1 3 -1.050000 -2.174992258 11.864367737 5.896222036 1 0 0 # ob 959 1 3 -1.050000 -1.306370774 9.359090348 6.134239507 1 0 0 # ob 960 1 5 0.425000 -6.279112005 11.565705782 7.995024441 1 0 0 # ho - 961 1 1 1.575000 0.777219961 -16.616182789 9.189872068 0 1 0 # ao - 962 1 2 2.100000 4.989292109 -8.863914582 -6.523590700 0 1 1 # st - 963 1 2 2.100000 5.025622929 -14.882798454 -6.517157818 0 1 1 # st - 964 1 3 -1.050000 4.739794784 -17.754681294 -8.141927041 0 1 1 # ob - 965 1 3 -1.050000 5.179133768 -15.010297434 -8.139170101 0 1 1 # ob - 966 1 4 -0.950000 2.579394453 -16.041760237 -8.209932150 0 1 1 # oh - 967 1 3 -1.050000 1.254126365 -14.326475333 -5.905112111 0 1 1 # ob - 968 1 3 -1.050000 4.726824812 -16.347280707 -5.909707262 0 1 1 # ob - 969 1 3 -1.050000 3.858203328 -13.842003318 -6.147724732 0 1 1 # ob - 970 1 5 0.425000 3.670944711 -16.048618752 -8.008509666 0 1 1 # ho - 971 1 1 1.575000 3.385387266 -12.133271479 9.189872068 0 1 0 # ao - 972 1 2 2.100000 2.381124805 -13.346825892 -6.523590700 0 1 1 # st - 973 1 2 2.100000 2.473790386 -10.399887143 -6.517157818 0 1 1 # st - 974 1 3 -1.050000 2.187962241 -13.271769983 -8.141927041 0 1 1 # ob - 975 1 3 -1.050000 2.627301225 -10.527386123 -8.139170101 0 1 1 # ob - 976 1 4 -0.950000 5.187561758 -11.558848927 -8.209932150 0 1 1 # oh - 977 1 3 -1.050000 3.862293670 -9.843564022 -5.905112111 0 1 1 # ob - 978 1 3 -1.050000 2.174992269 -11.864369396 -5.909707262 0 1 1 # ob - 979 1 3 -1.050000 1.306370785 -9.359092007 -6.147724732 0 1 1 # ob - 980 1 5 0.425000 6.279112015 -11.565707441 -8.008509666 0 1 1 # ho - 981 1 1 1.575000 0.819898846 -10.644944847 9.189872068 0 1 0 # ao + 961 1 1 1.575000 4.171097709 -16.252526266 -9.189871776 0 1 1 # ao + 962 1 2 2.100000 4.989292109 -8.863914582 -6.510105182 0 1 1 # st + 963 1 2 2.100000 5.025622929 -14.882798454 -6.503672300 0 1 1 # st + 964 1 3 -1.050000 4.739794784 -17.754681294 -8.128441523 0 1 1 # ob + 965 1 3 -1.050000 5.179133768 -15.010297434 -8.125684583 0 1 1 # ob + 966 1 4 -0.950000 2.579394453 -16.041760237 -8.196446632 0 1 1 # oh + 967 1 3 -1.050000 1.254126365 -14.326475333 -5.891626593 0 1 1 # ob + 968 1 3 -1.050000 4.726824812 -16.347280707 -5.896221744 0 1 1 # ob + 969 1 3 -1.050000 3.858203328 -13.842003318 -6.134239214 0 1 1 # ob + 970 1 5 0.425000 3.670944711 -16.048618752 -7.995024148 0 1 1 # ho + 971 1 1 1.575000 6.779265014 -11.769614956 -9.189871776 0 1 1 # ao + 972 1 2 2.100000 2.381124805 -13.346825892 -6.510105182 0 1 1 # st + 973 1 2 2.100000 2.473790386 -10.399887143 -6.503672300 0 1 1 # st + 974 1 3 -1.050000 2.187962241 -13.271769983 -8.128441523 0 1 1 # ob + 975 1 3 -1.050000 2.627301225 -10.527386123 -8.125684583 0 1 1 # ob + 976 1 4 -0.950000 5.187561758 -11.558848927 -8.196446632 0 1 1 # oh + 977 1 3 -1.050000 3.862293670 -9.843564022 -5.891626593 0 1 1 # ob + 978 1 3 -1.050000 2.174992269 -11.864369396 -5.896221744 0 1 1 # ob + 979 1 3 -1.050000 1.306370785 -9.359092007 -6.134239214 0 1 1 # ob + 980 1 5 0.425000 6.279112015 -11.565707441 -7.995024148 0 1 1 # ho + 981 1 1 1.575000 4.213776594 -10.281288324 -9.189871776 0 1 1 # ao 982 1 2 2.100000 0.001704446 -18.033556531 6.510105475 0 1 0 # st 983 1 2 2.100000 -0.034626374 -12.014672659 6.503672592 0 1 0 # st 984 1 3 -1.050000 0.251201772 -9.142789819 8.128441816 0 1 0 # ob @@ -1026,7 +1026,7 @@ Atoms # full 988 1 3 -1.050000 0.264171744 -10.550190406 5.896222036 0 1 0 # ob 989 1 3 -1.050000 1.132793228 -13.055467795 6.134239507 0 1 0 # ob 990 1 5 0.425000 1.320051845 -10.848852361 7.995024441 0 1 0 # ho - 991 1 1 1.575000 -1.788268458 -15.127856157 9.189872068 0 1 0 # ao + 991 1 1 1.575000 1.605609290 -14.764199634 -9.189871776 0 1 1 # ao 992 1 2 2.100000 2.609871751 -13.550645221 6.510105475 0 1 0 # st 993 1 2 2.100000 2.517206169 -16.497583970 6.503672592 0 1 0 # st 994 1 3 -1.050000 2.803034315 -13.625701130 8.128441816 0 1 0 # ob @@ -1036,27 +1036,27 @@ Atoms # full 998 1 3 -1.050000 2.816004286 -15.033101717 5.896222036 0 1 0 # ob 999 1 3 -1.050000 3.684625771 -17.538379106 6.134239507 0 1 0 # ob 1000 1 5 0.425000 -1.288115460 -15.331763672 7.995024441 0 1 0 # ho - 1001 1 1 1.575000 5.937219808 -16.616182789 9.189872068 0 1 0 # ao - 1002 1 2 2.100000 -10.490708043 -8.863914582 -6.523590700 1 1 1 # st - 1003 1 2 2.100000 -10.454377223 -14.882798454 -6.517157818 1 1 1 # st - 1004 1 3 -1.050000 9.899794631 -17.754681294 -8.141927041 0 1 1 # ob - 1005 1 3 -1.050000 -10.300866385 -15.010297434 -8.139170101 1 1 1 # ob - 1006 1 4 -0.950000 7.739394301 -16.041760237 -8.209932150 0 1 1 # oh - 1007 1 3 -1.050000 6.414126212 -14.326475333 -5.905112111 0 1 1 # ob - 1008 1 3 -1.050000 -10.753175341 -16.347280707 -5.909707262 1 1 1 # ob - 1009 1 3 -1.050000 9.018203175 -13.842003318 -6.147724732 0 1 1 # ob - 1010 1 5 0.425000 8.830944558 -16.048618752 -8.008509666 0 1 1 # ho - 1011 1 1 1.575000 -12.094612887 -12.133271479 9.189872068 1 1 0 # ao - 1012 1 2 2.100000 7.541124652 -13.346825892 -6.523590700 0 1 1 # st - 1013 1 2 2.100000 7.633790234 -10.399887143 -6.517157818 0 1 1 # st - 1014 1 3 -1.050000 7.347962088 -13.271769983 -8.141927041 0 1 1 # ob - 1015 1 3 -1.050000 7.787301072 -10.527386123 -8.139170101 0 1 1 # ob - 1016 1 4 -0.950000 -10.292438395 -11.558848927 -8.209932150 1 1 1 # oh - 1017 1 3 -1.050000 9.022293517 -9.843564022 -5.905112111 0 1 1 # ob - 1018 1 3 -1.050000 7.334992116 -11.864369396 -5.909707262 0 1 1 # ob - 1019 1 3 -1.050000 6.466370632 -9.359092007 -6.147724732 0 1 1 # ob - 1020 1 5 0.425000 -9.200888137 -11.565707441 -8.008509666 1 1 1 # ho - 1021 1 1 1.575000 5.979898694 -10.644944847 9.189872068 0 1 0 # ao + 1001 1 1 1.575000 9.331097556 -16.252526266 -9.189871776 0 1 1 # ao + 1002 1 2 2.100000 -10.490708043 -8.863914582 -6.510105182 1 1 1 # st + 1003 1 2 2.100000 -10.454377223 -14.882798454 -6.503672300 1 1 1 # st + 1004 1 3 -1.050000 9.899794631 -17.754681294 -8.128441523 0 1 1 # ob + 1005 1 3 -1.050000 -10.300866385 -15.010297434 -8.125684583 1 1 1 # ob + 1006 1 4 -0.950000 7.739394301 -16.041760237 -8.196446632 0 1 1 # oh + 1007 1 3 -1.050000 6.414126212 -14.326475333 -5.891626593 0 1 1 # ob + 1008 1 3 -1.050000 -10.753175341 -16.347280707 -5.896221744 1 1 1 # ob + 1009 1 3 -1.050000 9.018203175 -13.842003318 -6.134239214 0 1 1 # ob + 1010 1 5 0.425000 8.830944558 -16.048618752 -7.995024148 0 1 1 # ho + 1011 1 1 1.575000 -8.700735139 -11.769614956 -9.189871776 1 1 1 # ao + 1012 1 2 2.100000 7.541124652 -13.346825892 -6.510105182 0 1 1 # st + 1013 1 2 2.100000 7.633790234 -10.399887143 -6.503672300 0 1 1 # st + 1014 1 3 -1.050000 7.347962088 -13.271769983 -8.128441523 0 1 1 # ob + 1015 1 3 -1.050000 7.787301072 -10.527386123 -8.125684583 0 1 1 # ob + 1016 1 4 -0.950000 -10.292438395 -11.558848927 -8.196446632 1 1 1 # oh + 1017 1 3 -1.050000 9.022293517 -9.843564022 -5.891626593 0 1 1 # ob + 1018 1 3 -1.050000 7.334992116 -11.864369396 -5.896221744 0 1 1 # ob + 1019 1 3 -1.050000 6.466370632 -9.359092007 -6.134239214 0 1 1 # ob + 1020 1 5 0.425000 -9.200888137 -11.565707441 -7.995024148 1 1 1 # ho + 1021 1 1 1.575000 9.373776442 -10.281288324 -9.189871776 0 1 1 # ao 1022 1 2 2.100000 5.161704293 -18.033556531 6.510105475 0 1 0 # st 1023 1 2 2.100000 5.125373474 -12.014672659 6.503672592 0 1 0 # st 1024 1 3 -1.050000 5.411201619 -9.142789819 8.128441816 0 1 0 # ob @@ -1066,7 +1066,7 @@ Atoms # full 1028 1 3 -1.050000 5.424171591 -10.550190406 5.896222036 0 1 0 # ob 1029 1 3 -1.050000 6.292793075 -13.055467795 6.134239507 0 1 0 # ob 1030 1 5 0.425000 6.480051692 -10.848852361 7.995024441 0 1 0 # ho - 1031 1 1 1.575000 3.371731389 -15.127856157 9.189872068 0 1 0 # ao + 1031 1 1 1.575000 6.765609137 -14.764199634 -9.189871776 0 1 1 # ao 1032 1 2 2.100000 -12.870128402 -13.550645221 6.510105475 1 1 0 # st 1033 1 2 2.100000 -12.962793984 -16.497583970 6.503672592 1 1 0 # st 1034 1 3 -1.050000 -12.676965838 -13.625701130 8.128441816 1 1 0 # ob @@ -1076,27 +1076,27 @@ Atoms # full 1038 1 3 -1.050000 -12.663995866 -15.033101717 5.896222036 1 1 0 # ob 1039 1 3 -1.050000 -11.795374382 -17.538379106 6.134239507 1 1 0 # ob 1040 1 5 0.425000 3.871884387 -15.331763672 7.995024441 0 1 0 # ho - 1041 1 1 1.575000 -9.542780344 -16.616182789 9.189872068 1 1 0 # ao - 1042 1 2 2.100000 -5.330708196 -8.863914582 -6.523590700 1 1 1 # st - 1043 1 2 2.100000 -5.294377376 -14.882798454 -6.517157818 1 1 1 # st - 1044 1 3 -1.050000 -5.580205521 -17.754681294 -8.141927041 1 1 1 # ob - 1045 1 3 -1.050000 -5.140866537 -15.010297434 -8.139170101 1 1 1 # ob - 1046 1 4 -0.950000 -7.740605852 -16.041760237 -8.209932150 1 1 1 # oh - 1047 1 3 -1.050000 -9.065873940 -14.326475333 -5.905112111 1 1 1 # ob - 1048 1 3 -1.050000 -5.593175493 -16.347280707 -5.909707262 1 1 1 # ob - 1049 1 3 -1.050000 -6.461796978 -13.842003318 -6.147724732 1 1 1 # ob - 1050 1 5 0.425000 -6.649055594 -16.048618752 -8.008509666 1 1 1 # ho - 1051 1 1 1.575000 -6.934613039 -12.133271479 9.189872068 1 1 0 # ao - 1052 1 2 2.100000 -7.938875500 -13.346825892 -6.523590700 1 1 1 # st - 1053 1 2 2.100000 -7.846209919 -10.399887143 -6.517157818 1 1 1 # st - 1054 1 3 -1.050000 -8.132038064 -13.271769983 -8.141927041 1 1 1 # ob - 1055 1 3 -1.050000 -7.692699080 -10.527386123 -8.139170101 1 1 1 # ob - 1056 1 4 -0.950000 -5.132438547 -11.558848927 -8.209932150 1 1 1 # oh - 1057 1 3 -1.050000 -6.457706636 -9.843564022 -5.905112111 1 1 1 # ob - 1058 1 3 -1.050000 -8.145008036 -11.864369396 -5.909707262 1 1 1 # ob - 1059 1 3 -1.050000 -9.013629520 -9.359092007 -6.147724732 1 1 1 # ob - 1060 1 5 0.425000 -4.040888290 -11.565707441 -8.008509666 1 1 1 # ho - 1061 1 1 1.575000 -9.500101459 -10.644944847 9.189872068 1 1 0 # ao + 1041 1 1 1.575000 -6.148902596 -16.252526266 -9.189871776 1 1 1 # ao + 1042 1 2 2.100000 -5.330708196 -8.863914582 -6.510105182 1 1 1 # st + 1043 1 2 2.100000 -5.294377376 -14.882798454 -6.503672300 1 1 1 # st + 1044 1 3 -1.050000 -5.580205521 -17.754681294 -8.128441523 1 1 1 # ob + 1045 1 3 -1.050000 -5.140866537 -15.010297434 -8.125684583 1 1 1 # ob + 1046 1 4 -0.950000 -7.740605852 -16.041760237 -8.196446632 1 1 1 # oh + 1047 1 3 -1.050000 -9.065873940 -14.326475333 -5.891626593 1 1 1 # ob + 1048 1 3 -1.050000 -5.593175493 -16.347280707 -5.896221744 1 1 1 # ob + 1049 1 3 -1.050000 -6.461796978 -13.842003318 -6.134239214 1 1 1 # ob + 1050 1 5 0.425000 -6.649055594 -16.048618752 -7.995024148 1 1 1 # ho + 1051 1 1 1.575000 -3.540735291 -11.769614956 -9.189871776 1 1 1 # ao + 1052 1 2 2.100000 -7.938875500 -13.346825892 -6.510105182 1 1 1 # st + 1053 1 2 2.100000 -7.846209919 -10.399887143 -6.503672300 1 1 1 # st + 1054 1 3 -1.050000 -8.132038064 -13.271769983 -8.128441523 1 1 1 # ob + 1055 1 3 -1.050000 -7.692699080 -10.527386123 -8.125684583 1 1 1 # ob + 1056 1 4 -0.950000 -5.132438547 -11.558848927 -8.196446632 1 1 1 # oh + 1057 1 3 -1.050000 -6.457706636 -9.843564022 -5.891626593 1 1 1 # ob + 1058 1 3 -1.050000 -8.145008036 -11.864369396 -5.896221744 1 1 1 # ob + 1059 1 3 -1.050000 -9.013629520 -9.359092007 -6.134239214 1 1 1 # ob + 1060 1 5 0.425000 -4.040888290 -11.565707441 -7.995024148 1 1 1 # ho + 1061 1 1 1.575000 -6.106223711 -10.281288324 -9.189871776 1 1 1 # ao 1062 1 2 2.100000 -10.318295859 -18.033556531 6.510105475 1 1 0 # st 1063 1 2 2.100000 -10.354626679 -12.014672659 6.503672592 1 1 0 # st 1064 1 3 -1.050000 -10.068798533 -9.142789819 8.128441816 1 1 0 # ob @@ -1106,7 +1106,7 @@ Atoms # full 1068 1 3 -1.050000 -10.055828562 -10.550190406 5.896222036 1 1 0 # ob 1069 1 3 -1.050000 -9.187207077 -13.055467795 6.134239507 1 1 0 # ob 1070 1 5 0.425000 -8.999948461 -10.848852361 7.995024441 1 1 0 # ho - 1071 1 1 1.575000 -12.108268763 -15.127856157 9.189872068 1 1 0 # ao + 1071 1 1 1.575000 -8.714391015 -14.764199634 -9.189871776 1 1 1 # ao 1072 1 2 2.100000 -7.710128554 -13.550645221 6.510105475 1 1 0 # st 1073 1 2 2.100000 -7.802794136 -16.497583970 6.503672592 1 1 0 # st 1074 1 3 -1.050000 -7.516965991 -13.625701130 8.128441816 1 1 0 # ob @@ -1116,27 +1116,27 @@ Atoms # full 1078 1 3 -1.050000 -7.503996019 -15.033101717 5.896222036 1 1 0 # ob 1079 1 3 -1.050000 -6.635374534 -17.538379106 6.134239507 1 1 0 # ob 1080 1 5 0.425000 -11.608115765 -15.331763672 7.995024441 1 1 0 # ho - 1081 1 1 1.575000 -4.382780497 -16.616182789 9.189872068 1 1 0 # ao - 1082 1 2 2.100000 -0.170708348 -8.863914582 -6.523590700 1 1 1 # st - 1083 1 2 2.100000 -0.134377529 -14.882798454 -6.517157818 1 1 1 # st - 1084 1 3 -1.050000 -0.420205674 -17.754681294 -8.141927041 1 1 1 # ob - 1085 1 3 -1.050000 0.019133310 -15.010297434 -8.139170101 1 1 1 # ob - 1086 1 4 -0.950000 -2.580606005 -16.041760237 -8.209932150 1 1 1 # oh - 1087 1 3 -1.050000 -3.905874093 -14.326475333 -5.905112111 1 1 1 # ob - 1088 1 3 -1.050000 -0.433175646 -16.347280707 -5.909707262 1 1 1 # ob - 1089 1 3 -1.050000 -1.301797130 -13.842003318 -6.147724732 1 1 1 # ob - 1090 1 5 0.425000 -1.489055747 -16.048618752 -8.008509666 1 1 1 # ho - 1091 1 1 1.575000 -1.774613192 -12.133271479 9.189872068 1 1 0 # ao - 1092 1 2 2.100000 -2.778875653 -13.346825892 -6.523590700 1 1 1 # st - 1093 1 2 2.100000 -2.686210071 -10.399887143 -6.517157818 1 1 1 # st - 1094 1 3 -1.050000 -2.972038217 -13.271769983 -8.141927041 1 1 1 # ob - 1095 1 3 -1.050000 -2.532699233 -10.527386123 -8.139170101 1 1 1 # ob - 1096 1 4 -0.950000 0.027561300 -11.558848927 -8.209932150 1 1 1 # oh - 1097 1 3 -1.050000 -1.297706788 -9.843564022 -5.905112111 1 1 1 # ob - 1098 1 3 -1.050000 -2.985008189 -11.864369396 -5.909707262 1 1 1 # ob - 1099 1 3 -1.050000 -3.853629673 -9.359092007 -6.147724732 1 1 1 # ob - 1100 1 5 0.425000 1.119111558 -11.565707441 -8.008509666 1 1 1 # ho - 1101 1 1 1.575000 -4.340101611 -10.644944847 9.189872068 1 1 0 # ao + 1081 1 1 1.575000 -0.988902749 -16.252526266 -9.189871776 1 1 1 # ao + 1082 1 2 2.100000 -0.170708348 -8.863914582 -6.510105182 1 1 1 # st + 1083 1 2 2.100000 -0.134377529 -14.882798454 -6.503672300 1 1 1 # st + 1084 1 3 -1.050000 -0.420205674 -17.754681294 -8.128441523 1 1 1 # ob + 1085 1 3 -1.050000 0.019133310 -15.010297434 -8.125684583 1 1 1 # ob + 1086 1 4 -0.950000 -2.580606005 -16.041760237 -8.196446632 1 1 1 # oh + 1087 1 3 -1.050000 -3.905874093 -14.326475333 -5.891626593 1 1 1 # ob + 1088 1 3 -1.050000 -0.433175646 -16.347280707 -5.896221744 1 1 1 # ob + 1089 1 3 -1.050000 -1.301797130 -13.842003318 -6.134239214 1 1 1 # ob + 1090 1 5 0.425000 -1.489055747 -16.048618752 -7.995024148 1 1 1 # ho + 1091 1 1 1.575000 1.619264556 -11.769614956 -9.189871776 1 1 1 # ao + 1092 1 2 2.100000 -2.778875653 -13.346825892 -6.510105182 1 1 1 # st + 1093 1 2 2.100000 -2.686210071 -10.399887143 -6.503672300 1 1 1 # st + 1094 1 3 -1.050000 -2.972038217 -13.271769983 -8.128441523 1 1 1 # ob + 1095 1 3 -1.050000 -2.532699233 -10.527386123 -8.125684583 1 1 1 # ob + 1096 1 4 -0.950000 0.027561300 -11.558848927 -8.196446632 1 1 1 # oh + 1097 1 3 -1.050000 -1.297706788 -9.843564022 -5.891626593 1 1 1 # ob + 1098 1 3 -1.050000 -2.985008189 -11.864369396 -5.896221744 1 1 1 # ob + 1099 1 3 -1.050000 -3.853629673 -9.359092007 -6.134239214 1 1 1 # ob + 1100 1 5 0.425000 1.119111558 -11.565707441 -7.995024148 1 1 1 # ho + 1101 1 1 1.575000 -0.946223863 -10.281288324 -9.189871776 1 1 1 # ao 1102 1 2 2.100000 -5.158296012 -18.033556531 6.510105475 1 1 0 # st 1103 1 2 2.100000 -5.194626832 -12.014672659 6.503672592 1 1 0 # st 1104 1 3 -1.050000 -4.908798686 -9.142789819 8.128441816 1 1 0 # ob @@ -1146,7 +1146,7 @@ Atoms # full 1108 1 3 -1.050000 -4.895828714 -10.550190406 5.896222036 1 1 0 # ob 1109 1 3 -1.050000 -4.027207230 -13.055467795 6.134239507 1 1 0 # ob 1110 1 5 0.425000 -3.839948613 -10.848852361 7.995024441 1 1 0 # ho - 1111 1 1 1.575000 -6.948268916 -15.127856157 9.189872068 1 1 0 # ao + 1111 1 1 1.575000 -3.554391168 -14.764199634 -9.189871776 1 1 1 # ao 1112 1 2 2.100000 -2.550128707 -13.550645221 6.510105475 1 1 0 # st 1113 1 2 2.100000 -2.642794289 -16.497583970 6.503672592 1 1 0 # st 1114 1 3 -1.050000 -2.356966143 -13.625701130 8.128441816 1 1 0 # ob @@ -1156,27 +1156,27 @@ Atoms # full 1118 1 3 -1.050000 -2.343996171 -15.033101717 5.896222036 1 1 0 # ob 1119 1 3 -1.050000 -1.475374687 -17.538379106 6.134239507 1 1 0 # ob 1120 1 5 0.425000 -6.448115918 -15.331763672 7.995024441 1 1 0 # ho - 1121 1 1 1.575000 0.833554723 -7.650360168 9.189872068 0 1 0 # ao - 1122 1 2 2.100000 5.045626871 0.101908040 -6.523590700 0 1 1 # st - 1123 1 2 2.100000 5.081957691 -5.916975832 -6.517157818 0 1 1 # st - 1124 1 3 -1.050000 4.796129546 -8.788858673 -8.141927041 0 1 1 # ob - 1125 1 3 -1.050000 5.235468530 -6.044474812 -8.139170101 0 1 1 # ob - 1126 1 4 -0.950000 2.635729215 -7.075937616 -8.209932150 0 1 1 # oh - 1127 1 3 -1.050000 1.310461127 -5.360652711 -5.905112111 0 1 1 # ob - 1128 1 3 -1.050000 4.783159574 -7.381458085 -5.909707262 0 1 1 # ob - 1129 1 3 -1.050000 3.914538089 -4.876180697 -6.147724732 0 1 1 # ob - 1130 1 5 0.425000 3.727279473 -7.082796130 -8.008509666 0 1 1 # ho - 1131 1 1 1.575000 3.441722028 -3.167448857 9.189872068 0 1 0 # ao - 1132 1 2 2.100000 2.437459567 -4.381003271 -6.523590700 0 1 1 # st - 1133 1 2 2.100000 2.530125148 -1.434064521 -6.517157818 0 1 1 # st - 1134 1 3 -1.050000 2.244297003 -4.305947362 -8.141927041 0 1 1 # ob - 1135 1 3 -1.050000 2.683635987 -1.561563502 -8.139170101 0 1 1 # ob - 1136 1 4 -0.950000 5.243896520 -2.593026305 -8.209932150 0 1 1 # oh - 1137 1 3 -1.050000 3.918628431 -0.877741400 -5.905112111 0 1 1 # ob - 1138 1 3 -1.050000 2.231327031 -2.898546774 -5.909707262 0 1 1 # ob - 1139 1 3 -1.050000 1.362705547 -0.393269386 -6.147724732 0 1 1 # ob - 1140 1 5 0.425000 6.335446777 -2.599884819 -8.008509666 0 1 1 # ho - 1141 1 1 1.575000 0.876233608 -1.679122225 9.189872068 0 1 0 # ao + 1121 1 1 1.575000 4.227432471 -7.286703645 -9.189871776 0 1 1 # ao + 1122 1 2 2.100000 5.045626871 0.101908040 -6.510105182 0 1 1 # st + 1123 1 2 2.100000 5.081957691 -5.916975832 -6.503672300 0 1 1 # st + 1124 1 3 -1.050000 4.796129546 -8.788858673 -8.128441523 0 1 1 # ob + 1125 1 3 -1.050000 5.235468530 -6.044474812 -8.125684583 0 1 1 # ob + 1126 1 4 -0.950000 2.635729215 -7.075937616 -8.196446632 0 1 1 # oh + 1127 1 3 -1.050000 1.310461127 -5.360652711 -5.891626593 0 1 1 # ob + 1128 1 3 -1.050000 4.783159574 -7.381458085 -5.896221744 0 1 1 # ob + 1129 1 3 -1.050000 3.914538089 -4.876180697 -6.134239214 0 1 1 # ob + 1130 1 5 0.425000 3.727279473 -7.082796130 -7.995024148 0 1 1 # ho + 1131 1 1 1.575000 6.835599776 -2.803792334 -9.189871776 0 1 1 # ao + 1132 1 2 2.100000 2.437459567 -4.381003271 -6.510105182 0 1 1 # st + 1133 1 2 2.100000 2.530125148 -1.434064521 -6.503672300 0 1 1 # st + 1134 1 3 -1.050000 2.244297003 -4.305947362 -8.128441523 0 1 1 # ob + 1135 1 3 -1.050000 2.683635987 -1.561563502 -8.125684583 0 1 1 # ob + 1136 1 4 -0.950000 5.243896520 -2.593026305 -8.196446632 0 1 1 # oh + 1137 1 3 -1.050000 3.918628431 -0.877741400 -5.891626593 0 1 1 # ob + 1138 1 3 -1.050000 2.231327031 -2.898546774 -5.896221744 0 1 1 # ob + 1139 1 3 -1.050000 1.362705547 -0.393269386 -6.134239214 0 1 1 # ob + 1140 1 5 0.425000 6.335446777 -2.599884819 -7.995024148 0 1 1 # ho + 1141 1 1 1.575000 4.270111356 -1.315465702 -9.189871776 0 1 1 # ao 1142 1 2 2.100000 0.058039208 -9.067733910 6.510105475 0 1 0 # st 1143 1 2 2.100000 0.021708388 -3.048850038 6.503672592 0 1 0 # st 1144 1 3 -1.050000 0.307536534 -0.176967197 8.128441816 0 1 0 # ob @@ -1186,7 +1186,7 @@ Atoms # full 1148 1 3 -1.050000 0.320506505 -1.584367785 5.896222036 0 1 0 # ob 1149 1 3 -1.050000 1.189127990 -4.089645173 6.134239507 0 1 0 # ob 1150 1 5 0.425000 1.376386606 -1.883029740 7.995024441 0 1 0 # ho - 1151 1 1 1.575000 -1.731933696 -6.162033536 9.189872068 0 1 0 # ao + 1151 1 1 1.575000 1.661944052 -5.798377013 -9.189871776 0 1 1 # ao 1152 1 2 2.100000 2.666206512 -4.584822599 6.510105475 0 1 0 # st 1153 1 2 2.100000 2.573540931 -7.531761349 6.503672592 0 1 0 # st 1154 1 3 -1.050000 2.859369076 -4.659878508 8.128441816 0 1 0 # ob @@ -1196,27 +1196,27 @@ Atoms # full 1158 1 3 -1.050000 2.872339048 -6.067279095 5.896222036 0 1 0 # ob 1159 1 3 -1.050000 3.740960532 -8.572556484 6.134239507 0 1 0 # ob 1160 1 5 0.425000 -1.231780698 -6.365941050 7.995024441 0 1 0 # ho - 1161 1 1 1.575000 5.993554570 -7.650360168 9.189872068 0 1 0 # ao - 1162 1 2 2.100000 -10.434373281 0.101908040 -6.523590700 1 1 1 # st - 1163 1 2 2.100000 -10.398042462 -5.916975832 -6.517157818 1 1 1 # st - 1164 1 3 -1.050000 9.956129393 -8.788858673 -8.141927041 0 1 1 # ob - 1165 1 3 -1.050000 -10.244531623 -6.044474812 -8.139170101 1 1 1 # ob - 1166 1 4 -0.950000 7.795729062 -7.075937616 -8.209932150 0 1 1 # oh - 1167 1 3 -1.050000 6.470460974 -5.360652711 -5.905112111 0 1 1 # ob - 1168 1 3 -1.050000 -10.696840579 -7.381458085 -5.909707262 1 1 1 # ob - 1169 1 3 -1.050000 9.074537937 -4.876180697 -6.147724732 0 1 1 # ob - 1170 1 5 0.425000 8.887279320 -7.082796130 -8.008509666 0 1 1 # ho - 1171 1 1 1.575000 -12.038278125 -3.167448857 9.189872068 1 1 0 # ao - 1172 1 2 2.100000 7.597459414 -4.381003271 -6.523590700 0 1 1 # st - 1173 1 2 2.100000 7.690124996 -1.434064521 -6.517157818 0 1 1 # st - 1174 1 3 -1.050000 7.404296850 -4.305947362 -8.141927041 0 1 1 # ob - 1175 1 3 -1.050000 7.843635834 -1.561563502 -8.139170101 0 1 1 # ob - 1176 1 4 -0.950000 -10.236103633 -2.593026305 -8.209932150 1 1 1 # oh - 1177 1 3 -1.050000 9.078628279 -0.877741400 -5.905112111 0 1 1 # ob - 1178 1 3 -1.050000 7.391326878 -2.898546774 -5.909707262 0 1 1 # ob - 1179 1 3 -1.050000 6.522705394 -0.393269386 -6.147724732 0 1 1 # ob - 1180 1 5 0.425000 -9.144553375 -2.599884819 -8.008509666 1 1 1 # ho - 1181 1 1 1.575000 6.036233456 -1.679122225 9.189872068 0 1 0 # ao + 1161 1 1 1.575000 9.387432318 -7.286703645 -9.189871776 0 1 1 # ao + 1162 1 2 2.100000 -10.434373281 0.101908040 -6.510105182 1 1 1 # st + 1163 1 2 2.100000 -10.398042462 -5.916975832 -6.503672300 1 1 1 # st + 1164 1 3 -1.050000 9.956129393 -8.788858673 -8.128441523 0 1 1 # ob + 1165 1 3 -1.050000 -10.244531623 -6.044474812 -8.125684583 1 1 1 # ob + 1166 1 4 -0.950000 7.795729062 -7.075937616 -8.196446632 0 1 1 # oh + 1167 1 3 -1.050000 6.470460974 -5.360652711 -5.891626593 0 1 1 # ob + 1168 1 3 -1.050000 -10.696840579 -7.381458085 -5.896221744 1 1 1 # ob + 1169 1 3 -1.050000 9.074537937 -4.876180697 -6.134239214 0 1 1 # ob + 1170 1 5 0.425000 8.887279320 -7.082796130 -7.995024148 0 1 1 # ho + 1171 1 1 1.575000 -8.644400377 -2.803792334 -9.189871776 1 1 1 # ao + 1172 1 2 2.100000 7.597459414 -4.381003271 -6.510105182 0 1 1 # st + 1173 1 2 2.100000 7.690124996 -1.434064521 -6.503672300 0 1 1 # st + 1174 1 3 -1.050000 7.404296850 -4.305947362 -8.128441523 0 1 1 # ob + 1175 1 3 -1.050000 7.843635834 -1.561563502 -8.125684583 0 1 1 # ob + 1176 1 4 -0.950000 -10.236103633 -2.593026305 -8.196446632 1 1 1 # oh + 1177 1 3 -1.050000 9.078628279 -0.877741400 -5.891626593 0 1 1 # ob + 1178 1 3 -1.050000 7.391326878 -2.898546774 -5.896221744 0 1 1 # ob + 1179 1 3 -1.050000 6.522705394 -0.393269386 -6.134239214 0 1 1 # ob + 1180 1 5 0.425000 -9.144553375 -2.599884819 -7.995024148 1 1 1 # ho + 1181 1 1 1.575000 9.430111204 -1.315465702 -9.189871776 0 1 1 # ao 1182 1 2 2.100000 5.218039055 -9.067733910 6.510105475 0 1 0 # st 1183 1 2 2.100000 5.181708235 -3.048850038 6.503672592 0 1 0 # st 1184 1 3 -1.050000 5.467536381 -0.176967197 8.128441816 0 1 0 # ob @@ -1226,7 +1226,7 @@ Atoms # full 1188 1 3 -1.050000 5.480506353 -1.584367785 5.896222036 0 1 0 # ob 1189 1 3 -1.050000 6.349127837 -4.089645173 6.134239507 0 1 0 # ob 1190 1 5 0.425000 6.536386454 -1.883029740 7.995024441 0 1 0 # ho - 1191 1 1 1.575000 3.428066151 -6.162033536 9.189872068 0 1 0 # ao + 1191 1 1 1.575000 6.821943899 -5.798377013 -9.189871776 0 1 1 # ao 1192 1 2 2.100000 -12.813793640 -4.584822599 6.510105475 1 1 0 # st 1193 1 2 2.100000 -12.906459222 -7.531761349 6.503672592 1 1 0 # st 1194 1 3 -1.050000 -12.620631076 -4.659878508 8.128441816 1 1 0 # ob @@ -1236,27 +1236,27 @@ Atoms # full 1198 1 3 -1.050000 -12.607661104 -6.067279095 5.896222036 1 1 0 # ob 1199 1 3 -1.050000 -11.739039620 -8.572556484 6.134239507 1 1 0 # ob 1200 1 5 0.425000 3.928219149 -6.365941050 7.995024441 0 1 0 # ho - 1201 1 1 1.575000 -9.486445582 -7.650360168 9.189872068 1 1 0 # ao - 1202 1 2 2.100000 -5.274373434 0.101908040 -6.523590700 1 1 1 # st - 1203 1 2 2.100000 -5.238042614 -5.916975832 -6.517157818 1 1 1 # st - 1204 1 3 -1.050000 -5.523870760 -8.788858673 -8.141927041 1 1 1 # ob - 1205 1 3 -1.050000 -5.084531776 -6.044474812 -8.139170101 1 1 1 # ob - 1206 1 4 -0.950000 -7.684271090 -7.075937616 -8.209932150 1 1 1 # oh - 1207 1 3 -1.050000 -9.009539178 -5.360652711 -5.905112111 1 1 1 # ob - 1208 1 3 -1.050000 -5.536840731 -7.381458085 -5.909707262 1 1 1 # ob - 1209 1 3 -1.050000 -6.405462216 -4.876180697 -6.147724732 1 1 1 # ob - 1210 1 5 0.425000 -6.592720833 -7.082796130 -8.008509666 1 1 1 # ho - 1211 1 1 1.575000 -6.878278278 -3.167448857 9.189872068 1 1 0 # ao - 1212 1 2 2.100000 -7.882540739 -4.381003271 -6.523590700 1 1 1 # st - 1213 1 2 2.100000 -7.789875157 -1.434064521 -6.517157818 1 1 1 # st - 1214 1 3 -1.050000 -8.075703302 -4.305947362 -8.141927041 1 1 1 # ob - 1215 1 3 -1.050000 -7.636364318 -1.561563502 -8.139170101 1 1 1 # ob - 1216 1 4 -0.950000 -5.076103786 -2.593026305 -8.209932150 1 1 1 # oh - 1217 1 3 -1.050000 -6.401371874 -0.877741400 -5.905112111 1 1 1 # ob - 1218 1 3 -1.050000 -8.088673274 -2.898546774 -5.909707262 1 1 1 # ob - 1219 1 3 -1.050000 -8.957294759 -0.393269386 -6.147724732 1 1 1 # ob - 1220 1 5 0.425000 -3.984553528 -2.599884819 -8.008509666 1 1 1 # ho - 1221 1 1 1.575000 -9.443766697 -1.679122225 9.189872068 1 1 0 # ao + 1201 1 1 1.575000 -6.092567834 -7.286703645 -9.189871776 1 1 1 # ao + 1202 1 2 2.100000 -5.274373434 0.101908040 -6.510105182 1 1 1 # st + 1203 1 2 2.100000 -5.238042614 -5.916975832 -6.503672300 1 1 1 # st + 1204 1 3 -1.050000 -5.523870760 -8.788858673 -8.128441523 1 1 1 # ob + 1205 1 3 -1.050000 -5.084531776 -6.044474812 -8.125684583 1 1 1 # ob + 1206 1 4 -0.950000 -7.684271090 -7.075937616 -8.196446632 1 1 1 # oh + 1207 1 3 -1.050000 -9.009539178 -5.360652711 -5.891626593 1 1 1 # ob + 1208 1 3 -1.050000 -5.536840731 -7.381458085 -5.896221744 1 1 1 # ob + 1209 1 3 -1.050000 -6.405462216 -4.876180697 -6.134239214 1 1 1 # ob + 1210 1 5 0.425000 -6.592720833 -7.082796130 -7.995024148 1 1 1 # ho + 1211 1 1 1.575000 -3.484400530 -2.803792334 -9.189871776 1 1 1 # ao + 1212 1 2 2.100000 -7.882540739 -4.381003271 -6.510105182 1 1 1 # st + 1213 1 2 2.100000 -7.789875157 -1.434064521 -6.503672300 1 1 1 # st + 1214 1 3 -1.050000 -8.075703302 -4.305947362 -8.128441523 1 1 1 # ob + 1215 1 3 -1.050000 -7.636364318 -1.561563502 -8.125684583 1 1 1 # ob + 1216 1 4 -0.950000 -5.076103786 -2.593026305 -8.196446632 1 1 1 # oh + 1217 1 3 -1.050000 -6.401371874 -0.877741400 -5.891626593 1 1 1 # ob + 1218 1 3 -1.050000 -8.088673274 -2.898546774 -5.896221744 1 1 1 # ob + 1219 1 3 -1.050000 -8.957294759 -0.393269386 -6.134239214 1 1 1 # ob + 1220 1 5 0.425000 -3.984553528 -2.599884819 -7.995024148 1 1 1 # ho + 1221 1 1 1.575000 -6.049888949 -1.315465702 -9.189871776 1 1 1 # ao 1222 1 2 2.100000 -10.261961097 -9.067733910 6.510105475 1 1 0 # st 1223 1 2 2.100000 -10.298291917 -3.048850038 6.503672592 1 1 0 # st 1224 1 3 -1.050000 -10.012463772 -0.176967197 8.128441816 1 1 0 # ob @@ -1266,7 +1266,7 @@ Atoms # full 1228 1 3 -1.050000 -9.999493800 -1.584367785 5.896222036 1 1 0 # ob 1229 1 3 -1.050000 -9.130872315 -4.089645173 6.134239507 1 1 0 # ob 1230 1 5 0.425000 -8.943613699 -1.883029740 7.995024441 1 1 0 # ho - 1231 1 1 1.575000 -12.051934002 -6.162033536 9.189872068 1 1 0 # ao + 1231 1 1 1.575000 -8.658056254 -5.798377013 -9.189871776 1 1 1 # ao 1232 1 2 2.100000 -7.653793793 -4.584822599 6.510105475 1 1 0 # st 1233 1 2 2.100000 -7.746459374 -7.531761349 6.503672592 1 1 0 # st 1234 1 3 -1.050000 -7.460631229 -4.659878508 8.128441816 1 1 0 # ob @@ -1276,27 +1276,27 @@ Atoms # full 1238 1 3 -1.050000 -7.447661257 -6.067279095 5.896222036 1 1 0 # ob 1239 1 3 -1.050000 -6.579039773 -8.572556484 6.134239507 1 1 0 # ob 1240 1 5 0.425000 -11.551781003 -6.365941050 7.995024441 1 1 0 # ho - 1241 1 1 1.575000 -4.326445735 -7.650360168 9.189872068 1 1 0 # ao - 1242 1 2 2.100000 -0.114373587 0.101908040 -6.523590700 1 1 1 # st - 1243 1 2 2.100000 -0.078042767 -5.916975832 -6.517157818 1 1 1 # st - 1244 1 3 -1.050000 -0.363870912 -8.788858673 -8.141927041 1 1 1 # ob - 1245 1 3 -1.050000 0.075468072 -6.044474812 -8.139170101 1 1 1 # ob - 1246 1 4 -0.950000 -2.524271243 -7.075937616 -8.209932150 1 1 1 # oh - 1247 1 3 -1.050000 -3.849539331 -5.360652711 -5.905112111 1 1 1 # ob - 1248 1 3 -1.050000 -0.376840884 -7.381458085 -5.909707262 1 1 1 # ob - 1249 1 3 -1.050000 -1.245462368 -4.876180697 -6.147724732 1 1 1 # ob - 1250 1 5 0.425000 -1.432720985 -7.082796130 -8.008509666 1 1 1 # ho - 1251 1 1 1.575000 -1.718278430 -3.167448857 9.189872068 1 1 0 # ao - 1252 1 2 2.100000 -2.722540891 -4.381003271 -6.523590700 1 1 1 # st - 1253 1 2 2.100000 -2.629875310 -1.434064521 -6.517157818 1 1 1 # st - 1254 1 3 -1.050000 -2.915703455 -4.305947362 -8.141927041 1 1 1 # ob - 1255 1 3 -1.050000 -2.476364471 -1.561563502 -8.139170101 1 1 1 # ob - 1256 1 4 -0.950000 0.083896062 -2.593026305 -8.209932150 1 1 1 # oh - 1257 1 3 -1.050000 -1.241372026 -0.877741400 -5.905112111 1 1 1 # ob - 1258 1 3 -1.050000 -2.928673427 -2.898546774 -5.909707262 1 1 1 # ob - 1259 1 3 -1.050000 -3.797294911 -0.393269386 -6.147724732 1 1 1 # ob - 1260 1 5 0.425000 1.175446320 -2.599884819 -8.008509666 1 1 1 # ho - 1261 1 1 1.575000 -4.283766850 -1.679122225 9.189872068 1 1 0 # ao + 1241 1 1 1.575000 -0.932567987 -7.286703645 -9.189871776 1 1 1 # ao + 1242 1 2 2.100000 -0.114373587 0.101908040 -6.510105182 1 1 1 # st + 1243 1 2 2.100000 -0.078042767 -5.916975832 -6.503672300 1 1 1 # st + 1244 1 3 -1.050000 -0.363870912 -8.788858673 -8.128441523 1 1 1 # ob + 1245 1 3 -1.050000 0.075468072 -6.044474812 -8.125684583 1 1 1 # ob + 1246 1 4 -0.950000 -2.524271243 -7.075937616 -8.196446632 1 1 1 # oh + 1247 1 3 -1.050000 -3.849539331 -5.360652711 -5.891626593 1 1 1 # ob + 1248 1 3 -1.050000 -0.376840884 -7.381458085 -5.896221744 1 1 1 # ob + 1249 1 3 -1.050000 -1.245462368 -4.876180697 -6.134239214 1 1 1 # ob + 1250 1 5 0.425000 -1.432720985 -7.082796130 -7.995024148 1 1 1 # ho + 1251 1 1 1.575000 1.675599318 -2.803792334 -9.189871776 1 1 1 # ao + 1252 1 2 2.100000 -2.722540891 -4.381003271 -6.510105182 1 1 1 # st + 1253 1 2 2.100000 -2.629875310 -1.434064521 -6.503672300 1 1 1 # st + 1254 1 3 -1.050000 -2.915703455 -4.305947362 -8.128441523 1 1 1 # ob + 1255 1 3 -1.050000 -2.476364471 -1.561563502 -8.125684583 1 1 1 # ob + 1256 1 4 -0.950000 0.083896062 -2.593026305 -8.196446632 1 1 1 # oh + 1257 1 3 -1.050000 -1.241372026 -0.877741400 -5.891626593 1 1 1 # ob + 1258 1 3 -1.050000 -2.928673427 -2.898546774 -5.896221744 1 1 1 # ob + 1259 1 3 -1.050000 -3.797294911 -0.393269386 -6.134239214 1 1 1 # ob + 1260 1 5 0.425000 1.175446320 -2.599884819 -7.995024148 1 1 1 # ho + 1261 1 1 1.575000 -0.889889102 -1.315465702 -9.189871776 1 1 1 # ao 1262 1 2 2.100000 -5.101961250 -9.067733910 6.510105475 1 1 0 # st 1263 1 2 2.100000 -5.138292070 -3.048850038 6.503672592 1 1 0 # st 1264 1 3 -1.050000 -4.852463924 -0.176967197 8.128441816 1 1 0 # ob @@ -1306,7 +1306,7 @@ Atoms # full 1268 1 3 -1.050000 -4.839493952 -1.584367785 5.896222036 1 1 0 # ob 1269 1 3 -1.050000 -3.970872468 -4.089645173 6.134239507 1 1 0 # ob 1270 1 5 0.425000 -3.783613851 -1.883029740 7.995024441 1 1 0 # ho - 1271 1 1 1.575000 -6.891934154 -6.162033536 9.189872068 1 1 0 # ao + 1271 1 1 1.575000 -3.498056406 -5.798377013 -9.189871776 1 1 1 # ao 1272 1 2 2.100000 -2.493793945 -4.584822599 6.510105475 1 1 0 # st 1273 1 2 2.100000 -2.586459527 -7.531761349 6.503672592 1 1 0 # st 1274 1 3 -1.050000 -2.300631381 -4.659878508 8.128441816 1 1 0 # ob diff --git a/tools/msi2lmp/test/reference/PyAC_bulk-clayff.data2 b/tools/msi2lmp/test/reference/PyAC_bulk-clayff.data2 index 4ee3c34e08..c37bbbfb6e 100644 --- a/tools/msi2lmp/test/reference/PyAC_bulk-clayff.data2 +++ b/tools/msi2lmp/test/reference/PyAC_bulk-clayff.data2 @@ -1,18 +1,18 @@ -LAMMPS data file via write_data, version 5 Oct 2016, timestep = 97 +LAMMPS data file via write_data, version 29 Aug 2024, timestep = 94, units = real 1280 atoms 5 atom types 128 bonds 1 bond types --1.0320000000000000e+01 1.0320000000000000e+01 xlo xhi --1.7931646038000000e+01 1.7931646038000000e+01 ylo yhi --9.1966146809999998e+00 9.1966146809999998e+00 zlo zhi -2.2533867499999999e-01 -3.3938777480000000e+00 -3.6365652300000001e-01 xy xz yz +-10.32 10.32 xlo xhi +-17.931646038 17.931646038 ylo yhi +-9.189871922 9.189871922 zlo zhi +0.225338675 -3.393877748 -0.363656523 xy xz yz Masses -1 26.9815 +1 26.98154 2 28.0855 3 15.9994 4 15.9994 @@ -32,2697 +32,2697 @@ Bond Coeffs # harmonic Atoms # full -1 1 1 1.5750000000000000e+00 2.5999859887541152e+00 1.4895295600930361e+00 -3.2684095454396100e-03 0 0 0 -2 1 2 2.1000000000000001e+00 3.3890158510276365e+00 8.8633001207774171e+00 2.7539653683351801e+00 0 0 0 -3 1 2 2.1000000000000001e+00 3.4122910819528869e+00 2.8524994338649421e+00 2.7618119426027334e+00 0 0 0 -4 1 3 -1.0500000000000000e+00 3.1917106615190871e+00 -3.1338269933883112e-02 1.1357135267887219e+00 0 0 0 -5 1 3 -1.0500000000000000e+00 3.5469715140592335e+00 2.7595296221569008e+00 1.1370155152705124e+00 0 0 0 -6 1 4 -9.4999999999999996e-01 9.0629865585113478e-01 1.5632893985929819e+00 1.0069802353213007e+00 0 0 0 -7 1 3 -1.0500000000000000e+00 -4.4288757751145802e-01 3.5553420116799757e+00 3.2357631024135358e+00 0 0 0 -8 1 3 -1.0500000000000000e+00 3.3001891233613350e+00 1.3781620274833877e+00 3.2374587487515178e+00 0 0 0 -9 1 3 -1.0500000000000000e+00 2.1467877066985572e+00 3.7041759320173213e+00 3.0459627186065976e+00 0 0 0 -10 1 5 4.2499999999999999e-01 3.9940955153589464e-01 6.8440286676932871e-01 1.1507107600825695e+00 0 0 0 -11 1 1 1.5750000000000000e+00 5.2081610166521486e+00 5.9724603411204953e+00 -3.2678269427819373e-03 0 0 0 -12 1 2 2.1000000000000001e+00 7.8082390717283801e-01 4.3803873151640857e+00 2.7539573196414509e+00 0 0 0 -13 1 2 2.1000000000000001e+00 8.6049184758384811e-01 7.3353954630155727e+00 2.7618077229661608e+00 0 0 0 -14 1 3 -1.0500000000000000e+00 6.3985291874622341e-01 4.4515561398457919e+00 1.1357491950576897e+00 0 0 0 -15 1 3 -1.0500000000000000e+00 9.9516171983727020e-01 7.2424161873666044e+00 1.1369982112775414e+00 0 0 0 -16 1 4 -9.4999999999999996e-01 3.5145105035101025e+00 6.0462386370433165e+00 1.0069819170376704e+00 0 0 0 -17 1 3 -1.0500000000000000e+00 2.1653116942489294e+00 8.0382453753562579e+00 3.2357864576820852e+00 0 0 0 -18 1 3 -1.0500000000000000e+00 7.4835045014725310e-01 5.8610807683013810e+00 3.2374554227766854e+00 0 0 0 -19 1 3 -1.0500000000000000e+00 -4.0504297107989196e-01 8.1871235044640613e+00 3.0459446104666839e+00 0 0 0 -20 1 5 4.2499999999999999e-01 3.0075427813219715e+00 5.1673094182610200e+00 1.1507483385791737e+00 0 0 0 -21 1 1 1.5750000000000000e+00 2.6161048042733217e+00 7.4761708036165011e+00 -3.7559789002870048e-03 0 0 0 -22 1 2 2.1000000000000001e+00 1.8270583837568388e+00 1.0238224436981014e-01 -2.7609433636234240e+00 0 0 0 -23 1 2 2.1000000000000001e+00 1.8037926928806804e+00 6.1131912799914794e+00 -2.7688607211148550e+00 0 0 0 -24 1 3 -1.0500000000000000e+00 2.0245843840439779e+00 8.9971802013490674e+00 -1.1427990238002010e+00 0 0 0 -25 1 3 -1.0500000000000000e+00 1.6690794936693152e+00 6.2059206004470688e+00 -1.1439630092406396e+00 0 0 0 -26 1 4 -9.4999999999999996e-01 4.3099221099139733e+00 7.4024984100470839e+00 -1.0139954105990832e+00 0 0 0 -27 1 3 -1.0500000000000000e+00 5.6590031416114179e+00 5.4102611437460766e+00 -3.2425548174288927e+00 0 0 0 -28 1 3 -1.0500000000000000e+00 1.9161020068286181e+00 7.5874974627099085e+00 -3.2446533398602568e+00 0 0 0 -29 1 3 -1.0500000000000000e+00 3.0692592992822245e+00 5.2614642342074163e+00 -3.0529614390734992e+00 0 0 0 -30 1 5 4.2499999999999999e-01 4.8166943688168953e+00 8.2812214959912609e+00 -1.1575736167124671e+00 0 0 0 -31 1 1 1.5750000000000000e+00 7.9432589838539513e-03 2.9932322017580155e+00 -3.7548310874804258e-03 0 0 0 -32 1 2 2.1000000000000001e+00 4.4352567674819241e+00 4.5852778048151954e+00 -2.7609429803808379e+00 0 0 0 -33 1 2 2.1000000000000001e+00 4.3556162421312727e+00 1.6303072110574242e+00 -2.7688546110084076e+00 0 0 0 -34 1 3 -1.0500000000000000e+00 4.5764329813965201e+00 4.5142930965604897e+00 -1.1428117296192752e+00 0 0 0 -35 1 3 -1.0500000000000000e+00 4.2208908922051336e+00 1.7230220252737531e+00 -1.1439381753911206e+00 0 0 0 -36 1 4 -9.4999999999999996e-01 1.7016840076240793e+00 2.9195149987798672e+00 -1.0140049080257008e+00 0 0 0 -37 1 3 -1.0500000000000000e+00 3.0508094436907065e+00 9.2735955111261248e-01 -3.2425715481628181e+00 0 0 0 -38 1 3 -1.0500000000000000e+00 4.4679443003019212e+00 3.1046094096202950e+00 -3.2446642002284571e+00 0 0 0 -39 1 3 -1.0500000000000000e+00 5.6210980414275298e+00 7.7850985095940217e-01 -3.0529442198684507e+00 0 0 0 -40 1 5 4.2499999999999999e-01 2.2085508398324158e+00 3.7983231087392824e+00 -1.1577011829365311e+00 0 0 0 -41 1 1 1.5750000000000000e+00 7.7600001218919523e+00 1.4895196291109798e+00 -3.2657864259189040e-03 0 0 0 -42 1 2 2.1000000000000001e+00 -1.2091006019115589e+01 8.8632974450891417e+00 2.7539547041448795e+00 1 0 0 -43 1 2 2.1000000000000001e+00 -1.2067713232602157e+01 2.8525136078719164e+00 2.7618235199017409e+00 1 0 0 -44 1 3 -1.0500000000000000e+00 8.3516959569061555e+00 -3.1339532310223461e-02 1.1357433570971924e+00 0 0 0 -45 1 3 -1.0500000000000000e+00 -1.1933025492148120e+01 2.7595329973670104e+00 1.1370134112483044e+00 1 0 0 -46 1 4 -9.4999999999999996e-01 6.0662672751172373e+00 1.5632462792111497e+00 1.0069655351602584e+00 0 0 0 -47 1 3 -1.0500000000000000e+00 4.7171183489904180e+00 3.5553451756140646e+00 3.2357682722939334e+00 0 0 0 -48 1 3 -1.0500000000000000e+00 -1.2179814839759992e+01 1.3781454091287202e+00 3.2374743626679336e+00 1 0 0 -49 1 3 -1.0500000000000000e+00 7.3067998243476922e+00 3.7041714141887212e+00 3.0459588478344077e+00 0 0 0 -50 1 5 4.2499999999999999e-01 5.5593955964411492e+00 6.8439387532284712e-01 1.1505669005138230e+00 0 0 0 -51 1 1 1.5750000000000000e+00 -1.0271831701454774e+01 5.9724606597474157e+00 -3.2705090813571758e-03 1 0 0 -52 1 2 2.1000000000000001e+00 5.9408180554345194e+00 4.3804006042748469e+00 2.7539659364818760e+00 0 0 0 -53 1 2 2.1000000000000001e+00 6.0204684195903440e+00 7.3353880878252973e+00 2.7618080745736791e+00 0 0 0 -54 1 3 -1.0500000000000000e+00 5.7998593359670885e+00 4.4515526262705585e+00 1.1357348262919498e+00 0 0 0 -55 1 3 -1.0500000000000000e+00 6.1551595276671165e+00 7.2424262028151780e+00 1.1369916832629752e+00 0 0 0 -56 1 4 -9.4999999999999996e-01 -1.1965495382161963e+01 6.0462304220183150e+00 1.0070022736298210e+00 1 0 0 -57 1 3 -1.0500000000000000e+00 7.3253562399943561e+00 8.0382687380075382e+00 3.2357743586474470e+00 0 0 0 -58 1 3 -1.0500000000000000e+00 5.9083487186442269e+00 5.8610518812341148e+00 3.2374617422859089e+00 0 0 0 -59 1 3 -1.0500000000000000e+00 4.7549501783979871e+00 8.1871270499566329e+00 3.0459460044387185e+00 0 0 0 -60 1 5 4.2499999999999999e-01 8.1675777506518017e+00 5.1673452186951785e+00 1.1508566812238072e+00 0 0 0 -61 1 1 1.5750000000000000e+00 7.7761138336528575e+00 7.4761697781487406e+00 -3.7580555383147640e-03 0 0 0 -62 1 2 2.1000000000000001e+00 6.9870625419908770e+00 1.0236857881965378e-01 -2.7609530688361730e+00 0 0 0 -63 1 2 2.1000000000000001e+00 6.9638163429426783e+00 6.1131999232440997e+00 -2.7688597574900182e+00 0 0 0 -64 1 3 -1.0500000000000000e+00 7.1845768042068343e+00 8.9971837243373898e+00 -1.1427811518142867e+00 0 0 0 -65 1 3 -1.0500000000000000e+00 6.8290823812761374e+00 6.2059099045834571e+00 -1.1439562764296625e+00 0 0 0 -66 1 4 -9.4999999999999996e-01 -1.1170087733372744e+01 7.4024835932118371e+00 -1.0139778639834489e+00 1 0 0 -67 1 3 -1.0500000000000000e+00 -9.8209500322774037e+00 5.4102853287564159e+00 -3.2425657088525419e+00 1 0 0 -68 1 3 -1.0500000000000000e+00 7.0761041543125955e+00 7.5875255489076068e+00 -3.2446577662945764e+00 0 0 0 -69 1 3 -1.0500000000000000e+00 8.2292664263953057e+00 5.2614600888460927e+00 -3.0529632035400311e+00 0 0 0 -70 1 5 4.2499999999999999e-01 -1.0663272694379867e+01 8.2812544466164262e+00 -1.1574899906021443e+00 1 0 0 -71 1 1 1.5750000000000000e+00 5.1679291867752841e+00 2.9932421670148486e+00 -3.7577228510699001e-03 0 0 0 -72 1 2 2.1000000000000001e+00 -1.1044767300168616e+01 4.5852760408334774e+00 -2.7609521778782176e+00 1 0 0 -73 1 2 2.1000000000000001e+00 -1.1124388980911734e+01 1.6303195608669299e+00 -2.7688431292616507e+00 1 0 0 -74 1 3 -1.0500000000000000e+00 -1.0903581498310636e+01 4.5142906934311924e+00 -1.1427845473335605e+00 1 0 0 -75 1 3 -1.0500000000000000e+00 -1.1259106369033972e+01 1.7230268384806458e+00 -1.1439410995923396e+00 1 0 0 -76 1 4 -9.4999999999999996e-01 6.8617144958469076e+00 2.9195557673048818e+00 -1.0139879262950160e+00 0 0 0 -77 1 3 -1.0500000000000000e+00 8.2108057946072996e+00 9.2735729230230390e-01 -3.2425776499777719e+00 0 0 0 -78 1 3 -1.0500000000000000e+00 -1.1012059810070339e+01 3.1045919471971004e+00 -3.2446473870783468e+00 1 0 0 -79 1 3 -1.0500000000000000e+00 -9.8588900558710808e+00 7.7850518566728155e-01 -3.0529472339342547e+00 1 0 0 -80 1 5 4.2499999999999999e-01 7.3685681396951637e+00 3.7983364769353116e+00 -1.1575475830507376e+00 0 0 0 -81 1 1 1.5750000000000000e+00 -7.7200137563852609e+00 1.4895294530661403e+00 -3.2684599822623284e-03 1 0 0 -82 1 2 2.1000000000000001e+00 -6.9309835694285180e+00 8.8633000360003891e+00 2.7539655829756615e+00 1 0 0 -83 1 2 2.1000000000000001e+00 -6.9077088390904287e+00 2.8524991867344269e+00 2.7618117534379607e+00 1 0 0 -84 1 3 -1.0500000000000000e+00 -7.1282896122357791e+00 -3.1337790666402299e-02 1.1357138609993100e+00 1 0 0 -85 1 3 -1.0500000000000000e+00 -6.7730290070254648e+00 2.7595293114608452e+00 1.1370158767616427e+00 1 0 0 -86 1 4 -9.4999999999999996e-01 -9.4137011727461637e+00 1.5632889909165790e+00 1.0069804405775873e+00 1 0 0 -87 1 3 -1.0500000000000000e+00 -1.0762886927889904e+01 3.5553424385566323e+00 3.2357631758371692e+00 1 0 0 -88 1 3 -1.0500000000000000e+00 -7.0198114225359447e+00 1.3781627149599167e+00 3.2374581287983730e+00 1 0 0 -89 1 3 -1.0500000000000000e+00 -8.1732123893871496e+00 3.7041758941777481e+00 3.0459624211456457e+00 1 0 0 -90 1 5 4.2499999999999999e-01 -9.9205899642595909e+00 6.8440357171864363e-01 1.1507119739579768e+00 1 0 0 -91 1 1 1.5750000000000000e+00 -5.1118389155552801e+00 5.9724604992986876e+00 -3.2677938051328681e-03 1 0 0 -92 1 2 2.1000000000000001e+00 -9.5391764189832653e+00 4.3803869998831004e+00 2.7539574271080607e+00 1 0 0 -93 1 2 2.1000000000000001e+00 -9.4595081799096388e+00 7.3353965025317827e+00 2.7618078347600434e+00 1 0 0 -94 1 3 -1.0500000000000000e+00 -9.6801468113930103e+00 4.4515564521540760e+00 1.1357496055372582e+00 1 0 0 -95 1 3 -1.0500000000000000e+00 -9.3248381172301720e+00 7.2424157374410534e+00 1.1369982830380732e+00 1 0 0 -96 1 4 -9.4999999999999996e-01 -6.8054892996665686e+00 6.0462398159612256e+00 1.0069818364520042e+00 1 0 0 -97 1 3 -1.0500000000000000e+00 -8.1546879688645451e+00 8.0382456007117398e+00 3.2357858485300515e+00 1 0 0 -98 1 3 -1.0500000000000000e+00 -9.5716494128412961e+00 5.8610801354623092e+00 3.2374551881368738e+00 1 0 0 -99 1 3 -1.0500000000000000e+00 -1.0725043969209066e+01 8.1871237437456550e+00 3.0459446329304463e+00 1 0 0 -100 1 5 4.2499999999999999e-01 -7.3124574315238666e+00 5.1673091520186851e+00 1.1507497875855961e+00 1 0 0 -101 1 1 1.5750000000000000e+00 -7.7038948864236607e+00 7.4761709779946877e+00 -3.7563858194751276e-03 1 0 0 -102 1 2 2.1000000000000001e+00 -8.4929416250579237e+00 1.0238205190912097e-01 -2.7609433628825437e+00 1 0 0 -103 1 2 2.1000000000000001e+00 -8.5162068339533832e+00 6.1131914931820752e+00 -2.7688610988552806e+00 1 0 0 -104 1 3 -1.0500000000000000e+00 -8.2954155113210621e+00 8.9971804574221004e+00 -1.1427988389805961e+00 1 0 0 -105 1 3 -1.0500000000000000e+00 -8.6509205149498847e+00 6.2059203624978956e+00 -1.1439628590241284e+00 1 0 0 -106 1 4 -9.4999999999999996e-01 -6.0100779546322549e+00 7.4024979040990928e+00 -1.0139958673379255e+00 1 0 0 -107 1 3 -1.0500000000000000e+00 -4.6609961845666854e+00 5.4102617087963587e+00 -3.2425544229802377e+00 1 0 0 -108 1 3 -1.0500000000000000e+00 -8.4038980403496684e+00 7.5874972699614922e+00 -3.2446537367483241e+00 1 0 0 -109 1 3 -1.0500000000000000e+00 -7.2507418722833936e+00 5.2614645688107124e+00 -3.0529610822747220e+00 1 0 0 -110 1 5 4.2499999999999999e-01 -5.5033059365206407e+00 8.2812208311420328e+00 -1.1575765233774842e+00 1 0 0 -111 1 1 1.5750000000000000e+00 -1.0312056810203035e+01 2.9932322130065998e+00 -3.7546907983969646e-03 1 0 0 -112 1 2 2.1000000000000001e+00 -5.8847433874873287e+00 4.5852785579583113e+00 -2.7609429033193313e+00 1 0 0 -113 1 2 2.1000000000000001e+00 -5.9643839170466686e+00 1.6303066689684940e+00 -2.7688547216593093e+00 1 0 0 -114 1 3 -1.0500000000000000e+00 -5.7435669944029248e+00 4.5142929050558713e+00 -1.1428123357037983e+00 1 0 0 -115 1 3 -1.0500000000000000e+00 -6.0991091928443781e+00 1.7230226164423819e+00 -1.1439386957606192e+00 1 0 0 -116 1 4 -9.4999999999999996e-01 -8.6183165743253909e+00 2.9195138811823007e+00 -1.0140048055866231e+00 1 0 0 -117 1 3 -1.0500000000000000e+00 -7.2691903819614820e+00 9.2736020008046438e-01 -3.2425717834672385e+00 1 0 0 -118 1 3 -1.0500000000000000e+00 -5.8520558193569849e+00 3.1046084246173429e+00 -3.2446638893543165e+00 1 0 0 -119 1 3 -1.0500000000000000e+00 -4.6989020453283867e+00 7.7850970320779922e-01 -3.0529439927969300e+00 1 0 0 -120 1 5 4.2499999999999999e-01 -8.1114486366180678e+00 3.7983239523822867e+00 -1.1577012407700238e+00 1 0 0 -121 1 1 1.5750000000000000e+00 -2.5599994626479976e+00 1.4895195225863134e+00 -3.2660642944506435e-03 1 0 0 -122 1 2 2.1000000000000001e+00 -1.7710062286867227e+00 8.8632972747936982e+00 2.7539546373495405e+00 1 0 0 -123 1 2 2.1000000000000001e+00 -1.7477123009753157e+00 2.8525136185930755e+00 2.7618236550482624e+00 1 0 0 -124 1 3 -1.0500000000000000e+00 -1.9683041681687321e+00 -3.1339314943114260e-02 1.1357434005060227e+00 1 0 0 -125 1 3 -1.0500000000000000e+00 -1.6130256197699246e+00 2.7595328394810750e+00 1.1370135266718489e+00 1 0 0 -126 1 4 -9.4999999999999996e-01 -4.2537337856944379e+00 1.5632441680250189e+00 1.0069648786930525e+00 1 0 0 -127 1 3 -1.0500000000000000e+00 -5.6028806915798572e+00 3.5553457721911457e+00 3.2357688590305571e+00 1 0 0 -128 1 3 -1.0500000000000000e+00 -1.8598151617417997e+00 1.3781457768821710e+00 3.2374742127375917e+00 1 0 0 -129 1 3 -1.0500000000000000e+00 -3.0132009706882785e+00 3.7041715558434340e+00 3.0459588995882712e+00 1 0 0 -130 1 5 4.2499999999999999e-01 -4.7606046035337171e+00 6.8439349642540748e-01 1.1505607409996657e+00 1 0 0 -131 1 1 1.5750000000000000e+00 4.8168369057153271e-02 5.9724605712145085e+00 -3.2704117388746567e-03 1 0 0 -132 1 2 2.1000000000000001e+00 -4.3791817898634946e+00 4.3804013865484848e+00 2.7539658968809935e+00 1 0 0 -133 1 2 2.1000000000000001e+00 -4.2995319515097883e+00 7.3353882982958218e+00 2.7618078686262244e+00 1 0 0 -134 1 3 -1.0500000000000000e+00 -4.5201407292494871e+00 4.4515523522287275e+00 1.1357346289073345e+00 1 0 0 -135 1 3 -1.0500000000000000e+00 -4.1648406595123815e+00 7.2424265294413708e+00 1.1369912415882819e+00 1 0 0 -136 1 4 -9.4999999999999996e-01 -1.6454953945829232e+00 6.0462304571223804e+00 1.0070026477177905e+00 1 0 0 -137 1 3 -1.0500000000000000e+00 -2.9946439468920527e+00 8.0382690637231917e+00 3.2357743104080114e+00 1 0 0 -138 1 3 -1.0500000000000000e+00 -4.4116515205365854e+00 5.8610507068010520e+00 3.2374621599322246e+00 1 0 0 -139 1 3 -1.0500000000000000e+00 -5.5650494733788616e+00 8.1871266439263444e+00 3.0459458790032787e+00 1 0 0 -140 1 5 4.2499999999999999e-01 -2.1524218145509142e+00 5.1673462038601450e+00 1.1508591924041909e+00 1 0 0 -141 1 1 1.5750000000000000e+00 -2.5438859347542353e+00 7.4761697245415135e+00 -3.7582632092334478e-03 1 0 0 -142 1 2 2.1000000000000001e+00 -3.3329363804842851e+00 1.0236811376637078e-01 -2.7609530240071400e+00 1 0 0 -143 1 2 2.1000000000000001e+00 -3.3561829873008966e+00 6.1132000526679882e+00 -2.7688597295862918e+00 1 0 0 -144 1 3 -1.0500000000000000e+00 -3.1354235356298261e+00 8.9971842040066932e+00 -1.1427807792040543e+00 1 0 0 -145 1 3 -1.0500000000000000e+00 -3.4909182600583835e+00 6.2059094368932080e+00 -1.1439561958179105e+00 1 0 0 -146 1 4 -9.4999999999999996e-01 -8.5008735848989936e-01 7.4024836920448358e+00 -1.0139778123793981e+00 1 0 0 -147 1 3 -1.0500000000000000e+00 4.9904929440264922e-01 5.4102852955378324e+00 -3.2425656000684357e+00 1 0 0 -148 1 3 -1.0500000000000000e+00 -3.2438965163198175e+00 7.5875255784303768e+00 -3.2446584652783006e+00 1 0 0 -149 1 3 -1.0500000000000000e+00 -2.0907332304654513e+00 5.2614599131077355e+00 -3.0529635108663689e+00 1 0 0 -150 1 5 4.2499999999999999e-01 -3.4327252441785028e-01 8.2812545429943718e+00 -1.1574891817763771e+00 1 0 0 -151 1 1 1.5750000000000000e+00 -5.1520707526539180e+00 2.9932422696247407e+00 -3.7575710303681120e-03 1 0 0 -152 1 2 2.1000000000000001e+00 -7.2476725112305829e-01 4.5852758198773422e+00 -2.7609523624229535e+00 1 0 0 -153 1 2 2.1000000000000001e+00 -8.0438924000571177e-01 1.6303201218117742e+00 -2.7688428916587142e+00 1 0 0 -154 1 3 -1.0500000000000000e+00 -5.8358168128787469e-01 4.5142909571471250e+00 -1.1427842365077652e+00 1 0 0 -155 1 3 -1.0500000000000000e+00 -9.3910616162538574e-01 1.7230269705662700e+00 -1.1439410014752056e+00 1 0 0 -156 1 4 -9.4999999999999996e-01 -3.4582854395786136e+00 2.9195566902968508e+00 -1.0139875801681342e+00 1 0 0 -157 1 3 -1.0500000000000000e+00 -2.1091939503703152e+00 9.2735770730401157e-01 -3.2425784113786129e+00 1 0 0 -158 1 3 -1.0500000000000000e+00 -6.9205988720759315e-01 3.1045918731948028e+00 -3.2446474792644775e+00 1 0 0 -159 1 3 -1.0500000000000000e+00 4.6110929925033162e-01 7.7850537414041909e-01 -3.0529474813034367e+00 1 0 0 -160 1 5 4.2499999999999999e-01 -2.9514312473451580e+00 3.7983374737998545e+00 -1.1575435527823981e+00 1 0 0 -161 1 1 1.5750000000000000e+00 2.6563134001835174e+00 1.0455371516328153e+01 -3.2734775751421807e-03 0 0 0 -162 1 2 2.1000000000000001e+00 3.2200000793507915e+00 -1.8034168055802969e+01 2.7539699954278376e+00 0 1 0 -163 1 2 2.1000000000000001e+00 3.4686304351053021e+00 1.1818330371923647e+01 2.7618152520441335e+00 0 0 0 -164 1 3 -1.0500000000000000e+00 3.2480341177807297e+00 8.9344903893301542e+00 1.1357344410643542e+00 0 0 0 -165 1 3 -1.0500000000000000e+00 3.6033098122302398e+00 1.1725346434065575e+01 1.1370112044265888e+00 0 0 0 -166 1 4 -9.4999999999999996e-01 9.6267397491767959e-01 1.0529174885771052e+01 1.0070252189892397e+00 0 0 0 -167 1 3 -1.0500000000000000e+00 -3.8653877399151604e-01 1.2521163438190538e+01 3.2357641403694473e+00 0 0 0 -168 1 3 -1.0500000000000000e+00 3.3565301561723722e+00 1.0343985356367806e+01 3.2374269175278840e+00 0 0 0 -169 1 3 -1.0500000000000000e+00 2.2031200709181498e+00 1.2669998832466721e+01 3.0459640112402759e+00 0 0 0 -170 1 5 4.2499999999999999e-01 4.5576637197079073e-01 9.6503202047150296e+00 1.1510612204759685e+00 0 0 0 -171 1 1 1.5750000000000000e+00 5.2644962792255114e+00 1.4938261534530948e+01 -3.2647451325473753e-03 0 0 0 -172 1 2 2.1000000000000001e+00 8.3716356417033211e-01 1.3346218268802954e+01 2.7539470758802835e+00 0 0 0 -173 1 2 2.1000000000000001e+00 9.1681075011299562e-01 1.6301231345167661e+01 2.7618125766893566e+00 0 0 0 -174 1 3 -1.0500000000000000e+00 6.9620077398076852e-01 1.3417388047104328e+01 1.1357329589097631e+00 0 0 0 -175 1 3 -1.0500000000000000e+00 1.0514831822196271e+00 1.6208254021374852e+01 1.1370122634561355e+00 0 0 0 -176 1 4 -9.4999999999999996e-01 3.5707945950799811e+00 1.5012003258438757e+01 1.0069522165247839e+00 0 0 0 -177 1 3 -1.0500000000000000e+00 2.2216102376958187e+00 1.7004067428805609e+01 3.2357767642373751e+00 0 0 0 -178 1 3 -1.0500000000000000e+00 8.0468569905412757e-01 1.4826903246577299e+01 3.2374729366625967e+00 0 0 0 -179 1 3 -1.0500000000000000e+00 -3.4870265184444982e-01 1.7152922875565228e+01 3.0459529855636607e+00 0 0 0 -180 1 5 4.2499999999999999e-01 3.0638743761134464e+00 1.4133083814238685e+01 1.1505085237616779e+00 0 0 0 -181 1 1 1.5750000000000000e+00 2.6724321383913008e+00 1.6441984484756983e+01 -3.7572437476853793e-03 0 0 0 -182 1 2 2.1000000000000001e+00 1.8833962660382699e+00 9.0681931419489921e+00 -2.7609335407081872e+00 0 0 0 -183 1 2 2.1000000000000001e+00 1.8601111196809121e+00 1.5079040342628350e+01 -2.7688508235497418e+00 0 0 0 -184 1 3 -1.0500000000000000e+00 1.8555857070306878e+00 -1.7900270605410917e+01 -1.1427969927137482e+00 0 1 0 -185 1 3 -1.0500000000000000e+00 1.7253995479611604e+00 1.5171757634659638e+01 -1.1439490826924779e+00 0 0 0 -186 1 4 -9.4999999999999996e-01 4.3662322107158733e+00 1.6368309943641055e+01 -1.0139855229210077e+00 0 0 0 -187 1 3 -1.0500000000000000e+00 5.7153027190516923e+00 1.4376080986155142e+01 -3.2425652845281192e+00 0 0 0 -188 1 3 -1.0500000000000000e+00 1.9724442262540727e+00 1.6553320295057897e+01 -3.2446645540223695e+00 0 0 0 -189 1 3 -1.0500000000000000e+00 3.1255993997228746e+00 1.4227253671506265e+01 -3.0529479201541534e+00 0 0 0 -190 1 5 4.2499999999999999e-01 4.8730503872703981e+00 1.7247084738816252e+01 -1.1575108950696489e+00 0 0 0 -191 1 1 1.5750000000000000e+00 6.4287605771619738e-02 1.1959047499133955e+01 -3.7503865813821591e-03 0 0 0 -192 1 2 2.1000000000000001e+00 4.4915804029907438e+00 1.3551111831722395e+01 -2.7609471625592663e+00 0 0 0 -193 1 2 2.1000000000000001e+00 4.4119577432399595e+00 1.0596109686747351e+01 -2.7688628659227765e+00 0 0 0 -194 1 3 -1.0500000000000000e+00 4.6327720860930999e+00 1.3480101674103746e+01 -1.1428277031521787e+00 0 0 0 -195 1 3 -1.0500000000000000e+00 4.2772319797087590e+00 1.0688840900034439e+01 -1.1439426903633443e+00 0 0 0 -196 1 4 -9.4999999999999996e-01 1.7580042741312081e+00 1.1885300143491271e+01 -1.0140430923412715e+00 0 0 0 -197 1 3 -1.0500000000000000e+00 3.1071547577327969e+00 9.8931852268420180e+00 -3.2425655357851380e+00 0 0 0 -198 1 3 -1.0500000000000000e+00 4.5242699949370255e+00 1.2070431784027537e+01 -3.2446352562949325e+00 0 0 0 -199 1 3 -1.0500000000000000e+00 5.6774301856846563e+00 9.7443531193086770e+00 -3.0529533590117293e+00 0 0 0 -200 1 5 4.2499999999999999e-01 2.2648570458734767e+00 1.2764054876209290e+01 -1.1579888107521015e+00 0 0 0 -201 1 1 1.5750000000000000e+00 7.8163274667709004e+00 1.0455361822489916e+01 -3.2709891462765484e-03 0 0 0 -202 1 2 2.1000000000000001e+00 -1.2260022563792901e+01 -1.8034171036451074e+01 2.7539593838063130e+00 1 1 0 -203 1 2 2.1000000000000001e+00 -1.2011372927310228e+01 1.1818343457748362e+01 2.7618265468659775e+00 1 0 0 -204 1 3 -1.0500000000000000e+00 8.4080197705526096e+00 8.9344884845612391e+00 1.1357635616008466e+00 0 0 0 -205 1 3 -1.0500000000000000e+00 -1.1876687672220676e+01 1.1725350016313566e+01 1.1370089651291622e+00 1 0 0 -206 1 4 -9.4999999999999996e-01 6.1226432401957958e+00 1.0529133102795765e+01 1.0070115060204152e+00 0 0 0 -207 1 3 -1.0500000000000000e+00 4.7734668544665446e+00 1.2521166817291366e+01 3.2357690806651949e+00 0 0 0 -208 1 3 -1.0500000000000000e+00 -1.2123474030539958e+01 1.0343970332896109e+01 3.2374431734477387e+00 1 0 0 -209 1 3 -1.0500000000000000e+00 7.3631314638252157e+00 1.2669994182427597e+01 3.0459606244004132e+00 0 0 0 -210 1 5 4.2499999999999999e-01 5.6157533562485771e+00 9.6503133176461908e+00 1.1509245775060091e+00 0 0 0 -211 1 1 1.5750000000000000e+00 -1.0215496529349888e+01 1.4938261423331294e+01 -3.2671984669594423e-03 1 0 0 -212 1 2 2.1000000000000001e+00 5.9971582839890871e+00 1.3346232288807276e+01 2.7539563176022028e+00 0 0 0 -213 1 2 2.1000000000000001e+00 6.0767870017495582e+00 1.6301224009304729e+01 2.7618126124878195e+00 0 0 0 -214 1 3 -1.0500000000000000e+00 5.8562070155499164e+00 1.3417384395333375e+01 1.1357183501401593e+00 0 0 0 -215 1 3 -1.0500000000000000e+00 6.2114804261402661e+00 1.6208264386955651e+01 1.1370050309136168e+00 0 0 0 -216 1 4 -9.4999999999999996e-01 -1.1909211620817819e+01 1.5011994029862020e+01 1.0069718593232118e+00 1 0 0 -217 1 3 -1.0500000000000000e+00 7.3816555131360566e+00 1.7004091118074196e+01 3.2357656756583246e+00 0 0 0 -218 1 3 -1.0500000000000000e+00 5.9646835115051537e+00 1.4826873453180109e+01 3.2374785589618078e+00 0 0 0 -219 1 3 -1.0500000000000000e+00 4.8112906275483347e+00 1.7152926240598543e+01 3.0459544088802879e+00 0 0 0 -220 1 5 4.2499999999999999e-01 8.2239086021434531e+00 1.4133118218483940e+01 1.1506108499324537e+00 0 0 0 -221 1 1 1.5750000000000000e+00 7.8324408920161304e+00 1.6441983610717937e+01 -3.7594297909890884e-03 0 0 0 -222 1 2 2.1000000000000001e+00 7.0433997235143551e+00 9.0681792148270475e+00 -2.7609433793505778e+00 0 0 0 -223 1 2 2.1000000000000001e+00 7.0201340001296231e+00 1.5079048983075904e+01 -2.7688496626705303e+00 0 0 0 -224 1 3 -1.0500000000000000e+00 7.0155782305852874e+00 -1.7900266980861765e+01 -1.1427797504921990e+00 0 1 0 -225 1 3 -1.0500000000000000e+00 6.8854016068099639e+00 1.5171747073972536e+01 -1.1439426401780413e+00 0 0 0 -226 1 4 -9.4999999999999996e-01 -1.1113777147342798e+01 1.6368296182520734e+01 -1.0139673644246923e+00 1 0 0 -227 1 3 -1.0500000000000000e+00 -9.7646511702758900e+00 1.4376104836232383e+01 -3.2425769131107307e+00 1 0 0 -228 1 3 -1.0500000000000000e+00 7.1324461842806457e+00 1.6553349104877181e+01 -3.2446699530389926e+00 0 0 0 -229 1 3 -1.0500000000000000e+00 8.2856077950535507e+00 1.4227248777578755e+01 -3.0529496330623935e+00 0 0 0 -230 1 5 4.2499999999999999e-01 -1.0606916142948355e+01 1.7247118324243235e+01 -1.1574224546860155e+00 1 0 0 -231 1 1 1.5750000000000000e+00 5.2242739244354812e+00 1.1959056994618571e+01 -3.7532727914637576e-03 0 0 0 -232 1 2 2.1000000000000001e+00 -1.0988443129796577e+01 1.3551110560593809e+01 -2.7609569176995983e+00 1 0 0 -233 1 2 2.1000000000000001e+00 -1.1068048695702130e+01 1.0596121856381689e+01 -2.7688513474828484e+00 1 0 0 -234 1 3 -1.0500000000000000e+00 -1.0847241951081806e+01 1.3480099061382369e+01 -1.1428004053747500e+00 1 0 0 -235 1 3 -1.0500000000000000e+00 -1.1202765363241992e+01 1.0688845693686350e+01 -1.1439459591196393e+00 1 0 0 -236 1 4 -9.4999999999999996e-01 6.9180337353417016e+00 1.1885339729941098e+01 -1.0140270633946198e+00 0 0 0 -237 1 3 -1.0500000000000000e+00 8.2671524098729279e+00 9.8931836876833437e+00 -3.2425721304984254e+00 0 0 0 -238 1 3 -1.0500000000000000e+00 -1.0955734208517388e+01 1.2070414566453039e+01 -3.2446182476609398e+00 1 0 0 -239 1 3 -1.0500000000000000e+00 -9.8025577056732693e+00 9.7443480474548068e+00 -3.0529560570682035e+00 1 0 0 -240 1 5 4.2499999999999999e-01 7.4248734752605259e+00 1.2764066295177155e+01 -1.1578424655573620e+00 0 0 0 -241 1 1 1.5750000000000000e+00 -7.6636863980105723e+00 1.0455371549550875e+01 -3.2736966669304479e-03 1 0 0 -242 1 2 2.1000000000000001e+00 -7.0999990710355192e+00 -1.8034168111282145e+01 2.7539701102788019e+00 1 1 0 -243 1 2 2.1000000000000001e+00 -6.8513690426892451e+00 1.1818330417854160e+01 2.7618149786464521e+00 1 0 0 -244 1 3 -1.0500000000000000e+00 -7.0719662711101812e+00 8.9344903042783628e+00 1.1357345069924225e+00 1 0 0 -245 1 3 -1.0500000000000000e+00 -6.7166907540342233e+00 1.1725346382842613e+01 1.1370110473100272e+00 1 0 0 -246 1 4 -9.4999999999999996e-01 -9.3573260672622727e+00 1.0529174208841528e+01 1.0070252131964725e+00 1 0 0 -247 1 3 -1.0500000000000000e+00 -1.0706539610507846e+01 1.2521163280396088e+01 3.2357640814962956e+00 1 0 0 -248 1 3 -1.0500000000000000e+00 -6.9634703631137729e+00 1.0343985091037393e+01 3.2374266804707119e+00 1 0 0 -249 1 3 -1.0500000000000000e+00 -8.1168803339350379e+00 1.2669999097078030e+01 3.0459638003843619e+00 1 0 0 -250 1 5 4.2499999999999999e-01 -9.8642334722321223e+00 9.6503204443188402e+00 1.1510602007101145e+00 1 0 0 -251 1 1 1.5750000000000000e+00 -5.0555038517207738e+00 1.4938261738890343e+01 -3.2644360310367659e-03 1 0 0 -252 1 2 2.1000000000000001e+00 -9.4828364774811842e+00 1.3346217905042987e+01 2.7539473478098913e+00 1 0 0 -253 1 2 2.1000000000000001e+00 -9.4031892666074217e+00 1.6301231779632818e+01 2.7618124621169056e+00 1 0 0 -254 1 3 -1.0500000000000000e+00 -9.6237991655729793e+00 1.3417388438033317e+01 1.1357331457960171e+00 1 0 0 -255 1 3 -1.0500000000000000e+00 -9.2685168345328872e+00 1.6208253547145770e+01 1.1370123846061553e+00 1 0 0 -256 1 4 -9.4999999999999996e-01 -6.7492056915652885e+00 1.5012003321026935e+01 1.0069523554452928e+00 1 0 0 -257 1 3 -1.0500000000000000e+00 -8.0983893100633519e+00 1.7004067932473877e+01 3.2357766175726148e+00 1 0 0 -258 1 3 -1.0500000000000000e+00 -9.5153144179962954e+00 1.4826903279627327e+01 3.2374724255443947e+00 1 0 0 -259 1 3 -1.0500000000000000e+00 -1.0668703168553188e+01 1.7152922672989472e+01 3.0459531606477839e+00 1 0 0 -260 1 5 4.2499999999999999e-01 -7.2561252036506980e+00 1.4133084321951994e+01 1.1505098705128987e+00 1 0 0 -261 1 1 1.5750000000000000e+00 -7.6475676736905971e+00 1.6441984614323157e+01 -3.7576410911643876e-03 1 0 0 -262 1 2 2.1000000000000001e+00 -8.4366040275132406e+00 9.0681930278031473e+00 -2.7609335500569747e+00 1 0 0 -263 1 2 2.1000000000000001e+00 -8.4598890146513455e+00 1.5079040601080134e+01 -2.7688510489233167e+00 1 0 0 -264 1 3 -1.0500000000000000e+00 -8.4644143079304612e+00 -1.7900269957740477e+01 -1.1427967775321957e+00 1 1 0 -265 1 3 -1.0500000000000000e+00 -8.5946007116663132e+00 1.5171756827133724e+01 -1.1439485975510628e+00 1 0 0 -266 1 4 -9.4999999999999996e-01 -5.9537679681653151e+00 1.6368309168283670e+01 -1.0139856989316165e+00 1 0 0 -267 1 3 -1.0500000000000000e+00 -4.6046964494860898e+00 1.4376081286353607e+01 -3.2425649205705174e+00 1 0 0 -268 1 3 -1.0500000000000000e+00 -8.3475558754399124e+00 1.6553320859356912e+01 -3.2446655570405536e+00 1 0 0 -269 1 3 -1.0500000000000000e+00 -7.1944009052872069e+00 1.4227253415250768e+01 -3.0529479683150100e+00 1 0 0 -270 1 5 4.2499999999999999e-01 -5.4469496542482680e+00 1.7247084860834857e+01 -1.1575125267203532e+00 1 0 0 -271 1 1 1.5750000000000000e+00 -1.0255712633101528e+01 1.1959047249232842e+01 -3.7503693590110743e-03 1 0 0 -272 1 2 2.1000000000000001e+00 -5.8284201346147073e+00 1.3551112633249122e+01 -2.7609471419709433e+00 1 0 0 -273 1 2 2.1000000000000001e+00 -5.9080424941932153e+00 1.0596109220690689e+01 -2.7688628072186683e+00 1 0 0 -274 1 3 -1.0500000000000000e+00 -5.6872277599586996e+00 1.3480101435889090e+01 -1.1428283866518854e+00 1 0 0 -275 1 3 -1.0500000000000000e+00 -6.0427680579917293e+00 1.0688841214141384e+01 -1.1439433517717656e+00 1 0 0 -276 1 4 -9.4999999999999996e-01 -8.5619959665734147e+00 1.1885299915121873e+01 -1.0140429279795615e+00 1 0 0 -277 1 3 -1.0500000000000000e+00 -7.2128453343082271e+00 9.8931855774587198e+00 -3.2425657643378525e+00 1 0 0 -278 1 3 -1.0500000000000000e+00 -5.7957299312633053e+00 1.2070431551626321e+01 -3.2446350249445235e+00 1 0 0 -279 1 3 -1.0500000000000000e+00 -4.6425695661462196e+00 9.7443530477275679e+00 -3.0529532196506795e+00 1 0 0 -280 1 5 4.2499999999999999e-01 -8.0551425673563894e+00 1.2764055666864170e+01 -1.1579876909028854e+00 1 0 0 -281 1 1 1.5750000000000000e+00 -2.5036724751688171e+00 1.0455361759083811e+01 -3.2712711680531470e-03 1 0 0 -282 1 2 2.1000000000000001e+00 -1.9400228605798482e+00 -1.8034171454532189e+01 2.7539596865588258e+00 1 1 0 -283 1 2 2.1000000000000001e+00 -1.6913729123417731e+00 1.1818343618664318e+01 2.7618267327017616e+00 1 0 0 -284 1 3 -1.0500000000000000e+00 -1.9119803931372488e+00 8.9344892347650671e+00 1.1357639880848378e+00 1 0 0 -285 1 3 -1.0500000000000000e+00 -1.5566878690749917e+00 1.1725349553088837e+01 1.1370088549317554e+00 1 0 0 -286 1 4 -9.4999999999999996e-01 -4.1973568501655052e+00 1.0529132534062505e+01 1.0070110973834296e+00 1 0 0 -287 1 3 -1.0500000000000000e+00 -5.5465318119038987e+00 1.2521167504631986e+01 3.2357693370264009e+00 1 0 0 -288 1 3 -1.0500000000000000e+00 -1.8034741996403554e+00 1.0343970336471010e+01 3.2374423050401600e+00 1 0 0 -289 1 3 -1.0500000000000000e+00 -2.9568695567301644e+00 1.2669994183537906e+01 3.0459605033388062e+00 1 0 0 -290 1 5 4.2499999999999999e-01 -4.7042469088330439e+00 9.6503130783144044e+00 1.1509221001927727e+00 1 0 0 -291 1 1 1.5750000000000000e+00 1.0450346973463098e-01 1.4938261236297333e+01 -3.2670506730401172e-03 1 0 0 -292 1 2 2.1000000000000001e+00 -4.3228420481760716e+00 1.3346232732007316e+01 2.7539563932159634e+00 1 0 0 -293 1 2 2.1000000000000001e+00 -4.2432134692497367e+00 1.6301223752354527e+01 2.7618123881160574e+00 1 0 0 -294 1 3 -1.0500000000000000e+00 -4.4637927327409024e+00 1.3417383967610970e+01 1.1357180288639768e+00 1 0 0 -295 1 3 -1.0500000000000000e+00 -4.1085196585471646e+00 1.6208264626532635e+01 1.1370043708840640e+00 1 0 0 -296 1 4 -9.4999999999999996e-01 -1.5892118927513685e+00 1.5011993465855195e+01 1.0069717418888526e+00 1 0 0 -297 1 3 -1.0500000000000000e+00 -2.9383445989094135e+00 1.7004091419961096e+01 3.2357659595260095e+00 1 0 0 -298 1 3 -1.0500000000000000e+00 -4.3553166601485476e+00 1.4826873197424877e+01 3.2374790045700852e+00 1 0 0 -299 1 3 -1.0500000000000000e+00 -5.5087090978359239e+00 1.7152925908147093e+01 3.0459546197474001e+00 1 0 0 -300 1 5 4.2499999999999999e-01 -2.0960913924612612e+00 1.4133118310388799e+01 1.1506099517336104e+00 1 0 0 -301 1 1 1.5750000000000000e+00 -2.4875591017952310e+00 1.6441983631942183e+01 -3.7595151500315893e-03 1 0 0 -302 1 2 2.1000000000000001e+00 -3.2765996605939858e+00 9.0681789268622346e+00 -2.7609433563661456e+00 1 0 0 -303 1 2 2.1000000000000001e+00 -3.2998653023383682e+00 1.5079049184116290e+01 -2.7688496671533649e+00 1 0 0 -304 1 3 -1.0500000000000000e+00 -3.3044219890671753e+00 -1.7900266681096994e+01 -1.1427795585717835e+00 1 1 0 -305 1 3 -1.0500000000000000e+00 -3.4345989454937165e+00 1.5171746655803712e+01 -1.1439426919442308e+00 1 0 0 -306 1 4 -9.4999999999999996e-01 -7.9377665672300779e-01 1.6368296633100169e+01 -1.0139672617067319e+00 1 0 0 -307 1 3 -1.0500000000000000e+00 5.5534868438584084e-01 1.4376105151954103e+01 -3.2425775178456107e+00 1 0 0 -308 1 3 -1.0500000000000000e+00 -3.1875541544200496e+00 1.6553349368867583e+01 -3.2446705490554963e+00 1 0 0 -309 1 3 -1.0500000000000000e+00 -2.0343923260202423e+00 1.4227248534036743e+01 -3.0529497284009599e+00 1 0 0 -310 1 5 4.2499999999999999e-01 -2.8691619587030104e-01 1.7247118382062236e+01 -1.1574212565794415e+00 1 0 0 -311 1 1 1.5750000000000000e+00 -5.0957261453677205e+00 1.1959057139099524e+01 -3.7531547867057924e-03 1 0 0 -312 1 2 2.1000000000000001e+00 -6.6844354483350799e-01 1.3551110266998439e+01 -2.7609568234960040e+00 1 0 0 -313 1 2 2.1000000000000001e+00 -7.4804896223840167e-01 1.0596122230984061e+01 -2.7688515273933572e+00 1 0 0 -314 1 3 -1.0500000000000000e+00 -5.2724202123535946e-01 1.3480098969688658e+01 -1.1428001598979964e+00 1 0 0 -315 1 3 -1.0500000000000000e+00 -8.8276536122547178e-01 1.0688845631800483e+01 -1.1439460002810460e+00 1 0 0 -316 1 4 -9.4999999999999996e-01 -3.4019662726515918e+00 1.1885340094739142e+01 -1.0140272109664838e+00 1 0 0 -317 1 3 -1.0500000000000000e+00 -2.0528473081502625e+00 9.8931841810449264e+00 -3.2425722407459503e+00 1 0 0 -318 1 3 -1.0500000000000000e+00 -6.3573424619330154e-01 1.2070414396821146e+01 -3.2446180957081046e+00 1 0 0 -319 1 3 -1.0500000000000000e+00 5.1744185669619647e-01 9.7443480289127145e+00 -3.0529559039522471e+00 1 0 0 -320 1 5 4.2499999999999999e-01 -2.8951265260920414e+00 1.2764066156472012e+01 -1.1578424373010439e+00 1 0 0 -321 1 1 1.5750000000000000e+00 2.4873158312812595e+00 -1.6442085140542702e+01 -3.2731044767775330e-03 0 1 0 -322 1 2 2.1000000000000001e+00 3.2763303149493339e+00 -9.0683326576281793e+00 2.7539613504973826e+00 0 1 0 -323 1 2 2.1000000000000001e+00 3.2996440062323522e+00 -1.5079163855001152e+01 2.7618052351303000e+00 0 1 0 -324 1 3 -1.0500000000000000e+00 3.0790227247234743e+00 -1.7962996900519780e+01 1.1357369105052264e+00 0 1 0 -325 1 3 -1.0500000000000000e+00 3.4343217071143606e+00 -1.5172137633996615e+01 1.1369964612814574e+00 0 1 0 -326 1 4 -9.4999999999999996e-01 7.9370044014982000e-01 -1.6368273971208470e+01 1.0070225988534567e+00 0 1 0 -327 1 3 -1.0500000000000000e+00 -5.5550646979719431e-01 -1.4376303482612908e+01 3.2357750050409617e+00 0 1 0 -328 1 3 -1.0500000000000000e+00 3.1875194117149164e+00 -1.6553483052839894e+01 3.2374345381514082e+00 0 1 0 -329 1 3 -1.0500000000000000e+00 2.0341105779595932e+00 -1.4227436497588396e+01 3.0459506035312476e+00 0 1 0 -330 1 5 4.2499999999999999e-01 2.8674517218617801e-01 -1.7247173634562643e+01 1.1510526978509290e+00 0 1 0 -331 1 1 1.5750000000000000e+00 5.0954822332712713e+00 -1.1959202907500007e+01 -3.2689027687062833e-03 0 1 0 -332 1 2 2.1000000000000001e+00 6.6817149298760725e-01 -1.3551259854316488e+01 2.7539499370113383e+00 0 1 0 -333 1 2 2.1000000000000001e+00 7.4779844304577914e-01 -1.0596215429331199e+01 2.7618213942130811e+00 0 1 0 -334 1 3 -1.0500000000000000e+00 5.2719372455953284e-01 -1.3480064946544015e+01 1.1357459580603049e+00 0 1 0 -335 1 3 -1.0500000000000000e+00 8.8247034442780148e-01 -1.0689208474290135e+01 1.1370185048637591e+00 0 1 0 -336 1 4 -9.4999999999999996e-01 3.4017986335829793e+00 -1.1885434663339312e+01 1.0069865452366784e+00 0 1 0 -337 1 3 -1.0500000000000000e+00 2.0525899417440829e+00 -9.8934041355699218e+00 3.2357693155049283e+00 0 1 0 -338 1 3 -1.0500000000000000e+00 6.3569045021168691e-01 -1.2070565156037954e+01 3.2374462468578749e+00 0 1 0 -339 1 3 -1.0500000000000000e+00 -5.1770556733988826e-01 -9.7445696176577137e+00 3.0459634759630063e+00 0 1 0 -340 1 5 4.2499999999999999e-01 2.8948985199675210e+00 -1.2764300231969152e+01 1.1507672489080729e+00 0 1 0 -341 1 1 1.5750000000000000e+00 2.5034344422152106e+00 -1.0455505379844039e+01 -3.7523281344018500e-03 0 1 0 -342 1 2 2.1000000000000001e+00 1.7144048897053548e+00 -1.7829278827200582e+01 -2.7609375642366514e+00 0 1 0 -343 1 2 2.1000000000000001e+00 1.6910990452522778e+00 -1.1818431965120766e+01 -2.7688521544223557e+00 0 1 0 -344 1 3 -1.0500000000000000e+00 1.9119323284819547e+00 -8.9344496581989912e+00 -1.1428177382481586e+00 0 1 0 -345 1 3 -1.0500000000000000e+00 1.5563892707225317e+00 -1.1725703690663577e+01 -1.1439423397491009e+00 0 1 0 -346 1 4 -9.4999999999999996e-01 4.1971837724422230e+00 -1.0529223437421583e+01 -1.0140304498512833e+00 0 1 0 -347 1 3 -1.0500000000000000e+00 5.5462795339137028e+00 -1.2521387157719200e+01 -3.2425682558207303e+00 0 1 0 -348 1 3 -1.0500000000000000e+00 1.8034351269669298e+00 -1.0344148858506074e+01 -3.2446357157915351e+00 0 1 0 -349 1 3 -1.0500000000000000e+00 2.9565987372624374e+00 -1.2670221018382025e+01 -3.0529474743907752e+00 0 1 0 -350 1 5 4.2499999999999999e-01 4.7040264803948517e+00 -9.6504758402318025e+00 -1.1578593135248099e+00 0 1 0 -351 1 1 1.5750000000000000e+00 -1.0471558842234785e-01 -1.4938400364160536e+01 -3.7532330236462741e-03 0 1 0 -352 1 2 2.1000000000000001e+00 4.3225692029115947e+00 -1.3346363976966739e+01 -2.7609380283517364e+00 0 1 0 -353 1 2 2.1000000000000001e+00 4.2429709170616423e+00 -1.6301375789705347e+01 -2.7688688655989422e+00 0 1 0 -354 1 3 -1.0500000000000000e+00 4.4637558178452430e+00 -1.3417379075377282e+01 -1.1428141432543626e+00 0 1 0 -355 1 3 -1.0500000000000000e+00 4.1082428565530851e+00 -1.6208643768110964e+01 -1.1439575855412993e+00 0 1 0 -356 1 4 -9.4999999999999996e-01 1.5890483233411512e+00 -1.5012116338313390e+01 -1.0140187450303042e+00 0 1 0 -357 1 3 -1.0500000000000000e+00 2.9381893438004383e+00 -1.7004282362058166e+01 -3.2425562113891360e+00 0 1 0 -358 1 3 -1.0500000000000000e+00 4.3552644383759880e+00 -1.4827036483227031e+01 -3.2446472860660487e+00 0 1 0 -359 1 3 -1.0500000000000000e+00 5.5084200493482207e+00 -1.7153088410673128e+01 -3.0529633116519523e+00 0 1 0 -360 1 5 4.2499999999999999e-01 2.0958519709362449e+00 -1.4133378407095304e+01 -1.1577903583398630e+00 0 1 0 -361 1 1 1.5750000000000000e+00 7.6473300699624751e+00 -1.6442095286891750e+01 -3.2703635846598189e-03 0 1 0 -362 1 2 2.1000000000000001e+00 -1.2203692618505173e+01 -9.0683357616495428e+00 2.7539509521771546e+00 1 1 0 -363 1 2 2.1000000000000001e+00 -1.2180359496617088e+01 -1.5079150525191121e+01 2.7618176073756171e+00 1 1 0 -364 1 3 -1.0500000000000000e+00 8.2390082609686672e+00 -1.7962998453343072e+01 1.1357656996613876e+00 0 1 0 -365 1 3 -1.0500000000000000e+00 -1.2045675817277496e+01 -1.5172133958248716e+01 1.1369942170488319e+00 1 1 0 -366 1 4 -9.4999999999999996e-01 5.9536694527401863e+00 -1.6368316601029903e+01 1.0070080197153732e+00 0 1 0 -367 1 3 -1.0500000000000000e+00 4.6044999502781359e+00 -1.4376299637797334e+01 3.2357807367951334e+00 0 1 0 -368 1 3 -1.0500000000000000e+00 -1.2292484484628849e+01 -1.6553497884316052e+01 3.2374506520275279e+00 1 1 0 -369 1 3 -1.0500000000000000e+00 7.1941227993251466e+00 -1.4227441518171391e+01 3.0459466724115387e+00 0 1 0 -370 1 5 4.2499999999999999e-01 5.4467316809031381e+00 -1.7247181780075401e+01 1.1509108228447804e+00 0 1 0 -371 1 1 1.5750000000000000e+00 -1.0384509878063577e+01 -1.1959202847679125e+01 -3.2711064931607581e-03 1 1 0 -372 1 2 2.1000000000000001e+00 5.8281658705053552e+00 -1.3551245908771351e+01 2.7539592284405323e+00 0 1 0 -373 1 2 2.1000000000000001e+00 5.9077738247842859e+00 -1.0596222346036228e+01 2.7618212472175525e+00 0 1 0 -374 1 3 -1.0500000000000000e+00 5.6872004561341356e+00 -1.3480068502065599e+01 1.1357317122361454e+00 0 1 0 -375 1 3 -1.0500000000000000e+00 6.0424675752151202e+00 -1.0689198215110313e+01 1.1370119790707562e+00 0 1 0 -376 1 4 -9.4999999999999996e-01 -1.2078208028614750e+01 -1.1885444143137754e+01 1.0070061177644991e+00 1 1 0 -377 1 3 -1.0500000000000000e+00 7.2126361390250970e+00 -9.8933805239822377e+00 3.2357588745576784e+00 0 1 0 -378 1 3 -1.0500000000000000e+00 5.7956884111784390e+00 -1.2070595254996373e+01 3.2374511312518859e+00 0 1 0 -379 1 3 -1.0500000000000000e+00 4.6422879410256392e+00 -9.7445663758382555e+00 3.0459651484494685e+00 0 1 0 -380 1 5 4.2499999999999999e-01 8.0549327596562676e+00 -1.2764265216608282e+01 1.1508699292293620e+00 0 1 0 -381 1 1 1.5750000000000000e+00 7.6634431466777784e+00 -1.0455505708640878e+01 -3.7548033538037373e-03 0 1 0 -382 1 2 2.1000000000000001e+00 6.8744083301107217e+00 -1.7829293052197659e+01 -2.7609466521728914e+00 0 1 0 -383 1 2 2.1000000000000001e+00 6.8511227655710272e+00 -1.1818423654764077e+01 -2.7688514413745127e+00 0 1 0 -384 1 3 -1.0500000000000000e+00 7.0719252612919341e+00 -8.9344460915840020e+00 -1.1428009362290084e+00 0 1 0 -385 1 3 -1.0500000000000000e+00 6.7163919273608208e+00 -1.1725714227503657e+01 -1.1439356358725981e+00 0 1 0 -386 1 4 -9.4999999999999996e-01 -1.1282825542264309e+01 -1.0529236312045516e+01 -1.0140115555323934e+00 1 1 0 -387 1 3 -1.0500000000000000e+00 -9.9336734334620846e+00 -1.2521362616099530e+01 -3.2425798517804285e+00 1 1 0 -388 1 3 -1.0500000000000000e+00 6.9634372372059339e+00 -1.0344120541373844e+01 -3.2446411197658893e+00 0 1 0 -389 1 3 -1.0500000000000000e+00 8.1166063278059539e+00 -1.2670225192721546e+01 -3.0529487788076324e+00 0 1 0 -390 1 5 4.2499999999999999e-01 -1.0775939522237591e+01 -9.6504409144075929e+00 -1.1577656550448125e+00 1 1 0 -391 1 1 1.5750000000000000e+00 5.0552705213441183e+00 -1.4938390885101004e+01 -3.7556755525152141e-03 0 1 0 -392 1 2 2.1000000000000001e+00 -1.1157454505789488e+01 -1.3346365259602514e+01 -2.7609477580902304e+00 1 1 0 -393 1 2 2.1000000000000001e+00 -1.1237035180060985e+01 -1.6301363128204752e+01 -2.7688574109066444e+00 1 1 0 -394 1 3 -1.0500000000000000e+00 -1.1016258515593059e+01 -1.3417381597335801e+01 -1.1427868565141956e+00 1 1 0 -395 1 3 -1.0500000000000000e+00 -1.1371754569044754e+01 -1.6208638414131233e+01 -1.1439607996348045e+00 1 1 0 -396 1 4 -9.4999999999999996e-01 6.7490784560440211e+00 -1.5012076202167444e+01 -1.0140027578594140e+00 0 1 0 -397 1 3 -1.0500000000000000e+00 8.0981866850365734e+00 -1.7004283665969044e+01 -3.2425625386787402e+00 0 1 0 -398 1 3 -1.0500000000000000e+00 -1.1124739912558308e+01 -1.4827054551632816e+01 -3.2446301023047264e+00 1 1 0 -399 1 3 -1.0500000000000000e+00 -9.9715680099552539e+00 -1.7153093163802694e+01 -3.0529662797145232e+00 1 1 0 -400 1 5 4.2499999999999999e-01 7.2558685014983197e+00 -1.4133366923834902e+01 -1.1576429969303437e+00 0 1 0 -401 1 1 1.5750000000000000e+00 -7.8326838930931588e+00 -1.6442085371931373e+01 -3.2732823819223711e-03 1 1 0 -402 1 2 2.1000000000000001e+00 -7.0436687618137706e+00 -9.0683330137854199e+00 2.7539612939828899e+00 1 1 0 -403 1 2 2.1000000000000001e+00 -7.0203552020415820e+00 -1.5079163689271947e+01 2.7618054084239976e+00 1 1 0 -404 1 3 -1.0500000000000000e+00 -7.2409778107259708e+00 -1.7962996502787455e+01 1.1357372000778803e+00 1 1 0 -405 1 3 -1.0500000000000000e+00 -6.8856791733648324e+00 -1.5172137885807103e+01 1.1369963196178823e+00 1 1 0 -406 1 4 -9.4999999999999996e-01 -9.5262993670970957e+00 -1.6368274059361951e+01 1.0070224743079983e+00 1 1 0 -407 1 3 -1.0500000000000000e+00 -1.0875507123551024e+01 -1.4376303441124719e+01 3.2357750518877921e+00 1 1 0 -408 1 3 -1.0500000000000000e+00 -7.1324812203291952e+00 -1.6553483309654993e+01 3.2374341141377876e+00 1 1 0 -409 1 3 -1.0500000000000000e+00 -8.2858895255051763e+00 -1.4227436498303321e+01 3.0459500397149455e+00 1 1 0 -410 1 5 4.2499999999999999e-01 -1.0033255030691558e+01 -1.7247173624416206e+01 1.1510522172719124e+00 1 1 0 -411 1 1 1.5750000000000000e+00 -5.2245178771227252e+00 -1.1959202758526017e+01 -3.2686596132034396e-03 1 1 0 -412 1 2 2.1000000000000001e+00 -9.6518281984812564e+00 -1.3551260332238776e+01 2.7539503258911147e+00 1 1 0 -413 1 2 2.1000000000000001e+00 -9.5722019707608350e+00 -1.0596214958141957e+01 2.7618209577011115e+00 1 1 0 -414 1 3 -1.0500000000000000e+00 -9.7928062006518743e+00 -1.3480064638111777e+01 1.1357461718012836e+00 1 1 0 -415 1 3 -1.0500000000000000e+00 -9.4375295209633592e+00 -1.0689208902166378e+01 1.1370191291599561e+00 1 1 0 -416 1 4 -9.4999999999999996e-01 -6.9182015257625187e+00 -1.1885434141823438e+01 1.0069865076337194e+00 1 1 0 -417 1 3 -1.0500000000000000e+00 -8.2674096516605591e+00 -9.8934036664961500e+00 3.2357692780579193e+00 1 1 0 -418 1 3 -1.0500000000000000e+00 -9.6843094688930229e+00 -1.2070565339252298e+01 3.2374457243298291e+00 1 1 0 -419 1 3 -1.0500000000000000e+00 -1.0837706493502655e+01 -9.7445692971577635e+00 3.0459634859134095e+00 1 1 0 -420 1 5 4.2499999999999999e-01 -7.4251012846331328e+00 -1.2764299901878989e+01 1.1507685978831965e+00 1 1 0 -421 1 1 1.5750000000000000e+00 -7.8165653710802498e+00 -1.0455505239212570e+01 -3.7526247607146956e-03 1 1 0 -422 1 2 2.1000000000000001e+00 -8.6055950659194451e+00 -1.7829279483493142e+01 -2.7609372605772293e+00 1 1 0 -423 1 2 2.1000000000000001e+00 -8.6289008782796017e+00 -1.1818431879204407e+01 -2.7688521292950892e+00 1 1 0 -424 1 3 -1.0500000000000000e+00 -8.4080675550031589e+00 -8.9344488405749729e+00 -1.1428176752375947e+00 1 1 0 -425 1 3 -1.0500000000000000e+00 -8.7636107163403185e+00 -1.1725704386940036e+01 -1.1439420773679156e+00 1 1 0 -426 1 4 -9.4999999999999996e-01 -6.1228163916760510e+00 -1.0529224175237184e+01 -1.0140309851989482e+00 1 1 0 -427 1 3 -1.0500000000000000e+00 -4.7737195815235491e+00 -1.2521386711486187e+01 -3.2425678127633280e+00 1 1 0 -428 1 3 -1.0500000000000000e+00 -8.5165649126724521e+00 -1.0344149018842892e+01 -3.2446367291315585e+00 1 1 0 -429 1 3 -1.0500000000000000e+00 -7.3634018650483775e+00 -1.2670221015317008e+01 -3.0529473079508476e+00 1 1 0 -430 1 5 4.2499999999999999e-01 -5.6159739075346202e+00 -9.6504763623609211e+00 -1.1578626433938446e+00 1 1 0 -431 1 1 1.5750000000000000e+00 -1.0424715562756791e+01 -1.4938400553082197e+01 -3.7529812015879571e-03 1 1 0 -432 1 2 2.1000000000000001e+00 -5.9974311083098977e+00 -1.3346363613537441e+01 -2.7609379380798256e+00 1 1 0 -433 1 2 2.1000000000000001e+00 -6.0770293492200240e+00 -1.6301376203040785e+01 -2.7688686473689419e+00 1 1 0 -434 1 3 -1.0500000000000000e+00 -5.8562441201571032e+00 -1.3417379284137553e+01 -1.1428148565171732e+00 1 1 0 -435 1 3 -1.0500000000000000e+00 -6.2117573594784972e+00 -1.6208643250903787e+01 -1.1439581431602814e+00 1 1 0 -436 1 4 -9.4999999999999996e-01 -8.7309519339587123e+00 -1.5012116780663984e+01 -1.0140186145754715e+00 1 1 0 -437 1 3 -1.0500000000000000e+00 -7.3818111353884994e+00 -1.7004282131950792e+01 -3.2425561129342411e+00 1 1 0 -438 1 3 -1.0500000000000000e+00 -5.9647356400559168e+00 -1.4827036902064780e+01 -3.2446470576896891e+00 1 1 0 -439 1 3 -1.0500000000000000e+00 -4.8115794831903598e+00 -1.7153088674562756e+01 -3.0529634737335600e+00 1 1 0 -440 1 5 4.2499999999999999e-01 -8.2241476630143868e+00 -1.4133377854348987e+01 -1.1577899116091555e+00 1 1 0 -441 1 1 1.5750000000000000e+00 -2.6726699900750228e+00 -1.6442095184071334e+01 -3.2708152303708715e-03 1 1 0 -442 1 2 2.1000000000000001e+00 -1.8836926728028871e+00 -9.0683360706481988e+00 2.7539509370400470e+00 1 1 0 -443 1 2 2.1000000000000001e+00 -1.8603595713516334e+00 -1.5079150161517486e+01 2.7618175803065235e+00 1 1 0 -444 1 3 -1.0500000000000000e+00 -2.0809915940246260e+00 -1.7962997831100317e+01 1.1357659249768037e+00 1 1 0 -445 1 3 -1.0500000000000000e+00 -1.7256757375340950e+00 -1.5172134571584408e+01 1.1369946994743039e+00 1 1 0 -446 1 4 -9.4999999999999996e-01 -4.3663303959113087e+00 -1.6368316840061311e+01 1.0070078045087598e+00 1 1 0 -447 1 3 -1.0500000000000000e+00 -5.7154995299878735e+00 -1.4376299278351169e+01 3.2357812249751525e+00 1 1 0 -448 1 3 -1.0500000000000000e+00 -1.9724845002142448e+00 -1.6553498331239307e+01 3.2374496312659460e+00 1 1 0 -449 1 3 -1.0500000000000000e+00 -3.1258773791373198e+00 -1.4227441660105004e+01 3.0459467420251194e+00 1 1 0 -450 1 5 4.2499999999999999e-01 -4.8732684553684695e+00 -1.7247182150638707e+01 1.1509092295157224e+00 1 1 0 -451 1 1 1.5750000000000000e+00 -6.4509876999837346e-02 -1.1959203109449142e+01 -3.2708769273028793e-03 1 1 0 -452 1 2 2.1000000000000001e+00 -4.4918347776990135e+00 -1.3551245280844766e+01 2.7539590818090893e+00 1 1 0 -453 1 2 2.1000000000000001e+00 -4.4122264005535730e+00 -1.0596223265618471e+01 2.7618213123579540e+00 1 1 0 -454 1 3 -1.0500000000000000e+00 -4.6327993770775722e+00 -1.3480068776200241e+01 1.1357306843562007e+00 1 1 0 -455 1 3 -1.0500000000000000e+00 -4.2775323301208692e+00 -1.0689197415521184e+01 1.1370113492700824e+00 1 1 0 -456 1 4 -9.4999999999999996e-01 -1.7582085114615111e+00 -1.1885444828152252e+01 1.0070062173292218e+00 1 1 0 -457 1 3 -1.0500000000000000e+00 -3.1073643655971166e+00 -9.8933800969646732e+00 3.2357583287275880e+00 1 1 0 -458 1 3 -1.0500000000000000e+00 -4.5243114909096018e+00 -1.2070595204559629e+01 3.2374516197245775e+00 1 1 0 -459 1 3 -1.0500000000000000e+00 -5.6777118011680381e+00 -9.7445664568842254e+00 3.0459653648294243e+00 1 1 0 -460 1 5 4.2499999999999999e-01 -2.2650668296403058e+00 -1.2764264287005354e+01 1.1508705187769799e+00 1 1 0 -461 1 1 1.5750000000000000e+00 -2.6565565918664173e+00 -1.0455505879441525e+01 -3.7549348428154161e-03 1 1 0 -462 1 2 2.1000000000000001e+00 -3.4455908391566643e+00 -1.7829293092612534e+01 -2.7609464806422892e+00 1 1 0 -463 1 2 2.1000000000000001e+00 -3.4688767904129696e+00 -1.1818423511849518e+01 -2.7688516686671711e+00 1 1 0 -464 1 3 -1.0500000000000000e+00 -3.2480752505918087e+00 -8.9344459968549881e+00 -1.1428004913510801e+00 1 1 0 -465 1 3 -1.0500000000000000e+00 -3.6036086619536984e+00 -1.1725714425851610e+01 -1.1439355502562485e+00 1 1 0 -466 1 4 -9.4999999999999996e-01 -9.6282526002298496e-01 -1.0529236900614441e+01 -1.0140115755842860e+00 1 1 0 -467 1 3 -1.0500000000000000e+00 3.8632613444500308e-01 -1.2521362577138564e+01 -3.2425797939291590e+00 1 1 0 -468 1 3 -1.0500000000000000e+00 -3.3565633803144257e+00 -1.0344120627751945e+01 -3.2446417136325554e+00 1 1 0 -469 1 3 -1.0500000000000000e+00 -2.2033940096388758e+00 -1.2670225163556584e+01 -3.0529490801396841e+00 1 1 0 -470 1 5 4.2499999999999999e-01 -4.5593933850753388e-01 -9.6504406470385522e+00 -1.1577660591579200e+00 1 1 0 -471 1 1 1.5750000000000000e+00 -5.2647294099161668e+00 -1.4938390849710533e+01 -3.7554421477974387e-03 1 1 0 -472 1 2 2.1000000000000001e+00 -8.3745420972569029e-01 -1.3346365774570319e+01 -2.7609475596234478e+00 1 1 0 -473 1 2 2.1000000000000001e+00 -9.1703537310051608e-01 -1.6301362578024104e+01 -2.7688573923577673e+00 1 1 0 -474 1 3 -1.0500000000000000e+00 -6.9625852330998939e-01 -1.3417381412652670e+01 -1.1427865734610130e+00 1 1 0 -475 1 3 -1.0500000000000000e+00 -1.0517542431933027e+00 -1.6208638871829955e+01 -1.1439604905627423e+00 1 1 0 -476 1 4 -9.4999999999999996e-01 -3.5709214848472897e+00 -1.5012075671946061e+01 -1.0140027621155667e+00 1 1 0 -477 1 3 -1.0500000000000000e+00 -2.2218134642601797e+00 -1.7004283740992047e+01 -3.2425628740632551e+00 1 1 0 -478 1 3 -1.0500000000000000e+00 -8.0473984685049871e-01 -1.4827054452774018e+01 -3.2446303282137938e+00 1 1 0 -479 1 3 -1.0500000000000000e+00 3.4843154859986214e-01 -1.7153093111175764e+01 -3.0529662393152108e+00 1 1 0 -480 1 5 4.2499999999999999e-01 -3.0641314229533227e+00 -1.4133366897183697e+01 -1.1576420001890888e+00 1 1 0 -481 1 1 1.5750000000000000e+00 2.5436575698973183e+00 -7.4762807567511658e+00 -3.2681608649411942e-03 0 1 0 -482 1 2 2.1000000000000001e+00 3.3326771644182021e+00 -1.0251055584260627e-01 2.7539562495680912e+00 0 1 0 -483 1 2 2.1000000000000001e+00 3.3559732863378500e+00 -6.1133477231204605e+00 2.7618019815700912e+00 0 1 0 -484 1 3 -1.0500000000000000e+00 3.1353688973965141e+00 -8.9971784534458905e+00 1.1357161192366334e+00 0 1 0 -485 1 3 -1.0500000000000000e+00 3.4906521933711527e+00 -6.2063093440944606e+00 1.1370013682474660e+00 0 1 0 -486 1 4 -9.4999999999999996e-01 8.4999416594662414e-01 -7.4025141664959477e+00 1.0069772303784212e+00 0 1 0 -487 1 3 -1.0500000000000000e+00 -4.9918531211513084e-01 -5.4104789806646600e+00 3.2357748645072224e+00 0 1 0 -488 1 3 -1.0500000000000000e+00 3.2438478921430658e+00 -7.5876613926832288e+00 3.2374650513379368e+00 0 1 0 -489 1 3 -1.0500000000000000e+00 2.0904479338721824e+00 -5.2616143373361730e+00 3.0459494998857473e+00 0 1 0 -490 1 5 4.2499999999999999e-01 3.4305735839570417e-01 -8.2814451510232985e+00 1.1506997919603279e+00 0 1 0 -491 1 1 1.5750000000000000e+00 5.1518162806579664e+00 -2.9933577707057264e+00 -3.2719737650719338e-03 0 1 0 -492 1 2 2.1000000000000001e+00 7.2450056662052376e-01 -4.5854449159198332e+00 2.7539595529080376e+00 0 1 0 -493 1 2 2.1000000000000001e+00 8.0414918868178020e-01 -1.6304050196450568e+00 2.7618170330682865e+00 0 1 0 -494 1 3 -1.0500000000000000e+00 5.8351556926552206e-01 -4.5142509399890525e+00 1.1357618537192042e+00 0 1 0 -495 1 3 -1.0500000000000000e+00 9.3881877218889187e-01 -1.7234004054205592e+00 1.1370055641638572e+00 0 1 0 -496 1 4 -9.4999999999999996e-01 3.4581831713214264e+00 -2.9195538038595128e+00 1.0070161949531542e+00 0 1 0 -497 1 3 -1.0500000000000000e+00 2.1089597637650321e+00 -9.2758109333887262e-01 3.2357778931579837e+00 0 1 0 -498 1 3 -1.0500000000000000e+00 6.9202493621660821e-01 -3.1047407556425863e+00 3.2374286426884460e+00 0 1 0 -499 1 3 -1.0500000000000000e+00 -4.6137640541853386e-01 -7.7872267536189810e-01 3.0459549651529940e+00 0 1 0 -500 1 5 4.2499999999999999e-01 2.9512366629883733e+00 -3.7984283160022585e+00 1.1510066071998590e+00 0 1 0 -501 1 1 1.5750000000000000e+00 2.5597764890479056e+00 -1.4896728825261896e+00 -3.7510557474558226e-03 0 1 0 -502 1 2 2.1000000000000001e+00 1.7707367658906890e+00 -8.8634435099187865e+00 -2.7609471549488367e+00 0 1 0 -503 1 2 2.1000000000000001e+00 1.7474499480691374e+00 -2.8526347731487149e+00 -2.7688625207492397e+00 0 1 0 -504 1 3 -1.0500000000000000e+00 1.9682617548841872e+00 3.1354766712723858e-02 -1.1428194347213658e+00 0 1 0 -505 1 3 -1.0500000000000000e+00 1.6127391948302812e+00 -2.7598942364919772e+00 -1.1439556954086907e+00 0 1 0 -506 1 4 -9.4999999999999996e-01 4.2535424365009327e+00 -1.5633894774555195e+00 -1.0140400418681299e+00 0 1 0 -507 1 3 -1.0500000000000000e+00 5.6026491085120664e+00 -3.5555609449644461e+00 -3.2425576785307024e+00 0 1 0 -508 1 3 -1.0500000000000000e+00 1.8597625543053109e+00 -1.3783257788357659e+00 -3.2446235882093193e+00 0 1 0 -509 1 3 -1.0500000000000000e+00 3.0129281343278276e+00 -3.7043645719831666e+00 -3.0529605899078920e+00 0 1 0 -510 1 5 4.2499999999999999e-01 4.7603403166335863e+00 -6.8469223618015818e-01 -1.1579216441180478e+00 0 1 0 -511 1 1 1.5750000000000000e+00 -4.8390666169016328e-02 -5.9725694760793182e+00 -3.7574054098818976e-03 0 1 0 -512 1 2 2.1000000000000001e+00 4.3789145761620833e+00 -4.3805514632713773e+00 -2.7609339008297651e+00 0 1 0 -513 1 2 2.1000000000000001e+00 4.2992990506123103e+00 -7.3355316180595338e+00 -2.7688607932569447e+00 0 1 0 -514 1 3 -1.0500000000000000e+00 4.5200860118647945e+00 -4.4515414547653052e+00 -1.1427984036885626e+00 0 1 0 -515 1 3 -1.0500000000000000e+00 4.1645706838576562e+00 -7.2428167611125112e+00 -1.1439527343219709e+00 0 1 0 -516 1 4 -9.4999999999999996e-01 1.6453970630033830e+00 -6.0462561133443451e+00 -1.0139809143366438e+00 0 1 0 -517 1 3 -1.0500000000000000e+00 2.9945122334512657e+00 -8.0384625674130401e+00 -3.2425618392344440e+00 0 1 0 -518 1 3 -1.0500000000000000e+00 4.4116077983548383e+00 -5.8612132939321402e+00 -3.2446764319665711e+00 0 1 0 -519 1 3 -1.0500000000000000e+00 5.5647564535540042e+00 -8.1872858674545341e+00 -3.0529539697099937e+00 0 1 0 -520 1 5 4.2499999999999999e-01 2.1522145539965809e+00 -5.1674642083276847e+00 -1.1575048043055780e+00 0 1 0 -521 1 1 1.5750000000000000e+00 7.7036718214034785e+00 -7.4762910233615827e+00 -3.2653078128408453e-03 0 1 0 -522 1 2 2.1000000000000001e+00 -1.2147345593282450e+01 -1.0251359496978196e-01 2.7539461566052985e+00 1 1 0 -523 1 2 2.1000000000000001e+00 -1.2124030375241905e+01 -6.1133337616709902e+00 2.7618143580363075e+00 1 1 0 -524 1 3 -1.0500000000000000e+00 8.2953539893182153e+00 -8.9971801103008087e+00 1.1357452560675760e+00 0 1 0 -525 1 3 -1.0500000000000000e+00 -1.1989344710863094e+01 -6.2063052996612100e+00 1.1369992978851666e+00 1 1 0 -526 1 4 -9.4999999999999996e-01 6.0099622042933412e+00 -7.4025579776391464e+00 1.0069620216105442e+00 0 1 0 -527 1 3 -1.0500000000000000e+00 4.6608198109043748e+00 -5.4104755502326540e+00 3.2357801936372788e+00 0 1 0 -528 1 3 -1.0500000000000000e+00 -1.2236155841755377e+01 -7.5876771945797863e+00 3.2374811955831060e+00 1 1 0 -529 1 3 -1.0500000000000000e+00 7.2504604436044637e+00 -5.2616189118410208e+00 3.0459453948467168e+00 0 1 0 -530 1 5 4.2499999999999999e-01 5.5030432428799969e+00 -8.2814548631033578e+00 1.1505519475195651e+00 0 1 0 -531 1 1 1.5750000000000000e+00 -1.0328175895563996e+01 -2.9933575426158416e+00 -3.2744817930581149e-03 1 1 0 -532 1 2 2.1000000000000001e+00 5.8844947594629744e+00 -4.5854310172663979e+00 2.7539684166484335e+00 0 1 0 -533 1 2 2.1000000000000001e+00 5.9641241815446477e+00 -1.6304119441692926e+00 2.7618170105232309e+00 0 1 0 -534 1 3 -1.0500000000000000e+00 5.7435221593356864e+00 -4.5142545324410897e+00 1.1357475728004847e+00 0 1 0 -535 1 3 -1.0500000000000000e+00 6.0988162421821528e+00 -1.7233902325835047e+00 1.1369987706733902e+00 0 1 0 -536 1 4 -9.4999999999999996e-01 -1.2021823199294937e+01 -2.9195626861295096e+00 1.0070363114076599e+00 1 1 0 -537 1 3 -1.0500000000000000e+00 7.2690066204394697e+00 -9.2755674730927851e-01 3.2357668612914487e+00 0 1 0 -538 1 3 -1.0500000000000000e+00 5.8520231027928773e+00 -3.1047706349297179e+00 3.2374344835306346e+00 0 1 0 -539 1 3 -1.0500000000000000e+00 4.6986174638786373e+00 -7.7871980724010115e-01 3.0459566060165137e+00 0 1 0 -540 1 5 4.2499999999999999e-01 8.1112714877724414e+00 -3.7983924620060368e+00 1.1511131184559726e+00 0 1 0 -541 1 1 1.5750000000000000e+00 7.7197852579331645e+00 -1.4896736083571689e+00 -3.7533060240022564e-03 0 1 0 -542 1 2 2.1000000000000001e+00 6.9307402907651401e+00 -8.8634573665418070e+00 -2.7609563100003145e+00 0 1 0 -543 1 2 2.1000000000000001e+00 6.9074738694113726e+00 -2.8526269036832552e+00 -2.7688615983007780e+00 0 1 0 -544 1 3 -1.0500000000000000e+00 7.1282543967930359e+00 3.1358883413265204e-02 -1.1428023230370439e+00 0 1 0 -545 1 3 -1.0500000000000000e+00 6.7727419364633441e+00 -2.7599051276576141e+00 -1.1439490121146996e+00 0 1 0 -546 1 4 -9.4999999999999996e-01 -1.1226466741815884e+01 -1.5634024412045342e+00 -1.0140216083145610e+00 1 1 0 -547 1 3 -1.0500000000000000e+00 -9.8773043947366048e+00 -3.5555368621683208e+00 -3.2425683285331646e+00 1 1 0 -548 1 3 -1.0500000000000000e+00 7.0197645195179135e+00 -1.3782969003001959e+00 -3.2446289771148944e+00 0 1 0 -549 1 3 -1.0500000000000000e+00 8.1729347482067567e+00 -3.7043681527678913e+00 -3.0529625378704122e+00 0 1 0 -550 1 5 4.2499999999999999e-01 -1.0719626421899999e+01 -6.8465756426241953e-01 -1.1578295867776784e+00 1 1 0 -551 1 1 1.5750000000000000e+00 5.1115953590272625e+00 -5.9725597637293362e+00 -3.7601916249858647e-03 0 1 0 -552 1 2 2.1000000000000001e+00 -1.1101108931968247e+01 -4.3805528895496302e+00 -2.7609432872668886e+00 1 1 0 -553 1 2 2.1000000000000001e+00 -1.1180707067380455e+01 -7.3355188643798535e+00 -2.7688492591590554e+00 1 1 0 -554 1 3 -1.0500000000000000e+00 -1.0959928694497696e+01 -4.4515436898974556e+00 -1.1427711527976001e+00 1 1 0 -555 1 3 -1.0500000000000000e+00 -1.1315426415312240e+01 -7.2428113900070752e+00 -1.1439555106248189e+00 1 1 0 -556 1 4 -9.4999999999999996e-01 6.8054277571583519e+00 -6.0462150488380164e+00 -1.0139639895733374e+00 0 1 0 -557 1 3 -1.0500000000000000e+00 8.1545095181602107e+00 -8.0384640840914869e+00 -3.2425680420518717e+00 0 1 0 -558 1 3 -1.0500000000000000e+00 -1.1068396240802272e+01 -5.8612309436472234e+00 -3.2446597028975566e+00 1 1 0 -559 1 3 -1.0500000000000000e+00 -9.9152309072003355e+00 -8.1872904891822973e+00 -3.0529571692262092e+00 1 1 0 -560 1 5 4.2499999999999999e-01 7.3122321320503794e+00 -5.1674508614420542e+00 -1.1573511315660259e+00 0 1 0 -561 1 1 1.5750000000000000e+00 -7.7763421850555563e+00 -7.4762809230994502e+00 -3.2682934864940449e-03 1 1 0 -562 1 2 2.1000000000000001e+00 -6.9873219863206382e+00 -1.0251132671459473e-01 2.7539563597780923e+00 1 1 0 -563 1 2 2.1000000000000001e+00 -6.9640259840326628e+00 -6.1133472592188820e+00 2.7618022956295629e+00 1 1 0 -564 1 3 -1.0500000000000000e+00 -7.1846314193998664e+00 -8.9971779600544295e+00 1.1357163882947159e+00 1 1 0 -565 1 3 -1.0500000000000000e+00 -6.8293485880591112e+00 -6.2063096894111425e+00 1.1370012333877977e+00 1 1 0 -566 1 4 -9.4999999999999996e-01 -9.4700055755924701e+00 -7.4025139876644594e+00 1.0069773362883989e+00 1 1 0 -567 1 3 -1.0500000000000000e+00 -1.0819186074870123e+01 -5.4104790908412692e+00 3.2357742683187052e+00 1 1 0 -568 1 3 -1.0500000000000000e+00 -7.0761528036457531e+00 -7.5876609775127299e+00 3.2374644576831670e+00 1 1 0 -569 1 3 -1.0500000000000000e+00 -8.2295514763541018e+00 -5.2616148976108672e+00 3.0459489432022515e+00 1 1 0 -570 1 5 4.2499999999999999e-01 -9.9769424641830984e+00 -8.2814450142459481e+00 1.1507004566801982e+00 1 1 0 -571 1 1 1.5750000000000000e+00 -5.1681838127975688e+00 -2.9933576384022018e+00 -3.2717369706762867e-03 1 1 0 -572 1 2 2.1000000000000001e+00 -9.5954996993209818e+00 -4.5854449940775464e+00 2.7539595842160942e+00 1 1 0 -573 1 2 2.1000000000000001e+00 -9.5158507875170635e+00 -1.6304043820163336e+00 2.7618168255845852e+00 1 1 0 -574 1 3 -1.0500000000000000e+00 -9.7364845194737679e+00 -4.5142510709812989e+00 1.1357620952221374e+00 1 1 0 -575 1 3 -1.0500000000000000e+00 -9.3811811495596569e+00 -1.7234005945692772e+00 1.1370052607441838e+00 1 1 0 -576 1 4 -9.4999999999999996e-01 -6.8618165785256515e+00 -2.9195528214605435e+00 1.0070162671680265e+00 1 1 0 -577 1 3 -1.0500000000000000e+00 -8.2110400227350908e+00 -9.2758055861539646e-01 3.2357776111175447e+00 1 1 0 -578 1 3 -1.0500000000000000e+00 -9.6279751712075452e+00 -3.1047410652821092e+00 3.2374288782542173e+00 1 1 0 -579 1 3 -1.0500000000000000e+00 -1.0781377378976435e+01 -7.7872238917679937e-01 3.0459548222147177e+00 1 1 0 -580 1 5 4.2499999999999999e-01 -7.3687631344134621e+00 -3.7984280683325675e+00 1.1510087811947294e+00 1 1 0 -581 1 1 1.5750000000000000e+00 -7.7602231493103115e+00 -1.4896729773895174e+00 -3.7514049036762032e-03 1 1 0 -582 1 2 2.1000000000000001e+00 -8.5492636448235881e+00 -8.8634434759155383e+00 -2.7609469383151142e+00 1 1 0 -583 1 2 2.1000000000000001e+00 -8.5725493910960839e+00 -2.8526345568910330e+00 -2.7688621512718061e+00 1 1 0 -584 1 3 -1.0500000000000000e+00 -8.3517386022975746e+00 3.1355306993937404e-02 -1.1428188664021803e+00 1 1 0 -585 1 3 -1.0500000000000000e+00 -8.7072613660041132e+00 -2.7598948801617009e+00 -1.1439559119420721e+00 1 1 0 -586 1 4 -9.4999999999999996e-01 -6.0664578280469037e+00 -1.5633906786741285e+00 -1.0140407761717736e+00 1 1 0 -587 1 3 -1.0500000000000000e+00 -4.7173504441606600e+00 -3.5555605571846858e+00 -3.2425569990613576e+00 1 1 0 -588 1 3 -1.0500000000000000e+00 -8.4602377735195269e+00 -1.3783256930774925e+00 -3.2446245341017974e+00 1 1 0 -589 1 3 -1.0500000000000000e+00 -7.3070727313816795e+00 -3.7043646163271404e+00 -3.0529607277228852e+00 1 1 0 -590 1 5 4.2499999999999999e-01 -5.5596602828094399e+00 -6.8469307887884057e-01 -1.1579270767641230e+00 1 1 0 -591 1 1 1.5750000000000000e+00 -1.0368390725810965e+01 -5.9725695262430403e+00 -3.7573993430903840e-03 1 1 0 -592 1 2 2.1000000000000001e+00 -5.9410852260952272e+00 -4.3805507393552645e+00 -2.7609332752915812e+00 1 1 0 -593 1 2 2.1000000000000001e+00 -6.0207013841734955e+00 -7.3355318748997771e+00 -2.7688608337867908e+00 1 1 0 -594 1 3 -1.0500000000000000e+00 -5.7999138374145174e+00 -4.4515417632928145e+00 -1.1427994454172925e+00 1 1 0 -595 1 3 -1.0500000000000000e+00 -6.1554296040035208e+00 -7.2428163652707518e+00 -1.1439534513399927e+00 1 1 0 -596 1 4 -9.4999999999999996e-01 -8.6746031854828516e+00 -6.0462567207300957e+00 -1.0139806757747039e+00 1 1 0 -597 1 3 -1.0500000000000000e+00 -7.3254880189215017e+00 -8.0384622206913434e+00 -3.2425615735922868e+00 1 1 0 -598 1 3 -1.0500000000000000e+00 -5.9083924192090498e+00 -5.8612138215664249e+00 -3.2446762232977573e+00 1 1 0 -599 1 3 -1.0500000000000000e+00 -4.7552429311707556e+00 -8.1872863652617180e+00 -3.0529541602386461e+00 1 1 0 -600 1 5 4.2499999999999999e-01 -8.1677849706477055e+00 -5.1674634384285980e+00 -1.1575040236693965e+00 1 1 0 -601 1 1 1.5750000000000000e+00 -2.6163277838675221e+00 -7.4762909455420772e+00 -3.2656709391893912e-03 1 1 0 -602 1 2 2.1000000000000001e+00 -1.8273462383235461e+00 -1.0251424082924032e-01 2.7539460950975592e+00 1 1 0 -603 1 2 2.1000000000000001e+00 -1.8040299652667589e+00 -6.1133335430346563e+00 2.7618141317410618e+00 1 1 0 -604 1 3 -1.0500000000000000e+00 -2.0246462781465269e+00 -8.9971797593687484e+00 1.1357458882768405e+00 1 1 0 -605 1 3 -1.0500000000000000e+00 -1.6693450475600429e+00 -6.2063057861451725e+00 1.1369993464425061e+00 1 1 0 -606 1 4 -9.4999999999999996e-01 -4.3100379988542743e+00 -7.4025589029402692e+00 1.0069615315099991e+00 1 1 0 -607 1 3 -1.0500000000000000e+00 -5.6591796712508087e+00 -5.4104753627400228e+00 3.2357806674433647e+00 1 1 0 -608 1 3 -1.0500000000000000e+00 -1.9161562222272543e+00 -7.5876772803970987e+00 3.2374804258171608e+00 1 1 0 -609 1 3 -1.0500000000000000e+00 -3.0695406692448417e+00 -5.2616188661581340e+00 3.0459453864236909e+00 1 1 0 -610 1 5 4.2499999999999999e-01 -4.8169571538695717e+00 -8.2814553617741105e+00 1.1505481493262373e+00 1 1 0 -611 1 1 1.5750000000000000e+00 -8.1760772968380024e-03 -2.9933575181058600e+00 -3.2743711147666943e-03 1 1 0 -612 1 2 2.1000000000000001e+00 -4.4355051118200439e+00 -4.5854299392358087e+00 2.7539688922472791e+00 1 1 0 -613 1 2 2.1000000000000001e+00 -4.3558760344290208e+00 -1.6304124598534635e+00 2.7618174106720375e+00 1 1 0 -614 1 3 -1.0500000000000000e+00 -4.5764777419565466e+00 -4.5142548759855465e+00 1.1357463260141714e+00 1 1 0 -615 1 3 -1.0500000000000000e+00 -4.2211840342906868e+00 -1.7233895441759479e+00 1.1369977086180825e+00 1 1 0 -616 1 4 -9.4999999999999996e-01 -1.7018236124385560e+00 -2.9195635165858569e+00 1.0070367229964496e+00 1 1 0 -617 1 3 -1.0500000000000000e+00 -3.0509931277018758e+00 -9.2755589773917535e-01 3.2357665495488988e+00 1 1 0 -618 1 3 -1.0500000000000000e+00 -4.4679770346247647e+00 -3.1047713732493420e+00 3.2374347240366337e+00 1 1 0 -619 1 3 -1.0500000000000000e+00 -5.6213822534512161e+00 -7.7872002325319301e-01 3.0459565887809354e+00 1 1 0 -620 1 5 4.2499999999999999e-01 -2.2087277522865865e+00 -3.7983910799744223e+00 1.1511149260038867e+00 1 1 0 -621 1 1 1.5750000000000000e+00 -2.6002141654952915e+00 -1.4896737094533599e+00 -3.7533128050117881e-03 1 1 0 -622 1 2 2.1000000000000001e+00 -3.3892590053672063e+00 -8.8634574857208097e+00 -2.7609561680299493e+00 1 1 0 -623 1 2 2.1000000000000001e+00 -3.4125251930059894e+00 -2.8526268846259306e+00 -2.7688615286285891e+00 1 1 0 -624 1 3 -1.0500000000000000e+00 -3.1917462126351293e+00 3.1358788246325986e-02 -1.1428022026067133e+00 1 1 0 -625 1 3 -1.0500000000000000e+00 -3.5472590403252218e+00 -2.7599052235913142e+00 -1.1439491794483505e+00 1 1 0 -626 1 4 -9.4999999999999996e-01 -9.0646691514089461e-01 -1.5634032584722597e+00 -1.0140215315146257e+00 1 1 0 -627 1 3 -1.0500000000000000e+00 4.4269580016512577e-01 -3.5555364857703218e+00 -3.2425687501749536e+00 1 1 0 -628 1 3 -1.0500000000000000e+00 -3.3002363165323549e+00 -1.3782961327508190e+00 -3.2446289526978092e+00 1 1 0 -629 1 3 -1.0500000000000000e+00 -2.1470649566305173e+00 -3.7043686941940379e+00 -3.0529629239873710e+00 1 1 0 -630 1 5 4.2499999999999999e-01 -3.9962609031332441e-01 -6.8465733157457720e-01 -1.1578307953461699e+00 1 1 0 -631 1 1 1.5750000000000000e+00 -5.2084047404047134e+00 -5.9725595364899284e+00 -3.7600979616296826e-03 1 1 0 -632 1 2 2.1000000000000001e+00 -7.8110919308991456e-01 -4.3805529373242500e+00 -2.7609430555638124e+00 1 1 0 -633 1 2 2.1000000000000001e+00 -8.6070716056939567e-01 -7.3355179831783488e+00 -2.7688491805898519e+00 1 1 0 -634 1 3 -1.0500000000000000e+00 -6.3992886880595101e-01 -4.4515437816825045e+00 -1.1427704613142957e+00 1 1 0 -635 1 3 -1.0500000000000000e+00 -9.9542614064111312e-01 -7.2428116519511683e+00 -1.1439560424635076e+00 1 1 0 -636 1 4 -9.4999999999999996e-01 -3.5145719480547983e+00 -6.0462140630651522e+00 -1.0139637930845495e+00 1 1 0 -637 1 3 -1.0500000000000000e+00 -2.1654899354619133e+00 -8.0384635502983741e+00 -3.2425685873817418e+00 1 1 0 -638 1 3 -1.0500000000000000e+00 -7.4839646312622143e-01 -5.8612317329932946e+00 -3.2446596299773693e+00 1 1 0 -639 1 3 -1.0500000000000000e+00 4.0476806768967499e-01 -8.1872903970557633e+00 -3.0529571025848705e+00 1 1 0 -640 1 5 4.2499999999999999e-01 -3.0077675350384983e+00 -5.1674504112187361e+00 -1.1573483949331340e+00 1 1 0 -641 1 1 1.5750000000000000e+00 9.0327999603982079e-01 1.3078268179656156e+00 9.1936285392393344e+00 0 0 0 -642 1 2 2.1000000000000001e+00 5.0862246908167243e+00 9.0452767941395287e+00 -6.4424047165866876e+00 0 0 1 -643 1 2 2.1000000000000001e+00 5.1094714654176485e+00 3.0344442077888871e+00 -6.4345036943507274e+00 0 0 1 -644 1 3 -1.0500000000000000e+00 4.8887010421652999e+00 1.5046638157599901e-01 -8.0605839637767165e+00 0 0 1 -645 1 3 -1.0500000000000000e+00 5.2441927721705479e+00 2.9417321911529406e+00 -8.0593973812488873e+00 0 0 1 -646 1 4 -9.4999999999999996e-01 2.6033704126610484e+00 1.7451758067380290e+00 -8.1893676020018926e+00 0 0 1 -647 1 3 -1.0500000000000000e+00 1.2542455852135834e+00 3.7373752120048742e+00 -5.9607992485127665e+00 0 0 1 -648 1 3 -1.0500000000000000e+00 4.9971716802184414e+00 1.5601454347120267e+00 -5.9587058378165576e+00 0 0 1 -649 1 3 -1.0500000000000000e+00 3.8440041612115703e+00 3.8861931285291931e+00 -6.1503945630703374e+00 0 0 1 -650 1 5 4.2499999999999999e-01 2.0965664266306856e+00 8.6640571709351022e-01 -8.0457855564755221e+00 0 0 1 -651 1 1 1.5750000000000000e+00 3.5114550282895003e+00 5.7907578289563908e+00 9.1936291799320600e+00 0 0 0 -652 1 2 2.1000000000000001e+00 2.4780321755723715e+00 4.5623640127866523e+00 -6.4424127795872970e+00 0 0 1 -653 1 2 2.1000000000000001e+00 2.5576727978274061e+00 7.5173398896816757e+00 -6.4345086110749499e+00 0 0 1 -654 1 3 -1.0500000000000000e+00 2.3368431861690446e+00 4.6333600597891369e+00 -8.0605492211226508e+00 0 0 1 -655 1 3 -1.0500000000000000e+00 2.6923826235310759e+00 7.4246193194823391e+00 -8.0594146702702183e+00 0 0 1 -656 1 4 -9.4999999999999996e-01 5.2115829731287189e+00 6.2281267873758885e+00 -8.1893654320210807e+00 0 0 1 -657 1 3 -1.0500000000000000e+00 3.8624444953521344e+00 8.2202786364954399e+00 -5.9607758363894661e+00 0 0 1 -658 1 3 -1.0500000000000000e+00 2.4453330767289607e+00 6.0430641736627777e+00 -5.9587080529496408e+00 0 0 1 -659 1 3 -1.0500000000000000e+00 1.2921732996260165e+00 8.3691407396663671e+00 -6.1504119881455370e+00 0 0 1 -660 1 5 4.2499999999999999e-01 4.7046988428178977e+00 5.3493130177618120e+00 -8.0457432166301501e+00 0 0 1 -661 1 1 1.5750000000000000e+00 9.1939897374337853e-01 7.2944681040671533e+00 9.1931409599603455e+00 0 0 0 -662 1 2 2.1000000000000001e+00 1.3038993986497616e-01 -7.9297399937615864e-02 6.4359157158029880e+00 0 0 0 -663 1 2 2.1000000000000001e+00 1.0709548741821351e-01 5.9314795901314916e+00 6.4280530942119469e+00 0 0 0 -664 1 3 -1.0500000000000000e+00 3.2769702380019616e-01 8.8153278361017087e+00 8.0541328072809932e+00 0 0 0 -665 1 3 -1.0500000000000000e+00 -2.7577048002024540e-02 6.0244668918843125e+00 8.0528534019165114e+00 0 0 0 -666 1 4 -9.4999999999999996e-01 2.6131161534111662e+00 7.2207287036538688e+00 8.1828861539669902e+00 0 0 0 -667 1 3 -1.0500000000000000e+00 3.9622586469205352e+00 5.2286377281547125e+00 5.9541122763207728e+00 0 0 0 -668 1 3 -1.0500000000000000e+00 2.1920683743605807e-01 7.4058249891069572e+00 5.9524118713538332e+00 0 0 0 -669 1 3 -1.0500000000000000e+00 1.3725990807979294e+00 5.0798240702830135e+00 6.1439110162023720e+00 0 0 0 -670 1 5 4.2499999999999999e-01 3.1199732920356418e+00 8.0995675675041703e+00 8.0391592725870282e+00 0 0 0 -671 1 1 1.5750000000000000e+00 -1.6887626179515607e+00 2.8115295078010867e+00 9.1931424252045630e+00 0 0 0 -672 1 2 2.1000000000000001e+00 2.7385869519563499e+00 4.4035977810533353e+00 6.4359159181916219e+00 0 0 0 -673 1 2 2.1000000000000001e+00 2.6589194294892575e+00 1.4485953760994725e+00 6.4280582079700412e+00 0 0 0 -674 1 3 -1.0500000000000000e+00 2.8795450934696767e+00 4.3324405226142382e+00 8.0541191847946116e+00 0 0 0 -675 1 3 -1.0500000000000000e+00 2.5242342524813459e+00 1.5415689319063191e+00 8.0528783307282445e+00 0 0 0 -676 1 4 -9.4999999999999996e-01 4.8784689801895098e-03 2.7377462205787744e+00 8.1828769724994572e+00 0 0 0 -677 1 3 -1.0500000000000000e+00 1.3540645414120398e+00 7.4573621369630771e-01 5.9540951763794290e+00 0 0 0 -678 1 3 -1.0500000000000000e+00 2.7710487894445812e+00 2.9229363552675593e+00 5.9524016589185003e+00 0 0 0 -679 1 3 -1.0500000000000000e+00 3.9244363315488222e+00 5.9687068067378490e-01 6.1439283855687208e+00 0 0 0 -680 1 5 4.2499999999999999e-01 5.1182958888196950e-01 3.6166698539281406e+00 8.0390353352204258e+00 0 0 0 -681 1 1 1.5750000000000000e+00 6.0632942270537633e+00 1.3078169876851220e+00 9.1936311772266670e+00 0 0 0 -682 1 2 2.1000000000000001e+00 -1.0393798325235846e+01 9.0452739485367424e+00 -6.4424156068686536e+00 1 0 1 -683 1 2 2.1000000000000001e+00 -1.0370532688021308e+01 3.0344580928070393e+00 -6.4344922061203995e+00 1 0 1 -684 1 3 -1.0500000000000000e+00 1.0048686020539265e+01 1.5046462355047652e-01 -8.0605545916265591e+00 0 0 1 -685 1 3 -1.0500000000000000e+00 -1.0235804675645195e+01 2.9417358723974196e+00 -8.0593999402563039e+00 1 0 1 -686 1 4 -9.4999999999999996e-01 7.7633395990307577e+00 1.7451343070668237e+00 -8.1893820944806723e+00 0 0 1 -687 1 3 -1.0500000000000000e+00 6.4142512718313398e+00 3.7373784423628784e+00 -5.9607944539472024e+00 0 0 1 -688 1 3 -1.0500000000000000e+00 -1.0482832407337161e+01 1.5601299826680055e+00 -5.9586900611972622e+00 1 0 1 -689 1 3 -1.0500000000000000e+00 9.0040166486961724e+00 3.8861879123933356e+00 -6.1503979527282322e+00 0 0 1 -690 1 5 4.2499999999999999e-01 7.2565522149876038e+00 8.6639701182161843e-01 -8.0459259751794452e+00 0 0 1 -691 1 1 1.5750000000000000e+00 -1.1968537431183220e+01 5.7907580951124764e+00 9.1936264813234629e+00 1 0 0 -692 1 2 2.1000000000000001e+00 7.6380264848568480e+00 4.5623772756766172e+00 -6.4424042946661455e+00 0 0 1 -693 1 2 2.1000000000000001e+00 7.7176486578355963e+00 7.5173331294719183e+00 -6.4345083071021740e+00 0 0 1 -694 1 3 -1.0500000000000000e+00 7.4968492694646329e+00 4.6333567441294718e+00 -8.0605633819113667e+00 0 0 1 -695 1 3 -1.0500000000000000e+00 7.8523804276681837e+00 7.4246294275852094e+00 -8.0594213971891069e+00 0 0 1 -696 1 4 -9.4999999999999996e-01 -1.0268423694159754e+01 6.2281174442746980e+00 -8.1893456261947950e+00 1 0 1 -697 1 3 -1.0500000000000000e+00 9.0224907290535974e+00 8.2203025512221188e+00 -5.9607878799717344e+00 0 0 1 -698 1 3 -1.0500000000000000e+00 7.6053307981435196e+00 6.0430347893748895e+00 -5.9587019740450744e+00 0 0 1 -699 1 3 -1.0500000000000000e+00 6.4521656412247061e+00 8.3691447635715939e+00 -6.1504109506876619e+00 0 0 1 -700 1 5 4.2499999999999999e-01 9.8647336224214257e+00 5.3493476067839048e+00 -8.0456403223888646e+00 0 0 1 -701 1 1 1.5750000000000000e+00 6.0794078571688708e+00 7.2944672054170034e+00 9.1931387604710437e+00 0 0 0 -702 1 2 2.1000000000000001e+00 5.2903927590288387e+00 -7.9312034344852123e-02 6.4359061568910381e+00 0 0 0 -703 1 2 2.1000000000000001e+00 5.2671187145264451e+00 5.9314885138761895e+00 6.4280540558924795e+00 0 0 0 -704 1 3 -1.0500000000000000e+00 5.4876891838999047e+00 8.8153320262813786e+00 8.0541505226321348e+00 0 0 0 -705 1 3 -1.0500000000000000e+00 5.1324255766353062e+00 6.0244559010705814e+00 8.0528602469110595e+00 0 0 0 -706 1 4 -9.4999999999999996e-01 -1.2866893134241742e+01 7.2207151463837640e+00 8.1829043644805566e+00 1 0 0 -707 1 3 -1.0500000000000000e+00 -1.1517695582926537e+01 5.2286614618147631e+00 5.9541014619513177e+00 1 0 0 -708 1 3 -1.0500000000000000e+00 5.3792088967617762e+00 7.4058530000867115e+00 5.9524066869292191e+00 0 0 0 -709 1 3 -1.0500000000000000e+00 6.5326055526273592e+00 5.0798202391399414e+00 6.1439090309796480e+00 0 0 0 -710 1 5 4.2499999999999999e-01 -1.2359993555416519e+01 8.0996018552035594e+00 8.0392491603506926e+00 1 0 0 -711 1 1 1.5750000000000000e+00 3.4712234255278887e+00 2.8115394997158880e+00 9.1931391128052944e+00 0 0 0 -712 1 2 2.1000000000000001e+00 -1.2741436473488831e+01 4.4035966072164179e+00 6.4359068114368210e+00 1 0 0 -713 1 2 2.1000000000000001e+00 -1.2821086026369231e+01 1.4486074172151717e+00 6.4280700878493633e+00 1 0 0 -714 1 3 -1.0500000000000000e+00 -1.2600468968641490e+01 4.3324381244139616e+00 8.0541460499971507e+00 1 0 0 -715 1 3 -1.0500000000000000e+00 -1.2955762910609096e+01 1.5415738718932701e+00 8.0528750796115318e+00 1 0 0 -716 1 4 -9.4999999999999996e-01 5.1649091672213814e+00 2.7377878492092051e+00 8.1828935067407187e+00 0 0 0 -717 1 3 -1.0500000000000000e+00 6.5140624838503562e+00 7.4573449160736516e-01 5.9540890408837548e+00 0 0 0 -718 1 3 -1.0500000000000000e+00 -1.2708954990084692e+01 2.9229186261097801e+00 5.9524185176229256e+00 1 0 0 -719 1 3 -1.0500000000000000e+00 -1.1555552596604405e+01 5.9686666841561120e-01 6.1439252012806840e+00 1 0 0 -720 1 5 4.2499999999999999e-01 5.6718460531567594e+00 3.6166822258108873e+00 8.0391876250502783e+00 0 0 0 -721 1 1 1.5750000000000000e+00 -9.4167199060881881e+00 1.3078268191493692e+00 9.1936284569148015e+00 1 0 0 -722 1 2 2.1000000000000001e+00 -5.2337744657239433e+00 9.0452766042384738e+00 -6.4424045702369455e+00 1 0 1 -723 1 2 2.1000000000000001e+00 -5.2105281225591042e+00 3.0344444952698204e+00 -6.4345035089905984e+00 1 0 1 -724 1 3 -1.0500000000000000e+00 -5.4312992074936357e+00 1.5046681141750895e-01 -8.0605839113224356e+00 1 0 1 -725 1 3 -1.0500000000000000e+00 -5.0758078374764404e+00 2.9417318484454675e+00 -8.0593973266164873e+00 1 0 1 -726 1 4 -9.4999999999999996e-01 -7.7166293890810103e+00 1.7451757670354233e+00 -8.1893673368413982e+00 1 0 1 -727 1 3 -1.0500000000000000e+00 -9.0657538566382421e+00 3.7373757159528864e+00 -5.9607990251074803e+00 1 0 1 -728 1 3 -1.0500000000000000e+00 -5.3228288100664747e+00 1.5601455206964303e+00 -5.9587064079961714e+00 1 0 1 -729 1 3 -1.0500000000000000e+00 -6.4759962422743165e+00 3.8861930601011174e+00 -6.1503948993679884e+00 1 0 1 -730 1 5 4.2499999999999999e-01 -8.2234331102634997e+00 8.6640653809297419e-01 -8.0457837360048821e+00 1 0 1 -731 1 1 1.5750000000000000e+00 -6.8085449284088639e+00 5.7907577807183372e+00 9.1936293269548521e+00 1 0 0 -732 1 2 2.1000000000000001e+00 -7.8419680626851189e+00 4.5623634416592758e+00 -6.4424126333628751e+00 1 0 1 -733 1 2 2.1000000000000001e+00 -7.7623275299073544e+00 7.5173406513668297e+00 -6.4345089088641538e+00 1 0 1 -734 1 3 -1.0500000000000000e+00 -7.9831563836472403e+00 4.6333602950605197e+00 -8.0605491278584473e+00 1 0 1 -735 1 3 -1.0500000000000000e+00 -7.6276171712668432e+00 7.4246189825803306e+00 -8.0594145553904770e+00 1 0 1 -736 1 4 -9.4999999999999996e-01 -5.1084172118972777e+00 6.2281269736840663e+00 -8.1893656974078635e+00 1 0 1 -737 1 3 -1.0500000000000000e+00 -6.4575553159047985e+00 8.2202787726173803e+00 -5.9607759162796174e+00 1 0 1 -738 1 3 -1.0500000000000000e+00 -7.8746669235491940e+00 6.0430635684913305e+00 -5.9587081543805747e+00 1 0 1 -739 1 3 -1.0500000000000000e+00 -9.0278274510389611e+00 8.3691408883945471e+00 -6.1504119619142976e+00 1 0 1 -740 1 5 4.2499999999999999e-01 -5.6153013914286491e+00 5.3493123886781433e+00 -8.0457448175864990e+00 1 0 1 -741 1 1 1.5750000000000000e+00 -9.4006010736587182e+00 7.2944682033683108e+00 9.1931405770212429e+00 1 0 0 -742 1 2 2.1000000000000001e+00 -1.0189610402198802e+01 -7.9297605759123257e-02 6.4359160010201411e+00 1 0 0 -743 1 2 2.1000000000000001e+00 -1.0212904137937610e+01 5.9314797115273628e+00 6.4280531027538306e+00 1 0 0 -744 1 3 -1.0500000000000000e+00 -9.9923029717657226e+00 8.8153286205910888e+00 8.0541331394482896e+00 1 0 0 -745 1 3 -1.0500000000000000e+00 -1.0347577370841730e+01 6.0244660873837574e+00 8.0528534336309434e+00 1 0 0 -746 1 4 -9.4999999999999996e-01 -7.7068834885402087e+00 7.2207286744038370e+00 8.1828858968542733e+00 1 0 0 -747 1 3 -1.0500000000000000e+00 -6.3577402971397685e+00 5.2286383860826824e+00 5.9541124938145380e+00 1 0 0 -748 1 3 -1.0500000000000000e+00 -1.0100793183120766e+01 7.4058245081335947e+00 5.9524106541055115e+00 1 0 0 -749 1 3 -1.0500000000000000e+00 -8.9474022065604224e+00 5.0798241454221369e+00 6.1439110956024354e+00 1 0 0 -750 1 5 4.2499999999999999e-01 -7.2000270353828046e+00 8.0995672068003088e+00 8.0391580946381538e+00 1 0 0 -751 1 1 1.5750000000000000e+00 -1.2008762881451348e+01 2.8115295177180428e+00 9.1931425674069622e+00 1 0 0 -752 1 2 2.1000000000000001e+00 -7.5814135479309908e+00 4.4035983789351896e+00 6.4359165483385254e+00 1 0 0 -753 1 2 2.1000000000000001e+00 -7.6610808670997494e+00 1.4485945780712797e+00 6.4280582613292712e+00 1 0 0 -754 1 3 -1.0500000000000000e+00 -7.4404546055668366e+00 4.3324402056750237e+00 8.0541182772999065e+00 1 0 0 -755 1 3 -1.0500000000000000e+00 -7.7957660342353634e+00 1.5415692603738904e+00 8.0528775182214503e+00 1 0 0 -756 1 4 -9.4999999999999996e-01 -1.0315122227353797e+01 2.7377447118390101e+00 8.1828767909309512e+00 1 0 0 -757 1 3 -1.0500000000000000e+00 -8.9659352915333912e+00 7.4573683553695247e-01 5.9540956560325071e+00 1 0 0 -758 1 3 -1.0500000000000000e+00 -7.5489512806955084e+00 2.9229361730432970e+00 5.9524018601671909e+00 1 0 0 -759 1 3 -1.0500000000000000e+00 -6.3955632518685768e+00 5.9687042658164202e-01 6.1439287069185120e+00 1 0 0 -760 1 5 4.2499999999999999e-01 -9.8081700960310911e+00 3.6166702227703560e+00 8.0390328438422074e+00 1 0 0 -761 1 1 1.5750000000000000e+00 -4.2567053371850658e+00 1.3078168179231042e+00 9.1936310535964232e+00 1 0 0 -762 1 2 2.1000000000000001e+00 -7.3798358768387473e-02 9.0452733241225367e+00 -6.4424155682759778e+00 1 0 1 -763 1 2 2.1000000000000001e+00 -5.0532229357781233e-02 3.0344580784693349e+00 -6.4344918700685572e+00 1 0 1 -764 1 3 -1.0500000000000000e+00 -2.7131416236533745e-01 1.5046495859890285e-01 -8.0605544059037157e+00 1 0 1 -765 1 3 -1.0500000000000000e+00 8.4195332462293493e-02 2.9417357818207392e+00 -8.0593997996762017e+00 1 0 1 -766 1 4 -9.4999999999999996e-01 -2.5566610233707436e+00 1.7451330035208805e+00 -8.1893824664560277e+00 1 0 1 -767 1 3 -1.0500000000000000e+00 -3.9057480740250670e+00 3.7373787164792880e+00 -5.9607940658222685e+00 1 0 1 -768 1 3 -1.0500000000000000e+00 -1.6283268367977044e-01 1.5601301436753872e+00 -5.9586903754265217e+00 1 0 1 -769 1 3 -1.0500000000000000e+00 -1.3159834872646030e+00 3.8861877175565063e+00 -6.1503980963921077e+00 1 0 1 -770 1 5 4.2499999999999999e-01 -3.0634476704969611e+00 8.6639700750263771e-01 -8.0459289853123099e+00 1 0 1 -771 1 1 1.5750000000000000e+00 -1.6485375220706722e+00 5.7907579502462809e+00 9.1936265404422031e+00 1 0 0 -772 1 2 2.1000000000000001e+00 -2.6819735576082735e+00 4.5623781023095979e+00 -6.4424040572249073e+00 1 0 1 -773 1 2 2.1000000000000001e+00 -2.6023515605367979e+00 7.5173327298043446e+00 -6.4345082402300129e+00 1 0 1 -774 1 3 -1.0500000000000000e+00 -2.8231506835232665e+00 4.6333563194435925e+00 -8.0605641951789604e+00 1 0 1 -775 1 3 -1.0500000000000000e+00 -2.4676198319304250e+00 7.4246300333344308e+00 -8.0594222945481242e+00 1 0 1 -776 1 4 -9.4999999999999996e-01 5.1576495784171783e-02 6.2281174409833078e+00 -8.1893454451251166e+00 1 0 1 -777 1 3 -1.0500000000000000e+00 -1.2975095016776308e+00 8.2203029875449154e+00 -5.9607878139978627e+00 1 0 1 -778 1 3 -1.0500000000000000e+00 -2.7146694253441410e+00 6.0430341006248725e+00 -5.9587013238385174e+00 1 0 1 -779 1 3 -1.0500000000000000e+00 -3.8678341546705104e+00 8.3691444919325306e+00 -6.1504110733339727e+00 1 0 1 -780 1 5 4.2499999999999999e-01 -4.5526617430967775e-01 5.3493482680335944e+00 -8.0456383324324694e+00 1 0 1 -781 1 1 1.5750000000000000e+00 -4.2405919059825807e+00 7.2944670077944807e+00 9.1931388109336041e+00 1 0 0 -782 1 2 2.1000000000000001e+00 -5.0296064030418073e+00 -7.9312429721781541e-02 6.4359061250102680e+00 1 0 0 -783 1 2 2.1000000000000001e+00 -5.0528803208991677e+00 5.9314885847207393e+00 6.4280541425652125e+00 1 0 0 -784 1 3 -1.0500000000000000e+00 -4.8323113190408655e+00 8.8153320489722837e+00 8.0541507220343149e+00 1 0 0 -785 1 3 -1.0500000000000000e+00 -5.1875750890510588e+00 6.0244559180381110e+00 8.0528596115841111e+00 1 0 0 -786 1 4 -9.4999999999999996e-01 -2.5468930396501683e+00 7.2207149872186704e+00 8.1829042004208787e+00 1 0 0 -787 1 3 -1.0500000000000000e+00 -1.1976965541013911e+00 5.2286614070351156e+00 5.9541011082395521e+00 1 0 0 -788 1 3 -1.0500000000000000e+00 -4.9407917338772620e+00 7.4058524302343827e+00 5.9524067025542333e+00 1 0 0 -789 1 3 -1.0500000000000000e+00 -3.7873946085238082e+00 5.0798202478013827e+00 6.1439086823543967e+00 1 0 0 -790 1 5 4.2499999999999999e-01 -2.0399936634126092e+00 8.0996016485694859e+00 8.0392480188451927e+00 1 0 0 -791 1 1 1.5750000000000000e+00 -6.8487764459721472e+00 2.8115396349174162e+00 9.1931393353000779e+00 1 0 0 -792 1 2 2.1000000000000001e+00 -2.4214360684733940e+00 4.4035963531915776e+00 6.4359069157849582e+00 1 0 0 -793 1 2 2.1000000000000001e+00 -2.5010867592299606e+00 1.4486079821071627e+00 6.4280700334665895e+00 1 0 0 -794 1 3 -1.0500000000000000e+00 -2.2804693176190796e+00 4.3324382110458508e+00 8.0541466060331786e+00 1 0 0 -795 1 3 -1.0500000000000000e+00 -2.6357629333121757e+00 1.5415738251322431e+00 8.0528749502136669e+00 1 0 0 -796 1 4 -9.4999999999999996e-01 -5.1550909689645312e+00 2.7377879547736299e+00 8.1828937813504332e+00 1 0 0 -797 1 3 -1.0500000000000000e+00 -3.8059367645404496e+00 7.4573545262324359e-01 5.9540888558950602e+00 1 0 0 -798 1 3 -1.0500000000000000e+00 -2.3889553911064834e+00 2.9229182779601004e+00 5.9524183742338437e+00 1 0 0 -799 1 3 -1.0500000000000000e+00 -1.2355527417253249e+00 5.9686629625387155e-01 6.1439250256775200e+00 1 0 0 -800 1 5 4.2499999999999999e-01 -4.6481532735579094e+00 3.6166830920668787e+00 8.0391899192038352e+00 1 0 0 -801 1 1 1.5750000000000000e+00 9.5960721862191178e-01 1.0273669203704241e+01 9.1936235733136655e+00 0 0 0 -802 1 2 2.1000000000000001e+00 4.9172083055255325e+00 -1.7852191623671651e+01 -6.4424003148067897e+00 0 1 1 -803 1 2 2.1000000000000001e+00 5.1658109241490635e+00 1.2000275388808074e+01 -6.4345007360704409e+00 0 0 1 -804 1 3 -1.0500000000000000e+00 4.9450244688133118e+00 9.1162949204304589e+00 -8.0605632730520096e+00 0 0 1 -805 1 3 -1.0500000000000000e+00 5.3005308774925517e+00 1.1907548980836420e+01 -8.0594017059932295e+00 0 0 1 -806 1 4 -9.4999999999999996e-01 2.6597454135294925e+00 1.0711060731609876e+01 -8.1893222259048226e+00 0 0 1 -807 1 3 -1.0500000000000000e+00 1.3105929168482859e+00 1.2703195772710561e+01 -5.9607978618737851e+00 0 0 1 -808 1 3 -1.0500000000000000e+00 5.0535126422700269e+00 1.0525967976026514e+01 -5.9587374189296218e+00 0 0 1 -809 1 3 -1.0500000000000000e+00 3.9003367713006512e+00 1.2852015907184782e+01 -6.1503929107381827e+00 0 0 1 -810 1 5 4.2499999999999999e-01 2.1529230807864241e+00 9.8323236639573999e+00 -8.0454353340627449e+00 0 0 1 -811 1 1 1.5750000000000000e+00 3.5677901403638028e+00 1.4756558886709637e+01 9.1936320859504761e+00 0 0 0 -812 1 2 2.1000000000000001e+00 2.5343724058499628e+00 1.3528195803657599e+01 -6.4424233387050833e+00 0 0 1 -813 1 2 2.1000000000000001e+00 2.6139913107597703e+00 1.6483175576855384e+01 -6.4345033330073518e+00 0 0 1 -814 1 3 -1.0500000000000000e+00 2.3931907288388619e+00 1.3599192072250563e+01 -8.0605653726222926e+00 0 0 1 -815 1 3 -1.0500000000000000e+00 2.7487038780850757e+00 1.6390457170443742e+01 -8.0594014632146163e+00 0 0 1 -816 1 4 -9.4999999999999996e-01 5.2678674489775172e+00 1.5193891941576492e+01 -8.1893953522173568e+00 0 0 1 -817 1 3 -1.0500000000000000e+00 3.9187441878934699e+00 1.7186101345028060e+01 -5.9607852712427771e+00 0 0 1 -818 1 3 -1.0500000000000000e+00 2.5016682244761341e+00 1.5008886897788546e+01 -5.9586911872320503e+00 0 0 1 -819 1 3 -1.0500000000000000e+00 1.3485131765279306e+00 1.7334940064883479e+01 -6.1504041699538501e+00 0 0 1 -820 1 5 4.2499999999999999e-01 4.7610305469518792e+00 1.4315086371947789e+01 -8.0459836447046609e+00 0 0 1 -821 1 1 1.5750000000000000e+00 9.7572650104669378e-01 1.6260281732774981e+01 9.1931398694629891e+00 0 0 0 -822 1 2 2.1000000000000001e+00 1.8672671622704407e-01 8.8865134574184275e+00 6.4359252403327680e+00 0 0 0 -823 1 2 2.1000000000000001e+00 1.6341323909156813e-01 1.4897327838108385e+01 6.4280628738807160e+00 0 0 0 -824 1 3 -1.0500000000000000e+00 1.5869811845495363e-01 -1.8082123015369124e+01 8.0541340213658152e+00 0 1 0 -825 1 3 -1.0500000000000000e+00 2.8742433885017959e-02 1.4990304244778233e+01 8.0528666545587271e+00 0 0 0 -826 1 4 -9.4999999999999996e-01 2.6694272128344245e+00 1.6186542002547892e+01 8.1828957582626032e+00 0 0 0 -827 1 3 -1.0500000000000000e+00 4.0185585050563635e+00 1.4194457856414292e+01 5.9541011149118788e+00 0 0 0 -828 1 3 -1.0500000000000000e+00 2.7554898326217625e-01 1.6371647903721165e+01 5.9524005485591456e+00 0 0 0 -829 1 3 -1.0500000000000000e+00 1.4289385783169752e+00 1.4045613821719037e+01 6.1439242276787418e+00 0 0 0 -830 1 5 4.2499999999999999e-01 3.1763286179058490e+00 1.7065430306293440e+01 8.0392248483087627e+00 0 0 0 -831 1 1 1.5750000000000000e+00 -1.6324184683740341e+00 1.1777344934266068e+01 9.1931465772030379e+00 0 0 0 -832 1 2 2.1000000000000001e+00 2.7949109368093250e+00 1.3369431615951214e+01 6.4359123446196520e+00 0 0 0 -833 1 2 2.1000000000000001e+00 2.7152603396729695e+00 1.0414397731227350e+01 6.4280500078867284e+00 0 0 0 -834 1 3 -1.0500000000000000e+00 2.9358844870702256e+00 1.3298249254921139e+01 8.0541035265692571e+00 0 0 0 -835 1 3 -1.0500000000000000e+00 2.5805749833761542e+00 1.0507387417059693e+01 8.0528735429538472e+00 0 0 0 -836 1 4 -9.4999999999999996e-01 6.1197499880652373e-02 1.1703528856756993e+01 8.1828384666384260e+00 0 0 0 -837 1 3 -1.0500000000000000e+00 1.4104102567525914e+00 9.7115620528564612e+00 5.9541021771064706e+00 0 0 0 -838 1 3 -1.0500000000000000e+00 2.8273745913960777e+00 1.1888758720918936e+01 5.9524302538497906e+00 0 0 0 -839 1 3 -1.0500000000000000e+00 3.9807696072751568e+00 9.5627134385235948e+00 6.1439195726972784e+00 0 0 0 -840 1 5 4.2499999999999999e-01 5.6813568547824111e-01 1.2582401681601350e+01 8.0387418936318475e+00 0 0 0 -841 1 1 1.5750000000000000e+00 6.1196215253814046e+00 1.0273659065047806e+01 9.1936258533248534e+00 0 0 0 -842 1 2 2.1000000000000001e+00 -1.0562814009027809e+01 -1.7852194770053313e+01 -6.4424105685276851e+00 1 1 1 -843 1 2 2.1000000000000001e+00 -1.0314192648491979e+01 1.2000288960951057e+01 -6.4344887365136625e+00 1 0 1 -844 1 3 -1.0500000000000000e+00 1.0105009678123249e+01 9.1162929018207564e+00 -8.0605338552440937e+00 0 0 1 -845 1 3 -1.0500000000000000e+00 -1.0179466880668443e+01 1.1907552881889412e+01 -8.0594043468605676e+00 1 0 1 -846 1 4 -9.4999999999999996e-01 7.8197150885211819e+00 1.0711020034930083e+01 -8.1893365530719642e+00 0 0 1 -847 1 3 -1.0500000000000000e+00 6.4705998763924484e+00 1.2703199920926114e+01 -5.9607930505693743e+00 0 0 1 -848 1 3 -1.0500000000000000e+00 -1.0426491548728604e+01 1.0525953700197832e+01 -5.9587210876857588e+00 1 0 1 -849 1 3 -1.0500000000000000e+00 9.0603500318690493e+00 1.2852009950045630e+01 -6.1503965426825715e+00 0 0 1 -850 1 5 4.2499999999999999e-01 7.3129093231943401e+00 9.8323158297765794e+00 -8.0455729433718481e+00 0 0 1 -851 1 1 1.5750000000000000e+00 -1.1912201911242379e+01 1.4756558626125500e+01 9.1936299374457917e+00 1 0 0 -852 1 2 2.1000000000000001e+00 7.6943659531364794e+00 1.3528208886055431e+01 -6.4424143301466863e+00 0 0 1 -853 1 2 2.1000000000000001e+00 7.7739676680323946e+00 1.6483168696691120e+01 -6.4345034954359610e+00 0 0 1 -854 1 3 -1.0500000000000000e+00 7.5531973024030030e+00 1.3599188471640346e+01 -8.0605797041230307e+00 0 0 1 -855 1 3 -1.0500000000000000e+00 7.9087020351309256e+00 1.6390467445460896e+01 -8.0594079096168336e+00 0 0 1 -856 1 4 -9.4999999999999996e-01 -1.0212139335865229e+01 1.5193882091436695e+01 -8.1893761679918828e+00 1 0 1 -857 1 3 -1.0500000000000000e+00 9.0787892522491873e+00 1.7186124575197912e+01 -5.9607974326211624e+00 0 0 1 -858 1 3 -1.0500000000000000e+00 7.6616656495931785e+00 1.5008857806803714e+01 -5.9586855554315239e+00 0 0 1 -859 1 3 -1.0500000000000000e+00 6.5085065607252339e+00 1.7334943758700970e+01 -6.1504023223690254e+00 0 0 1 -860 1 5 4.2499999999999999e-01 9.9210652194241220e+00 1.4315120680602522e+01 -8.0458834643735155e+00 0 0 1 -861 1 1 1.5750000000000000e+00 6.1357349670429358e+00 1.6260281068792370e+01 9.1931373680824571e+00 0 0 0 -862 1 2 2.1000000000000001e+00 5.3467301429260417e+00 8.8864991034834802e+00 6.4359154980254800e+00 0 0 0 -863 1 2 2.1000000000000001e+00 5.3234375680067387e+00 1.4897337020707621e+01 6.4280633305033579e+00 0 0 0 -864 1 3 -1.0500000000000000e+00 5.3186906165474923e+00 -1.8082119281913194e+01 8.0541512151208217e+00 0 1 0 -865 1 3 -1.0500000000000000e+00 5.1887452446427620e+00 1.4990293685008570e+01 8.0528738710979937e+00 0 0 0 -866 1 4 -9.4999999999999996e-01 -1.2810582246898260e+01 1.6186528254419503e+01 8.1829142774136940e+00 1 0 0 -867 1 3 -1.0500000000000000e+00 -1.1461395078265998e+01 1.4194482279531240e+01 5.9540899004490573e+00 1 0 0 -868 1 3 -1.0500000000000000e+00 5.4355508226374880e+00 1.6371676240781067e+01 5.9523953371586007e+00 0 0 0 -869 1 3 -1.0500000000000000e+00 6.5889448393182057e+00 1.4045610487796125e+01 6.1439231486978052e+00 0 0 0 -870 1 5 4.2499999999999999e-01 -1.2303637921787351e+01 1.7065464446927333e+01 8.0393139102372757e+00 1 0 0 -871 1 1 1.5750000000000000e+00 3.5275677694768159e+00 1.1777354695333322e+01 9.1931437178279722e+00 0 0 0 -872 1 2 2.1000000000000001e+00 -1.2685112265527499e+01 1.3369430390314744e+01 6.4359024875393231e+00 1 0 0 -873 1 2 2.1000000000000001e+00 -1.2764745750720230e+01 1.0414410392944081e+01 6.4280618196758432e+00 1 0 0 -874 1 3 -1.0500000000000000e+00 -1.2544129418050623e+01 1.3298246737339653e+01 8.0541308893949868e+00 1 0 0 -875 1 3 -1.0500000000000000e+00 -1.2899422299851542e+01 1.0507392314703441e+01 8.0528703655478253e+00 1 0 0 -876 1 4 -9.4999999999999996e-01 5.2212278834502932e+00 1.1703569772888418e+01 8.1828544574580064e+00 0 0 0 -877 1 3 -1.0500000000000000e+00 6.5704071848659780e+00 9.7115602039361448e+00 5.9540954146707854e+00 0 0 0 -878 1 3 -1.0500000000000000e+00 -1.2652629524673211e+01 1.1888740289825776e+01 5.9524475845137914e+00 1 0 0 -879 1 3 -1.0500000000000000e+00 -1.1499218746205949e+01 9.5627087106832143e+00 6.1439160615276585e+00 1 0 0 -880 1 5 4.2499999999999999e-01 5.7281515933265581e+00 1.2582412438419141e+01 8.0388892666496652e+00 0 0 0 -881 1 1 1.5750000000000000e+00 -9.3603927864488465e+00 1.0273669083913415e+01 9.1936233661194393e+00 1 0 0 -882 1 2 2.1000000000000001e+00 -5.4027908311748014e+00 -1.7852191591920860e+01 -6.4423999875116174e+00 1 1 1 -883 1 2 2.1000000000000001e+00 -5.1541886950179352e+00 1.2000275601569015e+01 -6.4345007553289859e+00 1 0 1 -884 1 3 -1.0500000000000000e+00 -5.3749756973895453e+00 9.1162952266749038e+00 -8.0605627427916939e+00 1 0 1 -885 1 3 -1.0500000000000000e+00 -5.0194697033740354e+00 1.1907548341371790e+01 -8.0594018260737599e+00 1 0 1 -886 1 4 -9.4999999999999996e-01 -7.6602542409070491e+00 1.0711060750080897e+01 -8.1893221273874310e+00 1 0 1 -887 1 3 -1.0500000000000000e+00 -9.0094074701008555e+00 1.2703195909859126e+01 -5.9607981827342975e+00 1 0 1 -888 1 3 -1.0500000000000000e+00 -5.2664879046879927e+00 1.0525968292949354e+01 -5.9587382228044579e+00 1 0 1 -889 1 3 -1.0500000000000000e+00 -6.4196629439277020e+00 1.2852015502374297e+01 -6.1503932553102407e+00 1 0 1 -890 1 5 4.2499999999999999e-01 -8.1670767718813568e+00 9.8323240619096737e+00 -8.0454342284319758e+00 1 0 1 -891 1 1 1.5750000000000000e+00 -6.7522099413499017e+00 1.4756558947084972e+01 9.1936323727388007e+00 1 0 0 -892 1 2 2.1000000000000001e+00 -7.7856279740588574e+00 1.3528195363213435e+01 -6.4424232658091753e+00 1 0 1 -893 1 2 2.1000000000000001e+00 -7.7060087959671275e+00 1.6483175890169566e+01 -6.4345031930632031e+00 1 0 1 -894 1 3 -1.0500000000000000e+00 -7.9268092601655393e+00 1.3599192115246709e+01 -8.0605653924195604e+00 1 0 1 -895 1 3 -1.0500000000000000e+00 -7.5712960116532937e+00 1.6390457132414308e+01 -8.0594014597887931e+00 1 0 1 -896 1 4 -9.4999999999999996e-01 -5.0521325823369825e+00 1.5193892557836794e+01 -8.1893952534646299e+00 1 0 1 -897 1 3 -1.0500000000000000e+00 -6.4012557391469294e+00 1.7186101532233774e+01 -5.9607858793978696e+00 1 0 1 -898 1 3 -1.0500000000000000e+00 -7.8183317428747330e+00 1.5008887211419324e+01 -5.9586911269344274e+00 1 0 1 -899 1 3 -1.0500000000000000e+00 -8.9714872285006528e+00 1.7334939869012782e+01 -6.1504040245545895e+00 1 0 1 -900 1 5 4.2499999999999999e-01 -5.5589691759804651e+00 1.4315086850375383e+01 -8.0459819775647716e+00 1 0 1 -901 1 1 1.5750000000000000e+00 -9.3442733480072278e+00 1.6260281622445302e+01 9.1931395426557110e+00 1 0 0 -902 1 2 2.1000000000000001e+00 -1.0133273070639682e+01 8.8865135585351283e+00 6.4359254506516130e+00 1 0 0 -903 1 2 2.1000000000000001e+00 -1.0156586250533065e+01 1.4897327970275054e+01 6.4280630778184857e+00 1 0 0 -904 1 3 -1.0500000000000000e+00 -1.0161302019375981e+01 -1.8082122656764607e+01 8.0541343934514202e+00 1 1 0 -905 1 3 -1.0500000000000000e+00 -1.0291257777350937e+01 1.4990303857072181e+01 8.0528666793281509e+00 1 0 0 -906 1 4 -9.4999999999999996e-01 -7.6505730793458735e+00 1.6186540928105540e+01 8.1828953469210077e+00 1 0 0 -907 1 3 -1.0500000000000000e+00 -6.3014405501587794e+00 1.4194458390634342e+01 5.9541017585236222e+00 1 0 0 -908 1 3 -1.0500000000000000e+00 -1.0044451276249209e+01 1.6371648291125627e+01 5.9523999323485963e+00 1 0 0 -909 1 3 -1.0500000000000000e+00 -8.8910624691465792e+00 1.4045613984800305e+01 6.1439241727047040e+00 1 0 0 -910 1 5 4.2499999999999999e-01 -7.1436715781015101e+00 1.7065429938006378e+01 8.0392210249142231e+00 1 0 0 -911 1 1 1.5750000000000000e+00 -1.1952418365405634e+01 1.1777344769910933e+01 9.1931467279769166e+00 1 0 0 -912 1 2 2.1000000000000001e+00 -7.5250893067069100e+00 1.3369431895710402e+01 6.4359125458494333e+00 1 0 0 -913 1 2 2.1000000000000001e+00 -7.6047401369908787e+00 1.0414397698876499e+01 6.4280497411191835e+00 1 0 0 -914 1 3 -1.0500000000000000e+00 -7.3841152575290465e+00 1.3298249173792914e+01 8.0541030584957483e+00 1 0 0 -915 1 3 -1.0500000000000000e+00 -7.7394250224018784e+00 1.0507387430609697e+01 8.0528732915954180e+00 1 0 0 -916 1 4 -9.4999999999999996e-01 -1.0258802640316311e+01 1.1703528516691946e+01 8.1828385357148221e+00 1 0 0 -917 1 3 -1.0500000000000000e+00 -8.9095901142621763e+00 9.7115620830072906e+00 5.9541024500382438e+00 1 0 0 -918 1 3 -1.0500000000000000e+00 -7.4926254257397229e+00 1.1888758199297200e+01 5.9524304645907566e+00 1 0 0 -919 1 3 -1.0500000000000000e+00 -6.3392300077947583e+00 9.5627130728697161e+00 6.1439197965438819e+00 1 0 0 -920 1 5 4.2499999999999999e-01 -9.7518639894871963e+00 1.2582402105100961e+01 8.0387424963826071e+00 1 0 0 -921 1 1 1.5750000000000000e+00 -4.2003781666602471e+00 1.0273659178062957e+01 9.1936255865073093e+00 1 0 0 -922 1 2 2.1000000000000001e+00 -2.4281380657306251e-01 -1.7852194978795438e+01 -6.4424104817889924e+00 1 1 1 -923 1 2 2.1000000000000001e+00 5.8073311176265463e-03 1.2000289091306779e+01 -6.4344887684447798e+00 1 0 1 -924 1 3 -1.0500000000000000e+00 -2.1499032318097377e-01 9.1162934469930086e+00 -8.0605336122239901e+00 1 0 1 -925 1 3 -1.0500000000000000e+00 1.4053305155494300e-01 1.1907552348135759e+01 -8.0594038296133501e+00 1 0 1 -926 1 4 -9.4999999999999996e-01 -2.5002851790322218e+00 1.0711019049960040e+01 -8.1893367336811629e+00 1 0 1 -927 1 3 -1.0500000000000000e+00 -3.8493992000608142e+00 1.2703200254186580e+01 -5.9607924527654337e+00 1 0 1 -928 1 3 -1.0500000000000000e+00 -1.0649173895659203e-01 1.0525953880245513e+01 -5.9587217128014212e+00 1 0 1 -929 1 3 -1.0500000000000000e+00 -1.2596509514345406e+00 1.2852010139898166e+01 -6.1503965479066123e+00 1 0 1 -930 1 5 4.2499999999999999e-01 -3.0070906737129794e+00 9.8323157767275227e+00 -8.0455750813439302e+00 1 0 1 -931 1 1 1.5750000000000000e+00 -1.5922020057366204e+00 1.4756558606205918e+01 9.1936300371528645e+00 1 0 0 -932 1 2 2.1000000000000001e+00 -2.6256342849090215e+00 1.3528209535884709e+01 -6.4424139428101448e+00 1 0 1 -933 1 2 2.1000000000000001e+00 -2.5460327371080291e+00 1.6483168361853000e+01 -6.4345035627546698e+00 1 0 1 -934 1 3 -1.0500000000000000e+00 -2.7668025513118506e+00 1.3599188282117982e+01 -8.0605804580563216e+00 1 0 1 -935 1 3 -1.0500000000000000e+00 -2.4112982996207268e+00 1.6390467789148698e+01 -8.0594086153186755e+00 1 0 1 -936 1 4 -9.4999999999999996e-01 1.0786065194175976e-01 1.5193881944029133e+01 -8.1893757702455225e+00 1 0 1 -937 1 3 -1.0500000000000000e+00 -1.2412111852687584e+00 1.7186124642348755e+01 -5.9607969511890113e+00 1 0 1 -938 1 3 -1.0500000000000000e+00 -2.6583342475974394e+00 1.5008857192020724e+01 -5.9586852469727489e+00 1 0 1 -939 1 3 -1.0500000000000000e+00 -3.8114931308841973e+00 1.7334943467465425e+01 -6.1504023276487931e+00 1 0 1 -940 1 5 4.2499999999999999e-01 -3.9893427220104982e-01 1.4315121673056158e+01 -8.0458809439058001e+00 1 0 1 -941 1 1 1.5750000000000000e+00 -4.1842647733189811e+00 1.6260280900738014e+01 9.1931373541656640e+00 1 0 0 -942 1 2 2.1000000000000001e+00 -4.9732694000809214e+00 8.8864994991107871e+00 6.4359156339852426e+00 1 0 0 -943 1 2 2.1000000000000001e+00 -4.9965619586163417e+00 1.4897336869865573e+01 6.4280633746036724e+00 1 0 0 -944 1 3 -1.0500000000000000e+00 -5.0013096301578557e+00 -1.8082119246981069e+01 8.0541515174107730e+00 1 1 0 -945 1 3 -1.0500000000000000e+00 -5.1312553567957622e+00 1.4990293360305930e+01 8.0528735880481079e+00 1 0 0 -946 1 4 -9.4999999999999996e-01 -2.4905823373432447e+00 1.6186527481653730e+01 8.1829144834701140e+00 1 0 0 -947 1 3 -1.0500000000000000e+00 -1.1413951190011318e+00 1.4194482448426054e+01 5.9540897749393551e+00 1 0 0 -948 1 3 -1.0500000000000000e+00 -4.8844497340440238e+00 1.6371676351109574e+01 5.9523950906844671e+00 1 0 0 -949 1 3 -1.0500000000000000e+00 -3.7310550651778556e+00 1.4045610398815395e+01 6.1439228472924778e+00 1 0 0 -950 1 5 4.2499999999999999e-01 -1.9836375134124715e+00 1.7065464974264582e+01 8.0393139158871101e+00 1 0 0 -951 1 1 1.5750000000000000e+00 -6.7924322972263633e+00 1.1777354850443714e+01 9.1931438146612265e+00 1 0 0 -952 1 2 2.1000000000000001e+00 -2.3651126781701990e+00 1.3369430006310449e+01 6.4359026051072608e+00 1 0 0 -953 1 2 2.1000000000000001e+00 -2.4447460827353300e+00 1.0414411083296347e+01 6.4280619889271122e+00 1 0 0 -954 1 3 -1.0500000000000000e+00 -2.2241294262454936e+00 1.3298247110263322e+01 8.0541315514418770e+00 1 0 0 -955 1 3 -1.0500000000000000e+00 -2.5794223333527002e+00 1.0507391785141490e+01 8.0528704188601807e+00 1 0 0 -956 1 4 -9.4999999999999996e-01 -5.0987720093367859e+00 1.1703570724197846e+01 8.1828547460418974e+00 1 0 0 -957 1 3 -1.0500000000000000e+00 -3.7495921260073386e+00 9.7115606887493051e+00 5.9540950468016760e+00 1 0 0 -958 1 3 -1.0500000000000000e+00 -2.3326296115329681e+00 1.1888740063197147e+01 5.9524471808153692e+00 1 0 0 -959 1 3 -1.0500000000000000e+00 -1.1792192073374927e+00 9.5627085299782664e+00 6.1439162617713929e+00 1 0 0 -960 1 5 4.2499999999999999e-01 -4.5918481348798545e+00 1.2582412837426194e+01 8.0388923615312642e+00 1 0 0 -961 1 1 1.5750000000000000e+00 7.9060983098709947e-01 -1.6623787873959273e+01 9.1936241221863249e+00 0 1 0 -962 1 2 2.1000000000000001e+00 4.9735388822158324e+00 -8.8863555261994307e+00 -6.4424092998091940e+00 0 1 1 -963 1 2 2.1000000000000001e+00 4.9968241249244869e+00 -1.4897218761397980e+01 -6.4345103916532667e+00 0 1 1 -964 1 3 -1.0500000000000000e+00 4.7760131551751499e+00 -1.7781192163992994e+01 -8.0605609776657161e+00 0 1 1 -965 1 3 -1.0500000000000000e+00 5.1315425565977346e+00 -1.4989934535773086e+01 -8.0594167673316317e+00 0 1 1 -966 1 4 -9.4999999999999996e-01 2.4907718354025317e+00 -1.6186388039551588e+01 -8.1893254397424453e+00 0 1 1 -967 1 3 -1.0500000000000000e+00 1.1416263133331963e+00 -1.4194270381621900e+01 -5.9607865702829335e+00 0 1 1 -968 1 3 -1.0500000000000000e+00 4.8845019877675924e+00 -1.6371500937997865e+01 -5.9587293889869883e+00 0 1 1 -969 1 3 -1.0500000000000000e+00 3.7313268130514974e+00 -1.4045419052787359e+01 -6.1504066950524141e+00 0 1 1 -970 1 5 4.2499999999999999e-01 1.9839009717795477e+00 -1.7065170720648808e+01 -8.0454457650585169e+00 0 1 1 -971 1 1 1.5750000000000000e+00 3.3987763835280393e+00 -1.2140905346190326e+01 9.1936279670263232e+00 0 1 0 -972 1 2 2.1000000000000001e+00 2.3653801838590205e+00 -1.3369283232827510e+01 -6.4424200206570612e+00 0 1 1 -973 1 2 2.1000000000000001e+00 2.4449786524018275e+00 -1.0414270762708139e+01 -6.4344947097414282e+00 0 1 1 -974 1 3 -1.0500000000000000e+00 2.2241844110169264e+00 -1.3298260551948676e+01 -8.0605520411220617e+00 0 1 1 -975 1 3 -1.0500000000000000e+00 2.5796912969422721e+00 -1.0507005922241635e+01 -8.0593940590064257e+00 0 1 1 -976 1 4 -9.4999999999999996e-01 5.0988702527267655e+00 -1.1703548043198262e+01 -8.1893609630806132e+00 0 1 1 -977 1 3 -1.0500000000000000e+00 3.7497226337578446e+00 -9.7113711982098554e+00 -5.9607926003920886e+00 0 1 1 -978 1 3 -1.0500000000000000e+00 2.3326731922370776e+00 -1.1888581465336951e+01 -5.9587182867267821e+00 0 1 1 -979 1 3 -1.0500000000000000e+00 1.1795119362153041e+00 -9.5625531841926961e+00 -6.1503935088776673e+00 0 1 1 -980 1 5 4.2499999999999999e-01 4.5920550844469084e+00 -1.2582296526462123e+01 -8.0457276875216497e+00 0 1 1 -981 1 1 1.5750000000000000e+00 8.0672847412836290e-01 -1.0637207792244846e+01 9.1931446898855285e+00 0 1 0 -982 1 2 2.1000000000000001e+00 1.7735241745661767e-02 -1.8010958050662659e+01 6.4359214102136661e+00 0 1 0 -983 1 2 2.1000000000000001e+00 -5.5984692701720462e-03 -1.2000144101042064e+01 6.4280608641863495e+00 0 1 0 -984 1 3 -1.0500000000000000e+00 2.1504497388601607e-01 -9.1163020892610103e+00 8.0541133501916313e+00 0 1 0 -985 1 3 -1.0500000000000000e+00 -1.4026752501513329e-01 -1.1907157403873917e+01 8.0528737619875344e+00 0 1 0 -986 1 4 -9.4999999999999996e-01 2.5003778817693600e+00 -1.0710992981939540e+01 8.1828509244580552e+00 0 1 0 -987 1 3 -1.0500000000000000e+00 3.8495354817159484e+00 -1.2703010397069693e+01 5.9540985576838814e+00 0 1 0 -988 1 3 -1.0500000000000000e+00 1.0654000383107132e-01 -1.0525822550974517e+01 5.9524296977026996e+00 0 1 0 -989 1 3 -1.0500000000000000e+00 1.2599376891911795e+00 -1.2851860677111500e+01 6.1439254063881670e+00 0 1 0 -990 1 5 4.2499999999999999e-01 3.0073045113687797e+00 -9.8321299051036668e+00 8.0388731267178173e+00 0 1 0 -991 1 1 1.5750000000000000e+00 -1.8014219144703780e+00 -1.5120102745243090e+01 9.1931438625465880e+00 0 1 0 -992 1 2 2.1000000000000001e+00 2.6258996467147959e+00 -1.3528044251547879e+01 6.4359209604334673e+00 0 1 0 -993 1 2 2.1000000000000001e+00 2.5462735712870828e+00 -1.6483086989741455e+01 6.4280448059299271e+00 0 1 0 -994 1 3 -1.0500000000000000e+00 2.7668677793957492e+00 -1.3599231581151475e+01 8.0541173845552336e+00 0 1 0 -995 1 3 -1.0500000000000000e+00 2.4115859888696161e+00 -1.6390097180934195e+01 8.0528587154913431e+00 0 1 0 -996 1 4 -9.4999999999999996e-01 -1.0775774466886645e-01 -1.5193886699232316e+01 8.1828630796662409e+00 0 1 0 -997 1 3 -1.0500000000000000e+00 1.2414435679215039e+00 -1.7185906236511268e+01 5.9541111590081126e+00 0 1 0 -998 1 3 -1.0500000000000000e+00 2.6583690581402628e+00 -1.5008709077371243e+01 5.9524182019502625e+00 0 1 0 -999 1 3 -1.0500000000000000e+00 3.8117604092858510e+00 -1.7334728765347329e+01 6.1439089845392907e+00 0 1 0 -1000 1 5 4.2499999999999999e-01 3.9913087790997537e-01 -1.4315031645600069e+01 8.0389424860193159e+00 0 1 0 -1001 1 1 1.5750000000000000e+00 5.9506242267994338e+00 -1.6623797722718635e+01 9.1936266671202027e+00 0 1 0 -1002 1 2 2.1000000000000001e+00 -1.0506484231044222e+01 -8.8863590027846140e+00 -6.4424197062906847e+00 1 1 1 -1003 1 2 2.1000000000000001e+00 -1.0483178876911868e+01 -1.4897205772908450e+01 -6.4344990737011551e+00 1 1 1 -1004 1 3 -1.0500000000000000e+00 9.9359983241624121e+00 -1.7781194811616796e+01 -8.0605324246202574e+00 0 1 1 -1005 1 3 -1.0500000000000000e+00 -1.0348454886651519e+01 -1.4989930517170308e+01 -8.0594191130608888e+00 1 1 1 -1006 1 4 -9.4999999999999996e-01 7.6507412164599700e+00 -1.6186429673609464e+01 -8.1893398094371470e+00 0 1 1 -1007 1 3 -1.0500000000000000e+00 6.3016333555911750e+00 -1.4194266019203811e+01 -5.9607817823241254e+00 0 1 1 -1008 1 3 -1.0500000000000000e+00 -1.0595502565248184e+01 -1.6371514961221607e+01 -5.9587127227690981e+00 1 1 1 -1009 1 3 -1.0500000000000000e+00 8.8913398738443519e+00 -1.4045424689210986e+01 -6.1504098107569085e+00 0 1 1 -1010 1 5 4.2499999999999999e-01 7.1438876889407545e+00 -1.7065178919134084e+01 -8.0455854917667171e+00 0 1 1 -1011 1 1 1.5750000000000000e+00 -1.2081215850500879e+01 -1.2140905395609227e+01 9.1936259420414075e+00 1 1 0 -1012 1 2 2.1000000000000001e+00 7.5253738692339525e+00 -1.3369269642659075e+01 -6.4424111597687768e+00 0 1 1 -1013 1 2 2.1000000000000001e+00 7.6049545385687836e+00 -1.0414276974615465e+01 -6.4344945980992394e+00 0 1 1 -1014 1 3 -1.0500000000000000e+00 7.3841902361699852e+00 -1.3298264178152394e+01 -8.0605659001293244e+00 0 1 1 -1015 1 3 -1.0500000000000000e+00 7.7396886594995777e+00 -1.0506995362793919e+01 -8.0594009666794690e+00 0 1 1 -1016 1 4 -9.4999999999999996e-01 -1.0381136033782756e+01 -1.1703557162467805e+01 -8.1893415275727754e+00 1 1 1 -1017 1 3 -1.0500000000000000e+00 8.9097683875275777e+00 -9.7113477429998554e+00 -5.9608036880169948e+00 0 1 1 -1018 1 3 -1.0500000000000000e+00 7.4926707750249442e+00 -1.1888611603990626e+01 -5.9587129622138315e+00 0 1 1 -1019 1 3 -1.0500000000000000e+00 6.3395050264705226e+00 -9.5625498048806605e+00 -6.1503917722438839e+00 0 1 1 -1020 1 5 4.2499999999999999e-01 9.7520894232513022e+00 -1.2582261952400280e+01 -8.0456251567187103e+00 0 1 1 -1021 1 1 1.5750000000000000e+00 5.9667370778486841e+00 -1.0637208378655330e+01 9.1931422048264544e+00 0 1 0 -1022 1 2 2.1000000000000001e+00 5.1777388423707791e+00 -1.8010972751178073e+01 6.4359121754500244e+00 0 1 0 -1023 1 2 2.1000000000000001e+00 5.1544257741572128e+00 -1.2000135136612140e+01 6.4280617964993638e+00 0 1 0 -1024 1 3 -1.0500000000000000e+00 5.3750375393141656e+00 -9.1162982390217753e+00 8.0541305365780431e+00 0 1 0 -1025 1 3 -1.0500000000000000e+00 5.0197352902290149e+00 -1.1907167532088971e+01 8.0528807818657455e+00 0 1 0 -1026 1 4 -9.4999999999999996e-01 -1.2979631380919937e+01 -1.0711006291129422e+01 8.1828700503208935e+00 1 1 0 -1027 1 3 -1.0500000000000000e+00 -1.1630417926343309e+01 -1.2702985659105673e+01 5.9540877407865125e+00 1 1 0 -1028 1 3 -1.0500000000000000e+00 5.2665416753772529e+00 -1.0525794189955786e+01 5.9524244742663583e+00 0 1 0 -1029 1 3 -1.0500000000000000e+00 6.4199440092225899e+00 -1.2851863874618857e+01 6.1439235679236717e+00 0 1 0 -1030 1 5 4.2499999999999999e-01 -1.2472661329901149e+01 -9.8320942016287756e+00 8.0389673105729180e+00 1 1 0 -1031 1 1 1.5750000000000000e+00 3.3585649047189889e+00 -1.5120093348845725e+01 9.1931411954480566e+00 0 1 0 -1032 1 2 2.1000000000000001e+00 -1.2854123594330217e+01 -1.3528045164039147e+01 6.4359116103016181e+00 1 1 0 -1033 1 2 2.1000000000000001e+00 -1.2933732590309141e+01 -1.6483074437729339e+01 6.4280564286910398e+00 1 1 0 -1034 1 3 -1.0500000000000000e+00 -1.2713145906083197e+01 -1.3599233632746742e+01 8.0541449495269859e+00 1 1 0 -1035 1 3 -1.0500000000000000e+00 -1.3068411221795856e+01 -1.6390092450304788e+01 8.0528554659863651e+00 1 1 0 -1036 1 4 -9.4999999999999996e-01 5.0522727200298299e+00 -1.5193845531977976e+01 8.1828789851171209e+00 0 1 0 -1037 1 3 -1.0500000000000000e+00 6.4014411247152445e+00 -1.7185907439712917e+01 5.9541046118211618e+00 0 1 0 -1038 1 3 -1.0500000000000000e+00 -1.2821635126045146e+01 -1.5008727883675213e+01 5.9524348860327301e+00 1 1 0 -1039 1 3 -1.0500000000000000e+00 -1.1668227901039566e+01 -1.7334733486964044e+01 6.1439059255523709e+00 1 1 0 -1040 1 5 4.2499999999999999e-01 5.5591473737227073e+00 -1.4315020006199346e+01 8.0390921650199161e+00 0 1 0 -1041 1 1 1.5750000000000000e+00 -9.5293899139307694e+00 -1.6623787874027308e+01 9.1936237921326978e+00 1 1 0 -1042 1 2 2.1000000000000001e+00 -5.3464608308947765e+00 -8.8863556911962096e+00 -6.4424092176216048e+00 1 1 1 -1043 1 2 2.1000000000000001e+00 -5.3231748952036835e+00 -1.4897218973116527e+01 -6.4345104241940962e+00 1 1 1 -1044 1 3 -1.0500000000000000e+00 -5.5439872108319319e+00 -1.7781192038243685e+01 -8.0605608257717840e+00 1 1 1 -1045 1 3 -1.0500000000000000e+00 -5.1884582357209057e+00 -1.4989934983843813e+01 -8.0594168014332030e+00 1 1 1 -1046 1 4 -9.4999999999999996e-01 -7.8292281421606535e+00 -1.6186388852721340e+01 -8.1893254252454835e+00 1 1 1 -1047 1 3 -1.0500000000000000e+00 -9.1783739996627851e+00 -1.4194270057895784e+01 -5.9607866851716160e+00 1 1 1 -1048 1 3 -1.0500000000000000e+00 -5.4354986326112078e+00 -1.6371500383854073e+01 -5.9587300155577916e+00 1 1 1 -1049 1 3 -1.0500000000000000e+00 -6.5886729673149071e+00 -1.4045419390766746e+01 -6.1504069109579946e+00 1 1 1 -1050 1 5 4.2499999999999999e-01 -8.3360989250281836e+00 -1.7065170615191157e+01 -8.0454468626928328e+00 1 1 1 -1051 1 1 1.5750000000000000e+00 -6.9212237057752120e+00 -1.2140905279613698e+01 9.1936281507656687e+00 1 1 0 -1052 1 2 2.1000000000000001e+00 -7.9546199637027923e+00 -1.3369283212241633e+01 -6.4424198458964543e+00 1 1 1 -1053 1 2 2.1000000000000001e+00 -7.8750220126689445e+00 -1.0414269957845629e+01 -6.4344945451348980e+00 1 1 1 -1054 1 3 -1.0500000000000000e+00 -8.0958159273297188e+00 -1.3298260211663301e+01 -8.0605513382597493e+00 1 1 1 -1055 1 3 -1.0500000000000000e+00 -7.7403088366973201e+00 -1.0507006356603398e+01 -8.0593941765754753e+00 1 1 1 -1056 1 4 -9.4999999999999996e-01 -5.2211297865863031e+00 -1.1703547420839435e+01 -8.1893607408242381e+00 1 1 1 -1057 1 3 -1.0500000000000000e+00 -6.5702769425775092e+00 -9.7113707796203723e+00 -5.9607925028876929e+00 1 1 1 -1058 1 3 -1.0500000000000000e+00 -7.9873270257999014e+00 -1.1888581912789562e+01 -5.9587188809513618e+00 1 1 1 -1059 1 3 -1.0500000000000000e+00 -9.1404883090905678e+00 -9.5625534027958778e+00 -6.1503936739305480e+00 1 1 1 -1060 1 5 4.2499999999999999e-01 -5.7279445458000602e+00 -1.2582296132873992e+01 -8.0457254720891491e+00 1 1 1 -1061 1 1 1.5750000000000000e+00 -9.5132712414377334e+00 -1.0637207901225382e+01 9.1931444515859475e+00 1 1 0 -1062 1 2 2.1000000000000001e+00 -1.0302264392719938e+01 -1.8010958511740707e+01 6.4359215057736137e+00 1 1 0 -1063 1 2 2.1000000000000001e+00 -1.0325597763836218e+01 -1.2000143885660425e+01 6.4280610801444311e+00 1 1 0 -1064 1 3 -1.0500000000000000e+00 -1.0104955208427846e+01 -9.1163013593801878e+00 8.0541136736620658e+00 1 1 0 -1065 1 3 -1.0500000000000000e+00 -1.0460267735536464e+01 -1.1907157783691902e+01 8.0528740633627187e+00 1 1 0 -1066 1 4 -9.4999999999999996e-01 -7.8196223206297599e+00 -1.0710993626792270e+01 8.1828507388406777e+00 1 1 0 -1067 1 3 -1.0500000000000000e+00 -6.4704642052545438e+00 -1.2703010171579383e+01 5.9540992478015298e+00 1 1 0 -1068 1 3 -1.0500000000000000e+00 -1.0213460429886737e+01 -1.0525822438026575e+01 5.9524290073249873e+00 1 1 0 -1069 1 3 -1.0500000000000000e+00 -9.0600631078363385e+00 -1.2851860507306093e+01 6.1439252136403280e+00 1 1 0 -1070 1 5 4.2499999999999999e-01 -7.3126955303015109e+00 -9.8321299462400891e+00 8.0388711780317124e+00 1 1 0 -1071 1 1 1.5750000000000000e+00 -1.2121421966983748e+01 -1.5120103097951539e+01 9.1931440500472021e+00 1 1 0 -1072 1 2 2.1000000000000001e+00 -7.6941002854421603e+00 -1.3528043511188180e+01 6.4359209951073471e+00 1 1 0 -1073 1 2 2.1000000000000001e+00 -7.7737266980076587e+00 -1.6483087090498845e+01 6.4280445509300250e+00 1 1 0 -1074 1 3 -1.0500000000000000e+00 -7.5531320715693777e+00 -1.3599231720429650e+01 8.0541168058333739e+00 1 1 0 -1075 1 3 -1.0500000000000000e+00 -7.9084140951184896e+00 -1.6390096710853786e+01 8.0528583515780205e+00 1 1 0 -1076 1 4 -9.4999999999999996e-01 -1.0427757546752302e+01 -1.5193886230508655e+01 8.1828631556698568e+00 1 1 0 -1077 1 3 -1.0500000000000000e+00 -9.0785565111582986e+00 -1.7185905911303450e+01 5.9541108650454166e+00 1 1 0 -1078 1 3 -1.0500000000000000e+00 -7.6616310609012928e+00 -1.5008709807568991e+01 5.9524186852294534e+00 1 1 0 -1079 1 3 -1.0500000000000000e+00 -6.5082396808725838e+00 -1.7334728922656321e+01 6.1439092326832387e+00 1 1 0 -1080 1 5 4.2499999999999999e-01 -9.9208689362280111e+00 -1.4315031144537746e+01 8.0389449231468681e+00 1 1 0 -1081 1 1 1.5750000000000000e+00 -4.3693757223617666e+00 -1.6623797501862640e+01 9.1936263177833482e+00 1 1 0 -1082 1 2 2.1000000000000001e+00 -1.8648460984142190e-01 -8.8863586813278168e+00 -6.4424195624530487e+00 1 1 1 -1083 1 2 2.1000000000000001e+00 -1.6317858587872180e-01 -1.4897205481208472e+01 -6.4344990010154151e+00 1 1 1 -1084 1 3 -1.0500000000000000e+00 -3.8400153394278114e-01 -1.7781193820588967e+01 -8.0605319542434177e+00 1 1 1 -1085 1 3 -1.0500000000000000e+00 -2.8454999893785526e-02 -1.4989931453633501e+01 -8.0594185181939455e+00 1 1 1 -1086 1 4 -9.4999999999999996e-01 -2.6692591084942503e+00 -1.6186430683271997e+01 -8.1893402866282834e+00 1 1 1 -1087 1 3 -1.0500000000000000e+00 -4.0183661906291688e+00 -1.4194265831610833e+01 -5.9607808745353807e+00 1 1 1 -1088 1 3 -1.0500000000000000e+00 -2.7550238935744531e-01 -1.6371514908008283e+01 -5.9587140310484852e+00 1 1 1 -1089 1 3 -1.0500000000000000e+00 -1.4286614237111728e+00 -1.4045424230641748e+01 -6.1504096183736543e+00 1 1 1 -1090 1 5 4.2499999999999999e-01 -3.1761126419061068e+00 -1.7065179317613428e+01 -8.0455892156983246e+00 1 1 1 -1091 1 1 1.5750000000000000e+00 -1.7612158961272968e+00 -1.2140905479144390e+01 9.1936260759968853e+00 1 1 0 -1092 1 2 2.1000000000000001e+00 -2.7946263215985852e+00 -1.3369269256921584e+01 -6.4424110744123801e+00 1 1 1 -1093 1 2 2.1000000000000001e+00 -2.7150458550199357e+00 -1.0414277514160078e+01 -6.4344944256095680e+00 1 1 1 -1094 1 3 -1.0500000000000000e+00 -2.9358094427693269e+00 -1.3298264285990225e+01 -8.0605669808735332e+00 1 1 1 -1095 1 3 -1.0500000000000000e+00 -2.5803112238951149e+00 -1.0506994842385364e+01 -8.0594011087445736e+00 1 1 1 -1096 1 4 -9.4999999999999996e-01 -6.1136511004701077e-02 -1.1703557808529808e+01 -8.1893413821177354e+00 1 1 1 -1097 1 3 -1.0500000000000000e+00 -1.4102319921708553e+00 -9.7113476086937851e+00 -5.9608039429212827e+00 1 1 1 -1098 1 3 -1.0500000000000000e+00 -2.8273291376600058e+00 -1.1888611910680980e+01 -5.9587126204693064e+00 1 1 1 -1099 1 3 -1.0500000000000000e+00 -3.9804942634357019e+00 -9.5625498131225903e+00 -6.1503916166810431e+00 1 1 1 -1100 1 5 4.2499999999999999e-01 -5.6791014020577890e-01 -1.2582261570234339e+01 -8.0456254549334609e+00 1 1 1 -1101 1 1 1.5750000000000000e+00 -4.3532626822440168e+00 -1.0637208536663195e+01 9.1931420264587729e+00 1 1 0 -1102 1 2 2.1000000000000001e+00 -5.1422605340029284e+00 -1.8010972814526962e+01 6.4359120870863116e+00 1 1 0 -1103 1 2 2.1000000000000001e+00 -5.1655741403261368e+00 -1.2000135207445506e+01 6.4280620600064111e+00 1 1 0 -1104 1 3 -1.0500000000000000e+00 -4.9449629476468537e+00 -9.1162979298605773e+00 8.0541307780712827e+00 1 1 0 -1105 1 3 -1.0500000000000000e+00 -5.3002653908317328e+00 -1.1907167772463211e+01 8.0528808659252675e+00 1 1 0 -1106 1 4 -9.4999999999999996e-01 -2.6596311317213690e+00 -1.0711006131470690e+01 8.1828701429922646e+00 1 1 0 -1107 1 3 -1.0500000000000000e+00 -1.3104180489574517e+00 -1.2702985515202105e+01 5.9540876691461300e+00 1 1 0 -1108 1 3 -1.0500000000000000e+00 -5.0534589320363521e+00 -1.0525793717166195e+01 5.9524239638193972e+00 1 1 0 -1109 1 3 -1.0500000000000000e+00 -3.9000552799467680e+00 -1.2851864283020131e+01 6.1439229758272287e+00 1 1 0 -1110 1 5 4.2499999999999999e-01 -2.1526612288429927e+00 -9.8320937887400692e+00 8.0389686940673108e+00 1 1 0 -1111 1 1 1.5750000000000000e+00 -6.9614351741271125e+00 -1.5120093227433923e+01 9.1931413794662760e+00 1 1 0 -1112 1 2 2.1000000000000001e+00 -2.5341240788726989e+00 -1.3528045681933817e+01 6.4359119500026907e+00 1 1 0 -1113 1 2 2.1000000000000001e+00 -2.6137328955640973e+00 -1.6483073671230674e+01 6.4280561366629687e+00 1 1 0 -1114 1 3 -1.0500000000000000e+00 -2.3931455622464295e+00 -1.3599233318386894e+01 8.0541451348176771e+00 1 1 0 -1115 1 3 -1.0500000000000000e+00 -2.7484109330614652e+00 -1.6390092942449684e+01 8.0528560015686210e+00 1 1 0 -1116 1 4 -9.4999999999999996e-01 -5.2677274043188298e+00 -1.5193844862391190e+01 8.1828790257998492e+00 1 1 0 -1117 1 3 -1.0500000000000000e+00 -3.9185585465782733e+00 -1.7185907339500744e+01 5.9541042049943371e+00 1 1 0 -1118 1 3 -1.0500000000000000e+00 -2.5016349383751777e+00 -1.5008727970228067e+01 5.9524344787992991e+00 1 1 0 -1119 1 3 -1.0500000000000000e+00 -1.3482285748874521e+00 -1.7334733258778282e+01 6.1439062228621797e+00 1 1 0 -1120 1 5 4.2499999999999999e-01 -4.7608525391156977e+00 -1.4315020002600260e+01 8.0390931457615267e+00 1 1 0 -1121 1 1 1.5750000000000000e+00 8.4695165366353464e-01 -7.6579835812050305e+00 9.1936288874475025e+00 0 1 0 -1122 1 2 2.1000000000000001e+00 5.0298854047814761e+00 7.9466019669347077e-02 -6.4424144556152356e+00 0 1 1 -1123 1 2 2.1000000000000001e+00 5.0531536696119623e+00 -5.9314030709104628e+00 -6.4345137045491718e+00 0 1 1 -1124 1 3 -1.0500000000000000e+00 4.8323591387051597e+00 -8.8153742682486840e+00 -8.0605820239111772e+00 0 1 1 -1125 1 3 -1.0500000000000000e+00 5.1878732372825311e+00 -6.0241061595734156e+00 -8.0594120212256826e+00 0 1 1 -1126 1 4 -9.4999999999999996e-01 2.5470666548004353e+00 -7.2206258319811312e+00 -8.1893702843091258e+00 0 1 1 -1127 1 3 -1.0500000000000000e+00 1.1979474982824225e+00 -5.2284455720511929e+00 -5.9607876425440542e+00 0 1 1 -1128 1 3 -1.0500000000000000e+00 4.9408304873956261e+00 -7.4056780595905938e+00 -5.9586989539517790e+00 0 1 1 -1129 1 3 -1.0500000000000000e+00 3.7876635127476206e+00 -5.0795967839315637e+00 -6.1504077701680906e+00 0 1 1 -1130 1 5 4.2499999999999999e-01 2.0402139399280124e+00 -8.0994421271782215e+00 -8.0457925512527595e+00 0 1 1 -1131 1 1 1.5750000000000000e+00 3.4551102318040119e+00 -3.1750602373809240e+00 9.1936249963033703e+00 0 1 0 -1132 1 2 2.1000000000000001e+00 2.4217092647353429e+00 -4.4034682343922888e+00 -6.4424100556567545e+00 0 1 1 -1133 1 2 2.1000000000000001e+00 2.5013293209638849e+00 -1.4484603202585618e+00 -6.4344992392404317e+00 0 1 1 -1134 1 3 -1.0500000000000000e+00 2.2805060226959757e+00 -4.3324466752627728e+00 -8.0605363775723244e+00 0 1 1 -1135 1 3 -1.0500000000000000e+00 2.6360394328354282e+00 -1.5411974066626648e+00 -8.0594077778299429e+00 0 1 1 -1136 1 4 -9.4999999999999996e-01 5.1552544755530079e+00 -2.7376679909019241e+00 -8.1893317299450903e+00 0 1 1 -1137 1 3 -1.0500000000000000e+00 3.8060930080128710e+00 -7.4554775156575559e-01 -5.9607837224071307e+00 0 1 1 -1138 1 3 -1.0500000000000000e+00 2.3890075299204767e+00 -2.9227581668061351e+00 -5.9587353346909673e+00 0 1 1 -1139 1 3 -1.0500000000000000e+00 1.2358408994011736e+00 -5.9670616076288141e-01 -6.1504018482632379e+00 0 1 1 -1140 1 5 4.2499999999999999e-01 4.6483926264102795e+00 -3.6164252934885788e+00 -8.0454920049899243e+00 0 1 1 -1141 1 1 1.5750000000000000e+00 8.6307055672640232e-01 -1.6713754640994978e+00 9.1931460949904640e+00 0 1 0 -1142 1 2 2.1000000000000001e+00 7.4067440539106855e-02 -9.0451227689768317e+00 6.4359123217975327e+00 0 1 0 -1143 1 2 2.1000000000000001e+00 5.0752316497217009e-02 -3.0343466932277092e+00 6.4280512560994687e+00 0 1 0 -1144 1 3 -1.0500000000000000e+00 2.7137419103710414e-01 -1.5049744182525160e-01 8.0541123492163607e+00 0 1 0 -1145 1 3 -1.0500000000000000e+00 -8.3917914878398392e-02 -2.9413479949048895e+00 8.0528600947866131e+00 0 1 0 -1146 1 4 -9.4999999999999996e-01 2.5567365503950334e+00 -1.7451597138855846e+00 8.1828414199204254e+00 0 1 0 -1147 1 3 -1.0500000000000000e+00 3.9059034396848951e+00 -3.7371848336857560e+00 5.9541096293006248e+00 0 1 0 -1148 1 3 -1.0500000000000000e+00 1.6286724521224549e-01 -1.5599986393757455e+00 5.9524414687461746e+00 0 1 0 -1149 1 3 -1.0500000000000000e+00 1.3162680069554646e+00 -3.8860046896279545e+00 6.1439117370450713e+00 0 1 0 -1150 1 5 4.2499999999999999e-01 3.0636187093141718e+00 -8.6634582863043264e-01 8.0388105803397210e+00 0 1 0 -1151 1 1 1.5750000000000000e+00 -1.7450965412416704e+00 -6.1542721885858267e+00 9.1931396400753584e+00 0 1 0 -1152 1 2 2.1000000000000001e+00 2.6822454390238182e+00 -4.5622313652435196e+00 6.4359253215799903e+00 0 1 0 -1153 1 2 2.1000000000000001e+00 2.6026017072422256e+00 -7.5172435230657051e+00 6.4280525353786011e+00 0 1 0 -1154 1 3 -1.0500000000000000e+00 2.8231982980173314e+00 -4.6333936871729211e+00 8.0541324435706265e+00 0 1 0 -1155 1 3 -1.0500000000000000e+00 2.4679139242312864e+00 -7.4242702203153303e+00 8.0528635321919779e+00 0 1 0 -1156 1 4 -9.4999999999999996e-01 -5.1408619376051234e-02 -6.2280255545507099e+00 8.1829007040250481e+00 0 1 0 -1157 1 3 -1.0500000000000000e+00 1.2977674624764681e+00 -8.2200857847518538e+00 5.9541052800705856e+00 0 1 0 -1158 1 3 -1.0500000000000000e+00 2.7147125497670821e+00 -6.0428853524192228e+00 5.9523886568029081e+00 0 1 0 -1159 1 3 -1.0500000000000000e+00 3.8680955259932048e+00 -8.3689256292345569e+00 6.1439186514543209e+00 0 1 0 -1160 1 5 4.2499999999999999e-01 4.5549352909885421e-01 -5.3491180364217001e+00 8.0392291387924644e+00 0 1 0 -1161 1 1 1.5750000000000000e+00 6.0069661936035175e+00 -7.6579937548320185e+00 9.1936316044666739e+00 0 1 0 -1162 1 2 2.1000000000000001e+00 -1.0450137639744804e+01 7.9463299035463564e-02 -6.4424243710353402e+00 1 1 1 -1163 1 2 2.1000000000000001e+00 -1.0426849451533693e+01 -5.9313896376259745e+00 -6.4345018460644283e+00 1 1 1 -1164 1 3 -1.0500000000000000e+00 9.9923438028197751e+00 -8.8153761390063448e+00 -8.0605530520838684e+00 0 1 1 -1165 1 3 -1.0500000000000000e+00 -1.0292123773060068e+01 -6.0241018369519264e+00 -8.0594139253470427e+00 1 1 1 -1166 1 4 -9.4999999999999996e-01 7.7070346980525173e+00 -7.2206693221354694e+00 -8.1893857246142900e+00 0 1 1 -1167 1 3 -1.0500000000000000e+00 6.3579542845899226e+00 -5.2284417436908193e+00 -5.9607824168769161e+00 0 1 1 -1168 1 3 -1.0500000000000000e+00 -1.0539173651568435e+01 -7.4056923085044541e+00 -5.9586827688544748e+00 1 1 1 -1169 1 3 -1.0500000000000000e+00 8.9476763873501461e+00 -5.0796014856025877e+00 -6.1504115174662424e+00 0 1 1 -1170 1 5 4.2499999999999999e-01 7.2001993237605930e+00 -8.0994521615003521e+00 -8.0459408726587203e+00 0 1 1 -1171 1 1 1.5750000000000000e+00 -1.2024881927933214e+01 -3.1750599741083469e+00 9.1936226503397407e+00 1 1 0 -1172 1 2 2.1000000000000001e+00 7.5817028306539775e+00 -4.4034548242472553e+00 -6.4424012953316998e+00 0 1 1 -1173 1 2 2.1000000000000001e+00 7.6613050161260396e+00 -1.4484663840709260e+00 -6.4344990879805639e+00 0 1 1 -1174 1 3 -1.0500000000000000e+00 7.4405122110527309e+00 -4.3324502105469040e+00 -8.0605503307031601e+00 0 1 1 -1175 1 3 -1.0500000000000000e+00 7.7960372596729002e+00 -1.5411873078722387e+00 -8.0594145656169474e+00 0 1 1 -1176 1 4 -9.4999999999999996e-01 -1.0324751332420679e+01 -2.7376760680379526e+00 -8.1893113790367398e+00 1 1 1 -1177 1 3 -1.0500000000000000e+00 8.9661392654718526e+00 -7.4552372525409538e-01 -5.9607953656302595e+00 0 1 1 -1178 1 3 -1.0500000000000000e+00 7.5490052861507984e+00 -2.9227882817179367e+00 -5.9587292162986722e+00 0 1 1 -1179 1 3 -1.0500000000000000e+00 6.3958341876680791e+00 -5.9670249615731663e-01 -6.1504001903853247e+00 0 1 1 -1180 1 5 4.2499999999999999e-01 9.8084275444973414e+00 -3.6163895221434412e+00 -8.0453831689662785e+00 0 1 1 -1181 1 1 1.5750000000000000e+00 6.0230793220357306e+00 -1.6713760727027740e+00 9.1931436640286570e+00 0 1 0 -1182 1 2 2.1000000000000001e+00 5.2340703084782234e+00 -9.0451373900601055e+00 6.4359026509421398e+00 0 1 0 -1183 1 2 2.1000000000000001e+00 5.2107768285475249e+00 -3.0343378220597543e+00 6.4280520030326400e+00 0 1 0 -1184 1 3 -1.0500000000000000e+00 5.4313667981565832e+00 -1.5049347755671505e-01 8.0541295280698009e+00 0 1 0 -1185 1 3 -1.0500000000000000e+00 5.0760850477762016e+00 -2.9413590110961252e+00 8.0528672520399454e+00 0 1 0 -1186 1 4 -9.4999999999999996e-01 -1.2923273102973829e+01 -1.7451732121810899e+00 8.1828599921332490e+00 1 1 0 -1187 1 3 -1.0500000000000000e+00 -1.1574049043750753e+01 -3.7371601802106404e+00 5.9540991225733642e+00 1 1 0 -1188 1 3 -1.0500000000000000e+00 5.3228691356732281e+00 -1.5599701988768047e+00 5.9524365669383243e+00 0 1 0 -1189 1 3 -1.0500000000000000e+00 6.4762747623866446e+00 -3.8860082222617720e+00 6.1439098279782520e+00 0 1 0 -1190 1 5 4.2499999999999999e-01 -1.2416347906588287e+01 -8.6631108188533545e-01 8.0389014904005194e+00 1 1 0 -1191 1 1 1.5750000000000000e+00 3.4148894200107200e+00 -6.1542622930241908e+00 9.1931366055821719e+00 0 1 0 -1192 1 2 2.1000000000000001e+00 -1.2797778702965626e+01 -4.5622320761539967e+00 6.4359157700181306e+00 1 1 0 -1193 1 2 2.1000000000000001e+00 -1.2877404237061196e+01 -7.5172309934459047e+00 6.4280643167095981e+00 1 1 0 -1194 1 3 -1.0500000000000000e+00 -1.2656816171316713e+01 -4.6333960070141416e+00 8.0541603707338680e+00 1 1 0 -1195 1 3 -1.0500000000000000e+00 -1.3012083101506889e+01 -7.4242649986939213e+00 8.0528602947442778e+00 1 1 0 -1196 1 4 -9.4999999999999996e-01 5.1086228485452754e+00 -6.2279825781387714e+00 8.1829178979826871e+00 0 1 0 -1197 1 3 -1.0500000000000000e+00 6.4577656205449188e+00 -8.2200870263303347e+00 5.9540987494082156e+00 0 1 0 -1198 1 3 -1.0500000000000000e+00 -1.2765291524992206e+01 -6.0429045661158707e+00 5.9524054565834668e+00 1 1 0 -1199 1 3 -1.0500000000000000e+00 -1.1611893334617134e+01 -8.3689294997385222e+00 6.1439150437810142e+00 1 1 0 -1200 1 5 4.2499999999999999e-01 5.6155110306614677e+00 -5.3491040803435190e+00 8.0393878066564533e+00 0 1 0 -1201 1 1 1.5750000000000000e+00 -9.4730482558538363e+00 -7.6579837804608353e+00 9.1936288584421817e+00 1 1 0 -1202 1 2 2.1000000000000001e+00 -5.2901137812505343e+00 7.9465578875971943e-02 -6.4424141224911704e+00 1 1 1 -1203 1 2 2.1000000000000001e+00 -5.2668457657427714e+00 -5.9314030152575619e+00 -6.4345136025435696e+00 1 1 1 -1204 1 3 -1.0500000000000000e+00 -5.4876411141658021e+00 -8.8153737446390359e+00 -8.0605818622866803e+00 1 1 1 -1205 1 3 -1.0500000000000000e+00 -5.1321272527986546e+00 -6.0241062466034769e+00 -8.0594120618712797e+00 1 1 1 -1206 1 4 -9.4999999999999996e-01 -7.7729334170461382e+00 -7.2206262093781639e+00 -8.1893704172959332e+00 1 1 1 -1207 1 3 -1.0500000000000000e+00 -9.1220532114999084e+00 -5.2284458130184035e+00 -5.9607879501683279e+00 1 1 1 -1208 1 3 -1.0500000000000000e+00 -5.3791700396304432e+00 -7.4056785499597986e+00 -5.9586994192034970e+00 1 1 1 -1209 1 3 -1.0500000000000000e+00 -6.5323364101775097e+00 -5.0795968883872256e+00 -6.1504080690006111e+00 1 1 1 -1210 1 5 4.2499999999999999e-01 -8.2797860458884607e+00 -8.0994421096420997e+00 -8.0457936049874998e+00 1 1 1 -1211 1 1 1.5750000000000000e+00 -6.8648895692884251e+00 -3.1750601185026568e+00 9.1936250796126728e+00 1 1 0 -1212 1 2 2.1000000000000001e+00 -7.8982904263320277e+00 -4.4034684490191758e+00 -6.4424103941693547e+00 1 1 1 -1213 1 2 2.1000000000000001e+00 -7.8186708591875442e+00 -1.4484597541953050e+00 -6.4344993359968523e+00 1 1 1 -1214 1 3 -1.0500000000000000e+00 -8.0394941486076519e+00 -4.3324466787230307e+00 -8.0605359249190123e+00 1 1 1 -1215 1 3 -1.0500000000000000e+00 -7.6839605876128365e+00 -1.5411977245256949e+00 -8.0594077717620856e+00 1 1 1 -1216 1 4 -9.4999999999999996e-01 -5.1647454040881282e+00 -2.7376674564410823e+00 -8.1893315607054049e+00 1 1 1 -1217 1 3 -1.0500000000000000e+00 -6.5139066907486161e+00 -7.4554731891745618e-01 -5.9607837703642241e+00 1 1 1 -1218 1 3 -1.0500000000000000e+00 -7.9309926183745523e+00 -2.9227576840502572e+00 -5.9587353580265621e+00 1 1 1 -1219 1 3 -1.0500000000000000e+00 -9.0841593543895360e+00 -5.9670620551901621e-01 -6.1504020768312335e+00 1 1 1 -1220 1 5 4.2499999999999999e-01 -5.6716072349666886e+00 -3.6164251093405397e+00 -8.0454906260480783e+00 1 1 1 -1221 1 1 1.5750000000000000e+00 -9.4569292783711720e+00 -1.6713754920263852e+00 9.1931457295587080e+00 1 1 0 -1222 1 2 2.1000000000000001e+00 -1.0245932376290614e+01 -9.0451235386775792e+00 6.4359124866582462e+00 1 1 0 -1223 1 2 2.1000000000000001e+00 -1.0269246952870757e+01 -3.0343462448060272e+00 6.4280513194600832e+00 1 1 0 -1224 1 3 -1.0500000000000000e+00 -1.0048625782907161e+01 -1.5049666929490613e-01 8.0541125763199020e+00 1 1 0 -1225 1 3 -1.0500000000000000e+00 -1.0403918287278653e+01 -2.9413486509055247e+00 8.0528602350875680e+00 1 1 0 -1226 1 4 -9.4999999999999996e-01 -7.7632636474169150e+00 -1.7451601758199367e+00 8.1828410557435127e+00 1 1 0 -1227 1 3 -1.0500000000000000e+00 -6.4140961797655454e+00 -3.7371846807874363e+00 5.9541096999475336e+00 1 1 0 -1228 1 3 -1.0500000000000000e+00 -1.0157133031150034e+01 -1.5599995082696054e+00 5.9524404810865761e+00 1 1 0 -1229 1 3 -1.0500000000000000e+00 -9.0037325780420030e+00 -3.8860049270011618e+00 6.1439117463650170e+00 1 1 0 -1230 1 5 4.2499999999999999e-01 -7.2563815502586255e+00 -8.6634628906294964e-01 8.0388078115699031e+00 1 1 0 -1231 1 1 1.5750000000000000e+00 -1.2065096650972574e+01 -6.1542722772754583e+00 9.1931397726777710e+00 1 1 0 -1232 1 2 2.1000000000000001e+00 -7.6377546012995223e+00 -4.5622303585522523e+00 6.4359252178928603e+00 1 1 0 -1233 1 2 2.1000000000000001e+00 -7.7173985866608206e+00 -7.5172441124993092e+00 6.4280522853100841e+00 1 1 0 -1234 1 3 -1.0500000000000000e+00 -7.4968018474351847e+00 -4.6333944697759453e+00 8.0541317707799216e+00 1 1 0 -1235 1 3 -1.0500000000000000e+00 -7.8520862052053939e+00 -7.4242695437274406e+00 8.0528631393504213e+00 1 1 0 -1236 1 4 -9.4999999999999996e-01 -1.0371408885631821e+01 -6.2280260117520783e+00 8.1829008047331513e+00 1 1 0 -1237 1 3 -1.0500000000000000e+00 -9.0222328910023926e+00 -8.2200853313923243e+00 5.9541052342574226e+00 1 1 0 -1238 1 3 -1.0500000000000000e+00 -7.6052877106027434e+00 -6.0428855091388112e+00 5.9523894388192833e+00 1 1 0 -1239 1 3 -1.0500000000000000e+00 -6.4519043738629689e+00 -8.3689258339210468e+00 6.1439186596132167e+00 1 1 0 -1240 1 5 4.2499999999999999e-01 -9.8645060505972708e+00 -5.3491171533452899e+00 8.0392299847806328e+00 1 1 0 -1241 1 1 1.5750000000000000e+00 -4.3130335719799664e+00 -7.6579937038842196e+00 9.1936314019332954e+00 1 1 0 -1242 1 2 2.1000000000000001e+00 -1.3013744627974866e-01 7.9462913475630614e-02 -6.4424246376464893e+00 1 1 1 -1243 1 2 2.1000000000000001e+00 -1.0684893537057505e-01 -5.9313893508898428e+00 -6.4345017895585537e+00 1 1 1 -1244 1 3 -1.0500000000000000e+00 -3.2765635106517799e-01 -8.8153758391408683e+00 -8.0605526742101183e+00 1 1 1 -1245 1 3 -1.0500000000000000e+00 2.7875841094589404e-02 -6.0241024867204338e+00 -8.0594144214765731e+00 1 1 1 -1246 1 4 -9.4999999999999996e-01 -2.6129651405688552e+00 -7.2206691446852744e+00 -8.1893860636445819e+00 1 1 1 -1247 1 3 -1.0500000000000000e+00 -3.9620450314031350e+00 -5.2284410824498675e+00 -5.9607825544872162e+00 1 1 1 -1248 1 3 -1.0500000000000000e+00 -2.1917390278932380e-01 -7.4056927498705267e+00 -5.9586833079493466e+00 1 1 1 -1249 1 3 -1.0500000000000000e+00 -1.3723243147179893e+00 -5.0796017338512662e+00 -6.1504112915507374e+00 1 1 1 -1250 1 5 4.2499999999999999e-01 -3.1198011720613454e+00 -8.0994528008667253e+00 -8.0459422208187643e+00 1 1 1 -1251 1 1 1.5750000000000000e+00 -1.7048821048822589e+00 -3.1750600233974851e+00 9.1936227443404306e+00 1 1 0 -1252 1 2 2.1000000000000001e+00 -2.7382971234679294e+00 -4.4034543566522828e+00 -6.4424013859544047e+00 1 1 1 -1253 1 2 2.1000000000000001e+00 -2.6586950753550083e+00 -1.4484668500258664e+00 -6.4344993053995712e+00 1 1 1 -1254 1 3 -1.0500000000000000e+00 -2.8794880275479802e+00 -4.3324506611342049e+00 -8.0605508713604959e+00 1 1 1 -1255 1 3 -1.0500000000000000e+00 -2.5239630752470426e+00 -1.5411866297456065e+00 -8.0594147792074153e+00 1 1 1 -1256 1 4 -9.4999999999999996e-01 -4.7517837259558604e-03 -2.7376768429853673e+00 -8.1893112828875445e+00 1 1 1 -1257 1 3 -1.0500000000000000e+00 -1.3538609137908306e+00 -7.4552332505261631e-01 -5.9607953412511501e+00 1 1 1 -1258 1 3 -1.0500000000000000e+00 -2.7709951342120993e+00 -2.9227884148814525e+00 -5.9587287907420370e+00 1 1 1 -1259 1 3 -1.0500000000000000e+00 -3.9241656194851835e+00 -5.9670276827438684e-01 -6.1504002860173532e+00 1 1 1 -1260 1 5 4.2499999999999999e-01 -5.1157204379419063e-01 -3.6163887288381531e+00 -8.0453830198534977e+00 1 1 1 -1261 1 1 1.5750000000000000e+00 -4.2969203280827042e+00 -1.6713763688959737e+00 9.1931435527350907e+00 1 1 0 -1262 1 2 2.1000000000000001e+00 -5.0859285809060815e+00 -9.0451378610562490e+00 6.4359028036352743e+00 1 1 0 -1263 1 2 2.1000000000000001e+00 -5.1092223160746624e+00 -3.0343377842534469e+00 6.4280523464977879e+00 1 1 0 -1264 1 3 -1.0500000000000000e+00 -4.8886336868453881e+00 -1.5049298815613810e-01 8.0541300922261030e+00 1 1 0 -1265 1 3 -1.0500000000000000e+00 -5.2439155117684226e+00 -2.9413591861830017e+00 8.0528672575652891e+00 1 1 0 -1266 1 4 -9.4999999999999996e-01 -2.6032732264105301e+00 -1.7451740386941132e+00 8.1828598914546760e+00 1 1 0 -1267 1 3 -1.0500000000000000e+00 -1.2540490739490959e+00 -3.7371600128887970e+00 5.9540989905604338e+00 1 1 0 -1268 1 3 -1.0500000000000000e+00 -4.9971316149433278e+00 -1.5599701828983008e+00 5.9524360634314295e+00 1 1 0 -1269 1 3 -1.0500000000000000e+00 -3.8437253850415853e+00 -3.8860083322717820e+00 6.1439094566028505e+00 1 1 0 -1270 1 5 4.2499999999999999e-01 -2.0963478358362906e+00 -8.6631093607094911e-01 8.0388999212996310e+00 1 1 0 -1271 1 1 1.5750000000000000e+00 -6.9051103097426907e+00 -6.1542621657147709e+00 9.1931367563616888e+00 1 1 0 -1272 1 2 2.1000000000000001e+00 -2.4777787674654199e+00 -4.5622327426858931e+00 6.4359160209892181e+00 1 1 0 -1273 1 2 2.1000000000000001e+00 -2.5574043892191067e+00 -7.5172298950838190e+00 6.4280642763354940e+00 1 1 0 -1274 1 3 -1.0500000000000000e+00 -2.3368161191143999e+00 -4.6333959836381862e+00 8.0541606972964814e+00 1 1 0 -1275 1 3 -1.0500000000000000e+00 -2.6920831091204462e+00 -7.4242654010828897e+00 8.0528604112015607e+00 1 1 0 -1276 1 4 -9.4999999999999996e-01 -5.2113770815501210e+00 -6.2279820619212582e+00 8.1829178539255096e+00 1 1 0 -1277 1 3 -1.0500000000000000e+00 -3.8622341308639339e+00 -8.2200867295059776e+00 5.9540986555569546e+00 1 1 0 -1278 1 3 -1.0500000000000000e+00 -2.4452916356073002e+00 -6.0429049672168205e+00 5.9524054018626558e+00 1 1 0 -1279 1 3 -1.0500000000000000e+00 -1.2918938182515838e+00 -8.3689297483017420e+00 6.1439149539106701e+00 1 1 0 -1280 1 5 4.2499999999999999e-01 -4.7044890687991749e+00 -5.3491044489278803e+00 8.0393882099475782e+00 1 1 0 +264 1 3 -1.05 -8.46491586606061 -17.901292325442792 -1.1390414842434584 1 1 0 +304 1 3 -1.05 -3.3049186986576506 -17.901290750068977 -1.1390437890122005 1 1 0 +386 1 4 -0.95 -11.28157790356712 -10.528150577554554 -1.0092882779490928 1 1 0 +387 1 3 -1.05 -9.935357744081871 -12.520946280855286 -3.2389916781308177 1 1 0 +392 1 2 2.1 -11.15811099398584 -13.347486038255383 -2.757315613614664 1 1 0 +393 1 2 2.1 -11.237889362245781 -16.302594662783648 -2.7652874030664893 1 1 0 +394 1 3 -1.05 -11.016750509593795 -13.41838356845754 -1.1390376370894852 1 1 0 +395 1 3 -1.05 -11.372121493310306 -16.209812211718564 -1.1403956987186419 1 1 0 +398 1 3 -1.05 -11.123698820481325 -14.828288395652272 -3.2407370008852503 1 1 0 +399 1 3 -1.05 -9.973058453394058 -17.155713166817623 -3.0491809998142045 1 1 0 +422 1 2 2.1 -8.60626977865343 -17.83040303949202 -2.757307688224758 1 1 0 +423 1 2 2.1 -8.629758159651553 -11.819745199168645 -2.7652839948923624 1 1 0 +425 1 3 -1.05 -8.763971912039096 -11.726883125298963 -1.1403800557050356 1 1 0 +426 1 4 -0.95 -6.121574906086911 -10.528150365511065 -1.0092950354343362 1 1 0 +427 1 3 -1.05 -4.775388146251923 -12.520964201416565 -3.238989168491626 1 1 0 +428 1 3 -1.05 -8.515522554581228 -10.345254400323427 -3.2407468259276673 1 1 0 +429 1 3 -1.05 -7.364884681634946 -12.672823688539507 -3.0491633890428966 1 1 0 +430 1 5 0.425 -5.613050421649238 -9.64905707477696 -1.1481351133572844 1 1 0 +432 1 2 2.1 -5.998102242208308 -13.347479307389396 -2.7573203771857013 1 1 0 +433 1 2 2.1 -6.077895683838097 -16.302607571672493 -2.7652940728794455 1 1 0 +434 1 3 -1.05 -5.856746304305593 -13.418383628427902 -1.1390485082470203 1 1 0 +435 1 3 -1.05 -6.212119786812962 -16.209812802990335 -1.1403998993912392 1 1 0 +436 1 4 -0.95 -8.729708502581238 -15.011039091449069 -1.009285880112964 1 1 0 +437 1 3 -1.05 -7.383491201121386 -17.003869454733763 -3.2389871522032028 1 1 0 +438 1 3 -1.05 -5.963695048103962 -14.828277302058277 -3.240739000854397 1 1 0 +439 1 3 -1.05 -4.813069783015718 -17.155706888160015 -3.049179484681825 1 1 0 +440 1 5 0.425 -8.221237365494655 -14.13197978175737 -1.148104642070665 1 1 0 +462 1 2 2.1 -3.4462548346618433 -17.830406583197693 -2.7573102223982113 1 1 0 +463 1 2 2.1 -3.469735283388167 -11.819752050513955 -2.7652804467485357 1 1 0 +465 1 3 -1.05 -3.603971352452068 -11.726883408388186 -1.1403764365310618 1 1 0 +468 1 3 -1.05 -3.355521162154167 -10.345233316904825 -3.2407460058544304 1 1 0 +469 1 3 -1.05 -2.2048968961935245 -12.67281584124379 -3.0491647524307126 1 1 0 +470 1 5 0.425 -0.4530336944118556 -9.649058436806532 -1.1481079681882829 1 1 0 +476 1 4 -0.95 -3.569702194052315 -15.011029442444274 -1.0092829982342053 1 1 0 +477 1 3 -1.05 -2.223510954503034 -17.003879928264908 -3.238987017751225 1 1 0 +480 1 5 0.425 -3.061226086377273 -14.131977973418296 -1.1480614741617021 1 1 0 +842 1 2 2.1 -10.562132297329805 -17.851014175793086 -6.432546285956645 1 1 1 +882 1 2 2.1 -5.402127266641829 -17.85100681359648 -6.4325501191668755 1 1 1 +1003 1 2 2.1 -10.482315844290621 -14.895888504252758 -6.4245862304386065 1 1 1 +1005 1 3 -1.05 -10.348095162304375 -14.98875808661767 -8.049491681798669 1 1 1 +1008 1 3 -1.05 -10.596544769602936 -16.37040254747953 -5.949125564063066 1 1 1 +1011 1 1 1.575 -8.68725474195775 -11.77759774974404 -9.1893849316929 1 1 1 +1016 1 4 -0.95 -10.382364613954495 -11.704612098178766 -8.180586191286995 1 1 1 +1020 1 5 0.425 9.749175400148882 -12.583669666698384 -8.041796462307946 0 1 1 +1041 1 1 1.575 -6.135425776818094 -16.260473936348028 -9.189383231154757 1 1 1 +1043 1 2 2.1 -5.3223230647806945 -14.895898704501215 -6.424593124897623 1 1 1 +1044 1 3 -1.05 -5.543484086381093 -17.780185221481208 -8.050814994171715 1 1 1 +1045 1 3 -1.05 -5.188093428747346 -14.988758733832059 -8.04949629788937 1 1 1 +1046 1 4 -0.95 -7.8304849276464115 -16.187485278543456 -8.180577316436402 1 1 1 +1047 1 3 -1.05 -9.176703799441476 -14.19469188744462 -5.95088180516822 1 1 1 +1048 1 3 -1.05 -5.436541172095698 -16.370396928539293 -5.9491275765252185 1 1 1 +1049 1 3 -1.05 -6.587179230640963 -14.042820482901044 -6.140706408704635 1 1 1 +1050 1 5 0.425 -8.339016864185075 -17.06657758066529 -8.041710773044175 1 1 1 +1051 1 1 1.575 -3.527261903235889 -11.77759444673707 -9.189385423079939 1 1 1 +1052 1 2 2.1 -7.953952109972265 -13.36816612248871 -6.432556712138617 1 1 1 +1053 1 2 2.1 -7.8741625097250605 -10.413049444820782 -6.4245790461347605 1 1 1 +1054 1 3 -1.05 -8.095317714508074 -13.297256855654805 -8.050832188461564 1 1 1 +1055 1 3 -1.05 -7.739944593076132 -10.505829863398748 -8.049470808969227 1 1 1 +1056 1 4 -0.95 -5.22236234575324 -11.704613397559287 -8.180592735251716 1 1 1 +1057 1 3 -1.05 -6.568578215806069 -9.711775687113166 -5.950883008183602 1 1 1 +1058 1 3 -1.05 -7.988367489366465 -11.887339802933614 -5.949132603449773 1 1 1 +1059 1 3 -1.05 -9.13900815725675 -9.559928450728057 -6.140691573977905 1 1 1 +1060 1 5 0.425 -5.730842071322643 -12.583668202662341 -8.041826700411479 1 1 1 +1061 1 1 1.575 -9.51358678931899 -10.63699289645826 9.189382244602578 1 1 0 +1071 1 1 1.575 -12.121741592199127 -15.119878495438652 9.18938580037907 1 1 0 +1086 1 4 -0.95 -2.670491270081923 -16.187494416422872 -8.180579828027994 1 1 1 +1087 1 3 -1.05 -4.0166814766710734 -14.194679692266455 -5.950882081820672 1 1 1 +1092 1 2 2.1 -2.793967359717164 -13.368163663673295 -6.432553729688593 1 1 1 +1093 1 2 2.1 -2.714183453046749 -10.413041630295027 -6.4245816020409485 1 1 1 +1094 1 3 -1.05 -2.935315187147549 -13.297258489782635 -8.050829092232332 1 1 1 +1095 1 3 -1.05 -2.5799455030118272 -10.505829398335198 -8.049473780055107 1 1 1 +1098 1 3 -1.05 -2.828369189735337 -11.887360068177586 -5.949133477017218 1 1 1 +1099 1 3 -1.05 -3.978994149665634 -9.559937349167438 -6.140690350890794 1 1 1 +1111 1 1 1.575 -6.961748817696581 -15.11987630783114 9.189384651241914 1 1 0 +184 1 3 -1.05 1.8550841706248615 -17.90129235456656 -1.139041394638964 0 1 0 +224 1 3 -1.05 7.015081266257585 -17.90129077481551 -1.1390443875216096 0 1 0 +342 1 2 2.1 1.7137286735549981 -17.830403990336592 -2.7573075994425302 0 1 0 +343 1 2 2.1 1.6902433706862166 -11.81974490284625 -2.765283591611176 0 1 0 +345 1 3 -1.05 1.5560281171673136 -11.726883064897962 -1.1403796474735532 0 1 0 +346 1 4 -0.95 4.19842475094546 -10.528150984297781 -1.0092947548256248 0 1 0 +347 1 3 -1.05 5.544610676800708 -12.520964874446697 -3.238989158956352 0 1 0 +348 1 3 -1.05 1.8044775438647918 -10.34525379637876 -3.2407469149941974 0 1 0 +349 1 3 -1.05 2.9551154623676954 -12.672823617460397 -3.0491633747353077 0 1 0 +350 1 5 0.425 4.70694993483294 -9.649056414846337 -1.1481352776498177 0 1 0 +352 1 2 2.1 4.321898281880586 -13.347477423653608 -2.7573203124497994 0 1 0 +353 1 2 2.1 4.242103458566291 -16.302606453938324 -2.7652944924881524 0 1 0 +354 1 3 -1.05 4.463253605388115 -13.418383717750396 -1.1390481950715863 0 1 0 +355 1 3 -1.05 4.107880173799165 -16.20981289781871 -1.1404006770584267 0 1 0 +356 1 4 -0.95 1.590291661406587 -15.011038808742594 -1.0092858875728705 0 1 0 +357 1 3 -1.05 2.936511486433403 -17.003867946178126 -3.2389871417941283 0 1 0 +358 1 3 -1.05 4.356304894039823 -14.82828075681348 -3.240738966883275 0 1 0 +359 1 3 -1.05 5.5069296575236155 -17.15570655268637 -3.0491796518303165 0 1 0 +360 1 5 0.425 2.0987625163240065 -14.131980219839427 -1.1481045890306945 0 1 0 +382 1 2 2.1 6.873746152856757 -17.83040759592042 -2.7573103659838294 0 1 0 +383 1 2 2.1 6.850264705504596 -11.819752523567601 -2.765280196679372 0 1 0 +385 1 3 -1.05 6.716028840751633 -11.726883416657042 -1.1403757891844322 0 1 0 +388 1 3 -1.05 6.9644790606265055 -10.345231078604485 -3.2407460256393845 0 1 0 +389 1 3 -1.05 8.115103706656491 -12.67281636337119 -3.049164810920919 0 1 0 +390 1 5 0.425 -10.773033349967385 -9.649057938720548 -1.1481081159206 1 1 0 +396 1 4 -0.95 6.750298277493279 -15.011028676607136 -1.009283156622482 0 1 0 +397 1 3 -1.05 8.0964871902111 -17.003881231209515 -3.2389870762613597 0 1 0 +400 1 5 0.425 7.2587734956592485 -14.13197867342077 -1.1480612275841189 0 1 0 +466 1 4 -0.95 -0.9615776661893243 -10.528149916413177 -1.0092884371126996 1 1 0 +467 1 3 -1.05 0.384639153569287 -12.520948194899127 -3.238991755885184 1 1 0 +472 1 2 2.1 -0.8381092882996519 -13.347486331441 -2.757316117320512 1 1 0 +473 1 2 2.1 -0.9178906756593168 -16.302595966027578 -2.7652875692926067 1 1 0 +474 1 3 -1.05 -0.6967505864201584 -13.418383565182262 -1.1390380998660525 1 1 0 +475 1 3 -1.05 -1.052121569574025 -16.2098121280094 -1.1403955470516056 1 1 0 +478 1 3 -1.05 -0.80369877564749 -14.828286105827077 -3.240737085742131 1 1 0 +479 1 3 -1.05 0.3469420057265431 -17.155713599291026 -3.0491809320772196 1 1 0 +802 1 2 2.1 4.917871554153482 -17.85100620844276 -6.432550144635655 0 1 1 +922 1 2 2.1 -0.24213302537487635 -17.851014271362367 -6.432546231091103 1 1 1 +961 1 1 1.575 4.184574182271406 -16.26047384481049 -9.189383148012734 0 1 1 +963 1 2 2.1 4.997676106105352 -14.89589820439723 -6.4245934413401775 0 1 1 +964 1 3 -1.05 4.776515931907607 -17.780185218865757 -8.050814805633557 0 1 1 +965 1 3 -1.05 5.131906621044807 -14.988758753951338 -8.049496747093013 0 1 1 +966 1 4 -0.95 2.4895150021685026 -16.187485328567735 -8.18057734079336 0 1 1 +967 1 3 -1.05 1.143296887105647 -14.194691428548982 -5.9508816939542974 0 1 1 +968 1 3 -1.05 4.883458746547756 -16.37039893676453 -5.949127198225527 0 1 1 +969 1 3 -1.05 3.732820257087102 -14.042820299512814 -6.140706422631954 0 1 1 +970 1 5 0.425 1.9809831558045818 -17.066577464624032 -8.041710794411177 0 1 1 +971 1 1 1.575 6.7927381146326375 -11.77759445236754 -9.189385500475517 0 1 1 +972 1 2 2.1 2.366048010875998 -13.368167275390114 -6.432556655123134 0 1 1 +973 1 2 2.1 2.4458378380486767 -10.41305019631907 -6.424578726466713 0 1 1 +974 1 3 -1.05 2.2246822195755787 -13.29725686242322 -8.05083232247889 0 1 1 +975 1 3 -1.05 2.5800552709418074 -10.505829765473539 -8.049470445822946 0 1 1 +976 1 4 -0.95 5.0976375270724485 -11.704613678089164 -8.180592647516727 0 1 1 +977 1 3 -1.05 3.7514206411706486 -9.711776314023226 -5.950882972545097 0 1 1 +978 1 3 -1.05 2.3316324623441567 -11.887337544400147 -5.949132835505473 0 1 1 +979 1 3 -1.05 1.1809926474831354 -9.559928961691705 -6.140691595020608 0 1 1 +980 1 5 0.425 4.589158058033442 -12.583667977364943 -8.041826644512858 0 1 1 +981 1 1 1.575 0.8064132734883156 -10.636992854297397 9.189382072755725 0 1 0 +991 1 1 1.575 -1.8017416248975664 -15.119878549552123 9.189385972861375 0 1 0 +1001 1 1 1.575 9.344581867349657 -16.26047640418962 -9.189381749309328 0 1 1 +1004 1 3 -1.05 9.936511499778717 -17.780185040060882 -8.05080542913734 0 1 1 +1006 1 4 -0.95 7.649509037996275 -16.18749394104903 -8.180579812342321 0 1 1 +1007 1 3 -1.05 6.303320211197779 -14.1946787391996 -5.950882102933339 0 1 1 +1009 1 3 -1.05 8.892830645210495 -14.042826183342902 -6.140708128407816 0 1 1 +1010 1 5 0.425 7.140972750324373 -17.06658046434351 -8.041751694671362 0 1 1 +1012 1 2 2.1 7.526031845452174 -13.36816391655739 -6.432553511023912 0 1 1 +1013 1 2 2.1 7.605817828327925 -10.413041177382924 -6.424581346822816 0 1 1 +1014 1 3 -1.05 7.38468487052144 -13.29725845871807 -8.050828733960508 0 1 1 +1015 1 3 -1.05 7.7400544850575095 -10.50582940361992 -8.04947339734067 0 1 1 +1017 1 3 -1.05 8.911445176635418 -9.711761973761018 -5.950885381272831 0 1 1 +1018 1 3 -1.05 7.491630912269503 -11.887360305407935 -5.949133684133898 0 1 1 +1019 1 3 -1.05 6.3410053156834145 -9.559936868934592 -6.140690348261861 0 1 1 +1021 1 1 1.575 5.966421032045936 -10.636996344651585 9.18938264851844 0 1 0 +1031 1 1 1.575 3.358251150778086 -15.119876228292004 9.189384489321482 0 1 0 +1081 1 1 1.575 -0.9754180121607927 -16.260476400180153 -9.189381721845933 1 1 1 +1083 1 2 2.1 -0.16231620481821452 -14.89588855547083 -6.424586299599195 1 1 1 +1084 1 3 -1.05 -0.3834885547438134 -17.780184943603096 -8.050805349197944 1 1 1 +1085 1 3 -1.05 -0.028095111453609434 -14.988758081242285 -8.049491887024741 1 1 1 +1088 1 3 -1.05 -0.2765447418097864 -16.37040300834762 -5.9491255041968065 1 1 1 +1089 1 3 -1.05 -1.4271693938816554 -14.042826105628727 -6.140708097208753 1 1 1 +1090 1 5 0.425 -3.179027007517461 -17.066580020974854 -8.041751915389574 1 1 1 +1091 1 1 1.575 1.6327453228612008 -11.777597747951123 -9.189384953878353 1 1 1 +1096 1 4 -0.95 -0.0623645826637933 -11.704612065115022 -8.180586180092902 1 1 1 +1097 1 3 -1.05 -1.4085524757194428 -9.711760380898674 -5.950885471300883 1 1 1 +1100 1 5 0.425 -0.5708246238514203 -12.583669638729017 -8.04179655219608 1 1 1 +1101 1 1 1.575 -4.353578939913898 -10.636996303988589 9.18938270030422 1 1 0 +424 1 3 -1.05 -8.408577734808924 -8.935458287782806 -1.139059751375683 1 1 0 +464 1 3 -1.05 -3.248580635072857 -8.935456708467136 -1.1390620639004094 1 1 0 +546 1 4 -0.95 -11.22522470896448 -1.5623206043777742 -1.009290789576525 1 1 0 +547 1 3 -1.05 -9.878997905733707 -3.5551322132945415 -3.2389873081377836 1 1 0 +552 1 2 2.1 -11.101767117492052 -4.381650113675265 -2.757304877477752 1 1 0 +553 1 2 2.1 -11.18156269540419 -7.336765995632838 -2.765288344254607 1 1 0 +554 1 3 -1.05 -10.960418221812578 -4.452562993782461 -1.1390300904548987 1 1 0 +555 1 3 -1.05 -11.31579151270627 -7.243982991685186 -1.1404006413513326 1 1 0 +558 1 3 -1.05 -11.06735994354583 -5.86248222073808 -3.2407468195561364 1 1 0 +559 1 3 -1.05 -9.916720989567086 -8.189896133020403 -3.0491777269547473 1 1 0 +582 1 2 2.1 -8.549941681999599 -8.864628616853421 -2.7573212395646927 1 1 0 +583 1 2 2.1 -8.573408346697407 -2.853890413154632 -2.765288711323917 1 1 0 +585 1 3 -1.05 -8.707624618283598 -2.761073008594156 -1.1403895124190147 1 1 0 +586 1 4 -0.95 -6.065222151349898 -1.5623213449494315 -1.0092973063700743 1 1 0 +587 1 3 -1.05 -4.719027607075699 -3.5551497395403047 -3.2389848295174266 1 1 0 +588 1 3 -1.05 -8.459194165968885 -1.379498682372585 -3.2407378892779697 1 1 0 +589 1 3 -1.05 -7.308554273668932 -3.7069858317422746 -3.049174267227535 1 1 0 +590 1 5 0.425 -5.556733431736472 -0.6832639340674191 -1.1481901639195726 1 1 0 +592 1 2 2.1 -5.941758795256964 -4.381643258523525 -2.757309447300389 1 1 0 +593 1 2 2.1 -6.0215675995468345 -7.336778252386646 -2.7652948891186604 1 1 0 +594 1 3 -1.05 -5.800414010022665 -4.452562979690537 -1.1390409601576525 1 1 0 +595 1 3 -1.05 -6.155789757574291 -7.243983669469674 -1.1404049422805471 1 1 0 +596 1 4 -0.95 -8.67336350603957 -6.045192015790269 -1.009270118053955 1 1 0 +597 1 3 -1.05 -7.3271615151700535 -8.038035790495682 -3.238992981860023 1 1 0 +598 1 3 -1.05 -5.907356197440085 -5.862471934911557 -3.240748636610774 1 1 0 +599 1 3 -1.05 -4.756733445681928 -8.189889175673681 -3.0491762653397636 1 1 0 +600 1 5 0.425 -8.164891614098584 -5.166098789059271 -1.1479179100126835 1 1 0 +622 1 2 2.1 -3.389926071141417 -8.86463251987464 -2.757323741452172 1 1 0 +623 1 2 2.1 -3.413385787633488 -2.8538972774550295 -2.765285166795646 1 1 0 +625 1 3 -1.05 -3.5476240557786918 -2.7610733325609367 -1.1403860582710532 1 1 0 +628 1 3 -1.05 -3.2991927884903163 -1.3794780433085272 -3.240737139143919 1 1 0 +629 1 3 -1.05 -2.148566845248455 -3.70697790414607 -3.0491756564843833 1 1 0 +630 1 5 0.425 -0.39671665581836635 -0.6832652429943806 -1.1481630022982454 1 1 0 +636 1 4 -0.95 -3.5133574867319632 -6.045182758574995 -1.0092671746550455 1 1 0 +637 1 3 -1.05 -2.16718148458315 -8.038046305327988 -3.2389929701897184 1 1 0 +640 1 5 0.425 -3.004880126821485 -5.166096444308325 -1.147874732161542 1 1 0 +1002 1 2 2.1 -10.505804536592649 -8.88523539174835 -6.432560037265368 1 1 1 +1042 1 2 2.1 -5.345800298480642 -8.885227442477921 -6.432563806365232 1 1 1 +1163 1 2 2.1 -10.425983846443055 -5.930101828666643 -6.4245809302442 1 1 1 +1165 1 3 -1.05 -10.29176457220165 -6.022932645020992 -8.049476504290835 1 1 1 +1168 1 3 -1.05 -10.540210510252924 -7.404496027077366 -5.949119337484573 1 1 1 +1171 1 1 1.575 -8.63091986135657 -2.8117449377960053 -9.189382864542884 1 1 1 +1176 1 4 -0.95 -10.32599221206828 -2.738752318079948 -8.180571473894362 1 1 1 +1180 1 5 0.425 9.80549758975939 -3.6178227408875205 -8.041659890758954 0 1 1 +1201 1 1 1.575 -6.0790863142234715 -7.294676574746575 -9.189383710571903 1 1 1 +1203 1 2 2.1 -5.265992010385035 -5.9301120964551135 -6.424588121260333 1 1 1 +1204 1 3 -1.05 -5.487146068587514 -8.814352398208634 -8.050831889549796 1 1 1 +1205 1 3 -1.05 -5.13176290972859 -6.022933332502667 -8.049481513448013 1 1 1 +1206 1 4 -0.95 -7.774180195115953 -7.221701310871847 -8.180597375280326 1 1 1 +1207 1 3 -1.05 -9.120384514697443 -5.22887681444276 -5.950877168633751 1 1 1 +1208 1 3 -1.05 -5.380207122065727 -7.404491441720323 -5.949121385614578 1 1 1 +1209 1 3 -1.05 -6.530843795028404 -5.077002060288615 -6.140701804367323 1 1 1 +1210 1 5 0.425 -8.282682482860066 -8.100810146883555 -8.041925546941595 1 1 1 +1211 1 1 1.575 -3.4709270920462876 -2.8117417906836764 -9.189383656264441 1 1 1 +1212 1 2 2.1 -7.897616037858306 -4.402286956958651 -6.43254710358858 1 1 1 +1213 1 2 2.1 -7.8178154207096355 -1.447175155438515 -6.4245867930103415 1 1 1 +1214 1 3 -1.05 -8.038985872482693 -4.331447222326599 -8.050813553596454 1 1 1 +1215 1 3 -1.05 -7.683597598118229 -1.5400178951253878 -8.049489918538265 1 1 1 +1216 1 4 -0.95 -5.165990407443373 -2.738754409726031 -8.18057793517165 1 1 1 +1217 1 3 -1.05 -6.512212271258566 -0.7459521192234924 -5.950883480489781 1 1 1 +1218 1 3 -1.05 -7.932036772316762 -2.9216307666036787 -5.949131277993494 1 1 1 +1219 1 3 -1.05 -9.08267694565555 -0.5940914943799172 -6.140702906817449 1 1 1 +1220 1 5 0.425 -5.674519498759423 -3.617820687506949 -8.04169029858849 1 1 1 +1221 1 1 1.575 -9.457246412717982 -1.671157609032207 9.189383876214114 1 1 0 +1231 1 1 1.575 -12.065413830807493 -6.15404471574257 9.18938539391855 1 1 0 +1246 1 4 -0.95 -2.614186286838043 -7.2217101201307425 -8.180599793839612 1 1 1 +1247 1 3 -1.05 -3.960360277202871 -5.228863633164128 -5.950877486276319 1 1 1 +1252 1 2 2.1 -2.737632361906618 -4.402284731749566 -6.432544106737809 1 1 1 +1253 1 2 2.1 -2.6578351242773337 -1.4471666923434476 -6.424589226412993 1 1 1 +1254 1 3 -1.05 -2.8789833754375103 -4.331448893865138 -8.050810085224123 1 1 1 +1255 1 3 -1.05 -2.52359850535919 -1.5400175072708464 -8.049492401815417 1 1 1 +1258 1 3 -1.05 -2.7720385307922406 -2.9216515367363893 -5.949132112636712 1 1 1 +1259 1 3 -1.05 -3.9226633957335055 -0.5941000488864461 -6.140701563447556 1 1 1 +1271 1 1 1.575 -6.9054209708246175 -6.154042407692234 9.189383855475354 1 1 0 +344 1 3 -1.05 1.9114222200240487 -8.935458279277533 -1.139059564533735 0 1 0 +384 1 3 -1.05 7.071419484747558 -8.93545662912592 -1.139062289794058 0 1 0 +502 1 2 2.1 1.770058118239774 -8.864629279480386 -2.7573211125975057 0 1 0 +503 1 2 2.1 1.746593525661929 -2.8538905356736315 -2.7652882528616427 0 1 0 +505 1 3 -1.05 1.6123753212274217 -2.761072943936796 -1.1403890497030549 0 1 0 +506 1 4 -0.95 4.254777803494033 -1.5623214773049696 -1.009297090882841 0 1 0 +507 1 3 -1.05 5.60097265039013 -3.555149476103077 -3.2389847675130836 0 1 0 +508 1 3 -1.05 1.8608059611492145 -1.3794973995029132 -3.2407379362518247 0 1 0 +509 1 3 -1.05 3.0114454619351942 -3.706985652875458 -3.0491742347163786 0 1 0 +510 1 5 0.425 4.763266707487849 -0.6832636557343186 -1.1481903079338203 0 1 0 +512 1 2 2.1 4.37824108680676 -4.381642215703053 -2.7573092994894903 0 1 0 +513 1 2 2.1 4.298431828182256 -7.33677721573053 -2.7652951589904955 0 1 0 +514 1 3 -1.05 4.519586070169236 -4.452562991066955 -1.1390403197679557 0 1 0 +515 1 3 -1.05 4.164210351308228 -7.243983732757602 -1.1404051524282774 0 1 0 +516 1 4 -0.95 1.6466363953447338 -6.0451921005838205 -1.0092700800785774 0 1 0 +517 1 3 -1.05 2.9928396471856065 -8.038035104886228 -3.2389931344831044 0 1 0 +518 1 3 -1.05 4.412643745498574 -5.862474549618074 -3.2407487484486985 0 1 0 +519 1 3 -1.05 5.563266561391211 -8.18988915546985 -3.049176356081455 0 1 0 +520 1 5 0.425 2.1551084204706203 -5.16609866467239 -1.1479179157471826 0 1 0 +542 1 2 2.1 6.930073760633853 -8.864633805826598 -2.7573239225921 0 1 0 +543 1 2 2.1 6.906613517162718 -2.853897481917162 -2.7652852790953517 0 1 0 +545 1 3 -1.05 6.7723761157288465 -2.7610732877001496 -1.1403856281466087 0 1 0 +548 1 3 -1.05 7.020807272771009 -1.379476841625241 -3.240737136594837 0 1 0 +549 1 3 -1.05 8.171434172237035 -3.7069785563504976 -3.0491755848729465 0 1 0 +550 1 5 0.425 -10.71671674630369 -0.6832654187033356 -1.1481629281547843 1 1 0 +556 1 4 -0.95 6.806642683806004 -6.045182652289 -1.009267250406083 0 1 0 +557 1 3 -1.05 8.152818693761645 -8.038046252697145 -3.238992939192382 0 1 0 +560 1 5 0.425 7.315119719776252 -5.166096588082807 -1.1478746942687081 0 1 0 +626 1 4 -0.95 -0.9052247640192803 -1.5623207270903876 -1.0092908139849168 1 1 0 +627 1 3 -1.05 0.4409986064558229 -3.55513430014612 -3.238987362520838 1 1 0 +632 1 2 2.1 -0.781765198312538 -4.381650335007297 -2.757305136503125 1 1 0 +633 1 2 2.1 -0.8615624137678708 -7.3367669283109755 -2.765288206868874 1 1 0 +634 1 3 -1.05 -0.64041820705304 -4.452562941095179 -1.1390306805857744 1 1 0 +635 1 3 -1.05 -0.99579148699058 -7.243983089295831 -1.1404005503235641 1 1 0 +638 1 3 -1.05 -0.7473598652504947 -5.862480258450255 -3.2407468109284423 1 1 0 +639 1 3 -1.05 0.4032783081311422 -8.189895897628547 -3.0491778997613777 1 1 0 +962 1 2 2.1 4.974200302039463 -8.885226777627686 -6.432563729132441 0 1 1 +1082 1 2 2.1 -0.185806274293137 -8.88523494640969 -6.432559660140516 1 1 1 +1121 1 1 1.575 4.240913714129455 -7.294676590686793 -9.189383695860338 0 1 1 +1123 1 2 2.1 5.054007902786916 -5.930111693030472 -6.424588129023356 0 1 1 +1124 1 3 -1.05 4.832853936182163 -8.814352442842836 -8.050831879699462 0 1 1 +1125 1 3 -1.05 5.188237146181489 -6.022933335225861 -8.04948169103402 0 1 1 +1126 1 4 -0.95 2.545820092137145 -7.221700886779187 -8.180597400605782 0 1 1 +1127 1 3 -1.05 1.1996162029904571 -5.228876391762018 -5.950877255533969 0 1 1 +1128 1 3 -1.05 4.939792907189725 -7.404492472702664 -5.949121217373286 0 1 1 +1129 1 3 -1.05 3.7891559709552 -5.077001971456189 -6.140701857527632 0 1 1 +1130 1 5 0.425 2.037317272972258 -8.100810548569898 -8.041925509572767 0 1 1 +1131 1 1 1.575 6.849072888825457 -2.811741689480723 -9.189383537844549 0 1 1 +1132 1 2 2.1 2.4223841271020863 -4.402287509447001 -6.432547026529044 0 1 1 +1133 1 2 2.1 2.502185680946525 -1.4471749599701873 -6.42458639136697 0 1 1 +1134 1 3 -1.05 2.2810141007135005 -4.3314471971634685 -8.050813463045493 0 1 1 +1135 1 3 -1.05 2.6364022779876706 -1.5400179185302143 -8.04948944504933 0 1 1 +1136 1 4 -0.95 5.154009380117879 -2.73875462121606 -8.180577998655997 0 1 1 +1137 1 3 -1.05 3.807785149367101 -0.7459535839548899 -5.950883537936585 0 1 1 +1138 1 3 -1.05 2.387963148489902 -2.9216298569088384 -5.949131508409803 0 1 1 +1139 1 3 -1.05 1.2373236894784867 -0.5940918355153002 -6.140702909127059 0 1 1 +1140 1 5 0.425 4.645480590911754 -3.617820504422353 -8.041690386363467 0 1 1 +1141 1 1 1.575 0.8627536087070986 -1.6711577052294828 9.18938406619552 0 1 0 +1151 1 1 1.575 -1.7454137744760594 -6.154044591809328 9.18938520735365 0 1 0 +1161 1 1 1.575 9.400921345592693 -7.294679183739749 -9.189382152847024 0 1 1 +1164 1 3 -1.05 9.992849429211155 -8.81435221076156 -8.050822440181973 0 1 1 +1166 1 4 -0.95 7.705813430829789 -7.221710674457647 -8.18059968821399 0 1 1 +1167 1 3 -1.05 6.3596398781576156 -5.228863629458221 -5.950877516883361 0 1 1 +1169 1 3 -1.05 8.94916626015327 -5.077007792880384 -6.140703499406546 0 1 1 +1170 1 5 0.425 7.197307460595866 -8.100812243136945 -8.041966681414198 0 1 1 +1172 1 2 2.1 7.582367683488243 -4.402284032411057 -6.432543750269571 0 1 1 +1173 1 2 2.1 7.662165094646056 -1.447166201878126 -6.424589375468346 0 1 1 +1174 1 3 -1.05 7.441016703458011 -4.33144872801018 -8.05080991887099 0 1 1 +1175 1 3 -1.05 7.796401552064157 -1.5400176490207613 -8.049492711113222 0 1 1 +1177 1 3 -1.05 8.967811472553645 -0.7459382621993349 -5.950885996764763 0 1 1 +1178 1 3 -1.05 7.547961458135202 -2.921653116508409 -5.949132270533429 0 1 1 +1179 1 3 -1.05 6.397335934009757 -0.5940995052845253 -6.140701722863454 0 1 1 +1181 1 1 1.575 6.022761452224014 -1.6711610325038038 9.189384733881601 0 1 0 +1191 1 1 1.575 3.4145789253607752 -6.154042373460836 9.189383895405408 0 1 0 +1241 1 1 1.575 -0.919078623965504 -7.294679079021275 -9.189382380319893 1 1 1 +1243 1 2 2.1 -0.10598440370268136 -5.930100932531092 -6.424581119725159 1 1 1 +1244 1 3 -1.05 -0.32715050640343435 -8.814352196195246 -8.05082183294797 1 1 1 +1245 1 3 -1.05 0.028235486212588867 -6.022932681205882 -8.049476719692466 1 1 1 +1248 1 3 -1.05 -0.22021063902228377 -7.404498240171787 -5.949119370717186 1 1 1 +1249 1 3 -1.05 -1.370833370356804 -5.077008018598221 -6.140703488710428 1 1 1 +1250 1 5 0.425 -3.1226928311026017 -8.100812730925876 -8.041966561917656 1 1 1 +1251 1 1 1.575 1.6890802359826953 -2.811744966880619 -9.189383163839267 1 1 1 +1256 1 4 -0.95 -0.005992758242417295 -2.738753216631558 -8.180571330642541 1 1 1 +1257 1 3 -1.05 -1.3521890001903145 -0.7459384409832701 -5.950885927432735 1 1 1 +1260 1 5 0.425 -0.5145019540468603 -3.6178218687876402 -8.041660116739482 1 1 1 +1261 1 1 1.575 -4.297238698912295 -1.671161039098081 9.189384626544689 1 1 0 +66 1 4 -0.95 -11.168857413411244 7.40354230473303 -1.009270621918711 1 0 0 +67 1 3 -1.05 -9.822645460808385 5.410696592905623 -3.238991317700341 1 0 0 +72 1 2 2.1 -11.045431902700038 4.584118883669678 -2.7573129595669927 1 0 0 +73 1 2 2.1 -11.125242586862766 1.6290062999736783 -2.7652808348657647 1 0 0 +74 1 3 -1.05 -10.904080920918116 4.513273040814628 -1.139048034178586 1 0 0 +75 1 3 -1.05 -11.259470010039587 1.7218521674576586 -1.140382566465906 1 0 0 +78 1 3 -1.05 -11.011020631747801 3.1034522866952905 -3.2407497089701973 1 0 0 +79 1 3 -1.05 -9.860382334314748 0.7759117274275305 -3.049165720829433 1 0 0 +102 1 2 2.1 -8.493613424986258 0.10122409846838565 -2.757323192975578 1 0 0 +103 1 2 2.1 -8.517068108693376 6.111974095994508 -2.765294523187803 1 0 0 +105 1 3 -1.05 -8.651283926478486 6.204745693345419 -1.140405943066158 1 0 0 +106 1 4 -0.95 -6.008854360601466 7.403542287020748 -1.0092772192840815 1 0 0 +107 1 3 -1.05 -4.662672238122222 5.41068100659405 -3.238988681070188 1 0 0 +108 1 3 -1.05 -8.402859804670333 7.586231166902241 -3.2407428765350357 1 0 0 +109 1 3 -1.05 -7.252220538727875 5.258843554485125 -3.0491804233363666 1 0 0 +110 1 5 0.425 -5.500400910059637 8.282611476985458 -1.1479800750695226 1 0 0 +112 1 2 2.1 -5.885425009588118 4.584125237437899 -2.757317352913315 1 0 0 +113 1 2 2.1 -5.9652471268907 1.6289946463227452 -2.7652873359068035 1 0 0 +114 1 3 -1.05 -5.7440766738855835 4.513273091979116 -1.1390583802266399 1 0 0 +115 1 3 -1.05 -6.099468233788193 1.7218514621970193 -1.140386585906322 1 0 0 +116 1 4 -0.95 -8.617064557281656 2.9205985811717774 -1.0092826841011693 1 0 0 +117 1 3 -1.05 -7.270858314853548 0.9277887665995053 -3.2389934936208906 1 0 0 +118 1 3 -1.05 -5.851016824061415 3.103461965248542 -3.2407514463188676 1 0 0 +119 1 3 -1.05 -4.700394398805081 0.7759184548700553 -3.049164281105149 1 0 0 +120 1 5 0.425 -8.108543295227213 3.7997076991375103 -1.1480274788545408 1 0 0 +142 1 2 2.1 -3.3335981782845536 0.10122011566864941 -2.757325467057764 1 0 0 +143 1 2 2.1 -3.357047142472161 6.11196782113624 -2.7652915386543686 1 0 0 +145 1 3 -1.05 -3.491283130074649 6.204745345271121 -1.1404029948656156 1 0 0 +148 1 3 -1.05 -3.2428585701820545 7.586249294640197 -3.240741946764139 1 0 0 +149 1 3 -1.05 -2.092232840778756 5.258851348198554 -3.049181809306372 1 0 0 +150 1 5 0.425 -0.3403842372794994 8.28260966896027 -1.147952880973289 1 0 0 +156 1 4 -0.95 -3.457058959682409 2.9206070634285233 -1.0092797708444152 1 0 0 +157 1 3 -1.05 -2.1108774684637233 0.9277784951989752 -3.238993287738875 1 0 0 +160 1 5 0.425 -2.9485314187216485 3.799710624823639 -1.1479843692043108 1 0 0 +584 1 3 -1.05 -8.352243293361232 0.030357255394267924 -1.1390524737995804 1 1 0 +624 1 3 -1.05 -3.1922461439355594 0.030358867266201628 -1.1390547665052146 1 1 0 +683 1 2 2.1 -10.369664422889349 3.0356838773287826 -6.424575215502184 1 0 1 +685 1 3 -1.05 -10.23544329651916 2.9429037405897773 -8.049464594434008 1 0 1 +688 1 3 -1.05 -10.483869565823827 1.5614073964043058 -5.949127164061662 1 0 1 +691 1 1 1.575 -8.574578130087637 6.154071208432605 -9.189382180610352 1 0 1 +696 1 4 -0.95 -10.26966239527055 6.227051952939341 -8.18058521084057 1 0 1 +700 1 5 0.425 9.861819220954118 5.347945210496121 -8.041828398642311 0 0 1 +721 1 1 1.575 -6.022756736308421 1.6711303517316303 -9.189385968117815 1 0 1 +723 1 2 2.1 -5.209671651803681 3.0356748124450768 -6.424582436808366 1 0 1 +724 1 3 -1.05 -5.430810675667206 0.15147978126713468 -8.050841477729787 1 0 1 +725 1 3 -1.05 -5.075441675948434 2.9429030094494593 -8.049469596004098 1 0 1 +726 1 4 -0.95 -7.717868211333117 1.7441099614575464 -8.180598129872275 1 0 1 +727 1 3 -1.05 -9.064078445575394 3.7369535813240518 -5.950881069909556 1 0 1 +728 1 3 -1.05 -5.323866211609281 1.5614101422204314 -5.949129186293185 1 0 1 +729 1 3 -1.05 -6.474505361873171 3.888805922950688 -6.140690242051968 1 0 1 +730 1 5 0.425 -8.226330365821905 0.8650337528317387 -8.04190334906801 1 0 1 +731 1 1 1.575 -3.414585332552538 6.154074477881739 -9.189382566209709 1 0 1 +732 1 2 2.1 -7.841289477842381 4.563531901090041 -6.432556537458721 1 0 1 +733 1 2 2.1 -7.761472051295536 7.5186496363365265 -6.424587203245381 1 0 1 +734 1 3 -1.05 -7.982649355928281 4.634376331833035 -8.050818102751318 1 0 1 +735 1 3 -1.05 -7.627256350140505 7.425797358610215 -8.049487688430835 1 0 1 +736 1 4 -0.95 -5.109660696087643 6.227049833368586 -8.180591643035886 1 0 1 +737 1 3 -1.05 -6.455868157279019 8.219860147310026 -5.95087771030675 1 0 1 +738 1 3 -1.05 -7.875707074793935 6.044192212608447 -5.94912141317001 1 0 1 +739 1 3 -1.05 -9.026344217684544 8.371738975760678 -6.140708086311505 1 0 1 +740 1 5 0.425 -5.6181977533352105 5.3479473553048 -8.041858759758451 1 0 1 +741 1 1 1.575 -9.400915706306321 7.294693070513674 9.189384926206701 1 0 0 +751 1 1 1.575 -12.009080441958165 2.811750177053689 9.18938314266052 1 0 0 +766 1 4 -0.95 -2.557874232771691 1.7441011023401245 -8.180600685373042 1 0 1 +767 1 3 -1.05 -3.904055434708212 3.7369659883295796 -5.95088122604723 1 0 1 +772 1 2 2.1 -2.6813056080742177 4.563533847624484 -6.432553914027515 1 0 1 +773 1 2 2.1 -2.601490329276581 7.518657095971896 -6.424589458180317 1 0 1 +774 1 3 -1.05 -2.8226468844219257 4.634374668663277 -8.050815326905791 1 0 1 +775 1 3 -1.05 -2.4672573177963235 7.425797891173772 -8.049490259445886 1 0 1 +778 1 3 -1.05 -2.7157085450829808 6.044173304923305 -5.949121943750433 1 0 1 +779 1 3 -1.05 -3.8663325104492907 8.371731480896397 -6.1407067178695645 1 0 1 +791 1 1 1.575 -6.849087529751488 2.8117525878767076 9.1893818738603 1 0 0 +1162 1 2 2.1 -10.449465120471645 0.08055078594262355 -6.432560152381424 1 1 1 +1202 1 2 2.1 -5.289460289979266 0.08055961753068175 -6.432563855616946 1 1 1 +22 1 2 2.1 1.8263872057743669 0.10122323982130865 -2.757323069657862 0 0 0 +23 1 2 2.1 1.802934745269436 6.111973633171797 -2.765294216508333 0 0 0 +25 1 3 -1.05 1.668716070201313 6.204745778974964 -1.1404054664058876 0 0 0 +26 1 4 -0.95 4.311144928833087 7.403541337708113 -1.0092768860747352 0 0 0 +27 1 3 -1.05 5.657326890881341 5.410680424130639 -3.238988834418395 0 0 0 +28 1 3 -1.05 1.9171402669985689 7.586233181934492 -3.240742714496779 0 0 0 +29 1 3 -1.05 3.0677776400331744 5.2588447310193835 -3.0491803607990793 0 0 0 +30 1 5 0.425 4.8195997233909065 8.28261243314935 -1.1479801940850205 0 0 0 +32 1 2 2.1 4.434576150427134 4.584125949994959 -2.757317218978379 0 0 0 +33 1 2 2.1 4.354752672996783 1.6289957720393495 -2.7652874840181454 0 0 0 +34 1 3 -1.05 4.5759233376029655 4.513273129126759 -1.139057813284337 0 0 0 +35 1 3 -1.05 4.22053164114371 1.7218514201119213 -1.1403866240843445 0 0 0 +36 1 4 -0.95 1.7029355240571267 2.9205989386970366 -1.0092827600960685 0 0 0 +37 1 3 -1.05 3.0491419813654197 0.9277888380035257 -3.2389933763391117 0 0 0 +38 1 3 -1.05 4.468983158494831 3.1034597839279527 -3.2407515601519075 0 0 0 +39 1 3 -1.05 5.619605927640823 0.7759183556409575 -3.049164220104494 0 0 0 +40 1 5 0.425 2.2114564850808733 3.799707313511526 -1.1480272683057233 0 0 0 +62 1 2 2.1 6.98640117943874 0.10121957566898487 -2.757325568981271 0 0 0 +63 1 2 2.1 6.962952872691414 6.111965565108033 -2.765291053290289 0 0 0 +65 1 3 -1.05 6.828716908336894 6.204745500725302 -1.140402317732672 0 0 0 +68 1 3 -1.05 7.077141690972795 7.586254503483634 -3.240741979621399 0 0 0 +69 1 3 -1.05 8.22776739587561 5.258851157636421 -3.0491818244113524 0 0 0 +70 1 5 0.425 -10.660383820606539 8.282610501019679 -1.147952957708176 1 0 0 +76 1 4 -0.95 6.862941628467034 2.9206079904103746 -1.0092797796811954 0 0 0 +77 1 3 -1.05 8.209123056757985 0.9277789976030242 -3.238993264209034 0 0 0 +80 1 5 0.425 7.371467964909829 3.799709712130774 -1.1479841901184216 0 0 0 +146 1 4 -0.95 -0.848856980688371 7.403543065635521 -1.009270824726272 1 0 0 +147 1 3 -1.05 0.49734997703464323 5.410693694185699 -3.2389910874000423 1 0 0 +152 1 2 2.1 -0.7254293332831061 4.584118530889224 -2.7573137024694017 1 0 0 +153 1 2 2.1 -0.8052417523067312 1.6290045848501222 -2.7652807440033573 1 0 0 +154 1 3 -1.05 -0.5840810386894884 4.513273006817702 -1.139048972722227 1 0 0 +155 1 3 -1.05 -0.9394700517706553 1.72185203484268 -1.140382327667803 1 0 0 +158 1 3 -1.05 -0.6910205720729508 3.103455656179019 -3.240749615640384 1 0 0 +159 1 3 -1.05 0.459616131096908 0.7759123793662148 -3.049165738355901 1 0 0 +504 1 3 -1.05 1.9677567762458388 0.030357116215178337 -1.1390524463935598 0 1 0 +544 1 3 -1.05 7.12775394829735 0.03035871942021373 -1.1390547635028394 0 1 0 +641 1 1 1.575 4.297243280430267 1.671130298800211 -9.189385641244018 0 0 1 +643 1 2 2.1 5.110326224583382 3.0356739402471575 -6.424582352677525 0 0 1 +644 1 3 -1.05 4.889189397603788 0.15147969781351733 -8.050841862301551 0 0 1 +645 1 3 -1.05 5.244558465032792 2.9429031552053644 -8.049469782410847 0 0 1 +646 1 4 -0.95 2.6021319091520816 1.744110022530954 -8.180598314731402 0 0 1 +647 1 3 -1.05 1.2559239052415947 3.7369549667711297 -5.950880998844742 0 0 1 +648 1 3 -1.05 4.996133819794588 1.5614110520094115 -5.949129347804236 0 0 1 +649 1 3 -1.05 3.845495927210548 3.888805031760729 -6.140690357525236 0 0 1 +650 1 5 0.425 2.093669549271727 0.8650335817131634 -8.041903350027011 0 0 1 +651 1 1 1.575 6.9054145512570955 6.154074540458179 -9.189382656499738 0 0 1 +652 1 2 2.1 2.4787088179628842 4.563531561622252 -6.432556181635265 0 0 1 +653 1 2 2.1 2.5585280519123863 7.518649657399113 -6.424586980258276 0 0 1 +654 1 3 -1.05 2.337350595388921 4.634376273058173 -8.050818213443714 0 0 1 +655 1 3 -1.05 2.6927435931419677 7.425797306887876 -8.049487673491134 0 0 1 +656 1 4 -0.95 5.210339517630658 6.227049915612355 -8.180591746443643 0 0 1 +657 1 3 -1.05 3.864131301319931 8.21985992889757 -5.95087766666106 0 0 1 +658 1 3 -1.05 2.4442928575967446 6.0441923323782305 -5.949121469473265 0 0 1 +659 1 3 -1.05 1.293656047976766 8.371738811992955 -6.14070806309522 0 0 1 +660 1 5 0.425 4.701802056134319 5.3479472107053745 -8.041858727818608 0 0 1 +661 1 1 1.575 0.9190842362638474 7.294693176083765 9.18938507480893 0 0 0 +671 1 1 1.575 -1.6890804391306382 2.8117504093218777 9.18938332505079 0 0 0 +681 1 1 1.575 9.457250970166566 1.6711276693326198 -9.18938446512876 0 0 1 +684 1 3 -1.05 10.049184911415573 0.15147980327706279 -8.050832179942425 0 0 1 +686 1 4 -0.95 7.762125313928461 1.7441003699159374 -8.180600491077195 0 0 1 +687 1 3 -1.05 6.415947469477651 3.7369678311064902 -5.950881518055825 0 0 1 +689 1 3 -1.05 9.00550526381668 3.888799817051286 -6.140691886923977 0 0 1 +690 1 5 0.425 7.2536596052110625 0.8650317110006327 -8.0419445547798 0 0 1 +692 1 2 2.1 7.638693043216929 4.563534397426547 -6.4325531536019 0 0 1 +693 1 2 2.1 7.718508628628836 7.518659026560055 -6.424589752422065 0 0 1 +694 1 3 -1.05 7.497353200810323 4.6343747301491796 -8.050814243532022 0 0 1 +695 1 3 -1.05 7.852742644013961 7.425797704432085 -8.049490509348363 0 0 1 +697 1 3 -1.05 9.024155028039374 8.219873732752141 -5.950880032041851 0 0 1 +698 1 3 -1.05 7.604291305384475 6.044169570929853 -5.9491223139608955 0 0 1 +699 1 3 -1.05 6.4536687395115315 8.371730865840878 -6.140706656787863 0 0 1 +701 1 1 1.575 6.079092109884446 7.294689732957998 9.18938567229783 0 0 0 +711 1 1 1.575 3.470912432260338 2.811752520407829 9.18938169427584 0 0 0 +761 1 1 1.575 -0.862748970625967 1.6711279104285026 -9.189384196227254 1 0 1 +763 1 2 2.1 -0.04966552243351785 3.0356854531893447 -6.424575713127194 1 0 1 +764 1 3 -1.05 -0.27081511421111415 0.15147985204080072 -8.050831834106292 1 0 1 +765 1 3 -1.05 0.08455673703883626 2.942903550076892 -8.049465159099038 1 0 1 +768 1 3 -1.05 -0.1638698065461348 1.5614034363256728 -5.949127300886313 1 0 1 +769 1 3 -1.05 -1.314494472039442 3.888799611479218 -6.140691815778049 1 0 1 +770 1 5 0.425 -3.066340835168454 0.8650309510303416 -8.041944499248094 1 0 1 +771 1 1 1.575 1.7454217382651063 6.154071312403289 -9.189382010954455 1 0 1 +776 1 4 -0.95 0.05033727790187115 6.227051569791222 -8.180585248517964 1 0 1 +777 1 3 -1.05 -1.2958462848877286 8.219873003854904 -5.9508799815340065 1 0 1 +780 1 5 0.425 -0.45818049467738753 5.347945702077382 -8.041828574124134 1 0 1 +781 1 1 1.575 -4.24090790530673 7.294689732506644 9.189385751111567 1 0 0 +1122 1 2 2.1 5.030540831106158 0.08055969575320887 -6.432563931728475 0 1 1 +1242 1 2 2.1 -0.12946529950842844 0.08055281636946887 -6.432559861989349 1 1 1 +104 1 3 -1.05 -8.2959121065224 8.996169249695711 -1.1390343890689216 1 0 0 +144 1 3 -1.05 -3.135914908937057 8.996170757432672 -1.1390363095158538 1 0 0 +226 1 4 -0.95 -11.11254078166963 16.369359515899372 -1.0092685783280562 1 0 0 +227 1 3 -1.05 -9.766336767287553 14.376528090276803 -3.238995751451072 1 0 0 +232 1 2 2.1 -10.989105741717744 13.549928023588016 -2.757323935025683 1 0 0 +233 1 2 2.1 -11.068899971135107 10.594822188451765 -2.765279879336142 1 0 0 +234 1 3 -1.05 -10.847743946171107 13.479098577596996 -1.139055897700814 1 0 0 +235 1 3 -1.05 -11.203130777002407 10.687669044069192 -1.1403772521551225 1 0 0 +238 1 3 -1.05 -10.954690024169619 12.0692952367427 -3.2407400224236813 1 0 0 +239 1 3 -1.05 -9.804050408061553 9.741740732019732 -3.0491690386393895 1 0 0 +262 1 2 2.1 -8.437271860709437 9.067095538704638 -2.757309714415854 1 0 0 +263 1 2 2.1 -8.460747726831363 15.077765756381456 -2.7652898300476085 1 0 0 +265 1 3 -1.05 -8.59496192705347 15.170581558375392 -1.1403965373644613 1 0 0 +266 1 4 -0.95 -5.952537888276847 16.369358966890967 -1.009275025184838 1 0 0 +267 1 3 -1.05 -4.606364507026352 14.376511915388033 -3.2389931337439766 1 0 0 +268 1 3 -1.05 -8.346518937455452 16.552120589815434 -3.2407517060692665 1 0 0 +269 1 3 -1.05 -7.195882464907534 14.224652440524341 -3.049169641913795 1 0 0 +270 1 5 0.425 -5.444048709356704 17.248463720191207 -1.147926072458759 1 0 0 +272 1 2 2.1 -5.82909830220383 13.54993452321374 -2.7573283893462746 1 0 0 +273 1 2 2.1 -5.908905613187655 10.594810706455835 -2.7652862916855394 1 0 0 +274 1 3 -1.05 -5.687739797179347 13.479098509652726 -1.1390661905436907 1 0 0 +275 1 3 -1.05 -6.043129004198134 10.687668498390035 -1.1403811302142177 1 0 0 +276 1 4 -0.95 -8.560740308552909 11.886397563127215 -1.0092984324169958 1 0 0 +277 1 3 -1.05 -7.2145196644512986 9.893600558555839 -3.2389874855870726 1 0 0 +278 1 3 -1.05 -5.794686347127319 12.069305033408408 -3.2407418681182447 1 0 0 +279 1 3 -1.05 -4.644060718572748 9.741746448813768 -3.049167473148536 1 0 0 +280 1 5 0.425 -8.052219545977914 12.76547280470178 -1.1482142578089327 1 0 0 +302 1 2 2.1 -3.277258168611019 9.06709268329568 -2.757311979744827 1 0 0 +303 1 2 2.1 -3.300726568040103 15.077758676128251 -2.7652865730743468 1 0 0 +305 1 3 -1.05 -3.4349611231128563 15.170581239105989 -1.140393118347875 1 0 0 +308 1 3 -1.05 -3.1865176799692243 16.552141258442898 -3.2407508225636663 1 0 0 +309 1 3 -1.05 -2.0358940608778617 14.224659879221303 -3.049171103603271 1 0 0 +310 1 5 0.425 -0.2840321731062527 17.248462066835817 -1.147899000522239 1 0 0 +316 1 4 -0.95 -3.400734418826705 11.88640656459975 -1.009295426410807 1 0 0 +317 1 3 -1.05 -2.0545374153539004 9.893591389941722 -3.238987396892891 1 0 0 +320 1 5 0.425 -2.892208151349891 12.765475346667152 -1.1481709903587394 1 0 0 +682 1 2 2.1 -10.393123499696863 9.046417716708568 -6.432546443741804 1 0 1 +722 1 2 2.1 -5.233118165742026 9.046425415362691 -6.432550064074174 1 0 1 +843 1 2 2.1 -10.313327211997532 12.001542453469945 -6.424580425058928 1 0 1 +845 1 3 -1.05 -10.179104461980216 11.908724309692506 -8.049479685803213 1 0 1 +848 1 3 -1.05 -10.427534414878945 10.527148371504556 -5.949133527898292 1 0 1 +851 1 1 1.575 -8.51824369157916 15.119864900254253 -9.189383813337964 1 0 1 +856 1 4 -0.95 -10.213364798519171 15.19283911129094 -8.1805999456996 1 0 1 +860 1 5 0.425 9.918165904359903 14.31374353290386 -8.041964406905526 0 0 1 +881 1 1 1.575 -5.966426869062609 10.63697892126217 -9.189385316838635 1 0 1 +883 1 2 2.1 -5.153334349175274 12.001533504730254 -6.4245873656476835 1 0 1 +884 1 3 -1.05 -5.37447932514466 9.117293092454549 -8.05082458509691 1 0 1 +885 1 3 -1.05 -5.019102860091463 11.908723625594156 -8.049484444657638 1 0 1 +886 1 4 -0.95 -7.661503944640748 10.709971405747378 -8.180577991074518 1 0 1 +887 1 3 -1.05 -9.007728176997928 12.702784770767579 -5.950885590714915 1 0 1 +888 1 3 -1.05 -5.267530930954242 10.527151999311467 -5.949135635771657 1 0 1 +889 1 3 -1.05 -6.41817044734872 12.85463275660672 -6.140694819991762 1 0 1 +890 1 5 0.425 -8.169994851836138 9.830913491173146 -8.04168744838706 1 0 1 +891 1 1 1.575 -3.3582508216134324 15.119868110955334 -9.189384343267172 1 0 1 +892 1 2 2.1 -7.78495678012643 13.529298477845082 -6.43256609319971 1 0 1 +893 1 2 2.1 -7.705149478241227 16.484421351166855 -6.4245793302851455 1 0 1 +894 1 3 -1.05 -7.926311841895801 13.600212556274936 -8.050837106470572 1 0 1 +895 1 3 -1.05 -7.570934159096701 16.391631488705965 -8.049468672465693 1 0 1 +896 1 4 -0.95 -5.053362791171019 15.192837358764411 -8.180606557550988 1 0 1 +897 1 3 -1.05 -6.39956503035528 17.185682318971356 -5.950877092454645 1 0 1 +898 1 3 -1.05 -7.819368343047313 15.010129619847799 -5.949122570305669 1 0 1 +899 1 3 -1.05 -8.970006576215036 17.33754814394433 -6.140696561990261 1 0 1 +900 1 5 0.425 -5.561851311372902 14.31374529314498 -8.041994715875438 1 0 1 +901 1 1 1.575 -9.344586773498397 16.260504033010424 9.189383061469888 1 0 0 +911 1 1 1.575 -11.952738939766169 11.777562142070536 9.189383933133433 1 0 0 +926 1 4 -0.95 -2.501509910995111 10.709962944319543 -8.180580504155689 1 0 1 +927 1 3 -1.05 -3.847706337399428 12.702796475235434 -5.95088592683273 1 0 1 +932 1 2 2.1 -2.6249716110823975 13.529300956978918 -6.432563378051501 1 0 1 +933 1 2 2.1 -2.545169473198447 16.484429053990457 -6.424581927314836 1 0 1 +934 1 3 -1.05 -2.766309288557113 13.60021091940969 -8.050834014660039 1 0 1 +935 1 3 -1.05 -2.4109350640925493 16.391631918688784 -8.049471644682779 1 0 1 +938 1 3 -1.05 -2.6593699486230253 15.01010932633989 -5.949123442093706 1 0 1 +939 1 3 -1.05 -3.8099936729346524 17.337539995470653 -6.1406953820272605 1 0 1 +951 1 1 1.575 -6.792745980839589 11.77756446289758 9.18938260060447 1 0 0 +24 1 3 -1.05 2.024087914529936 8.996169156423772 -1.1390344040655176 0 0 0 +64 1 3 -1.05 7.1840851787278694 8.996170799073049 -1.1390371246747328 0 0 0 +182 1 2 2.1 1.8827269267658568 9.067094010459222 -2.7573098488458445 0 0 0 +183 1 2 2.1 1.8592529639379158 15.077765641437093 -2.765289826325004 0 0 0 +185 1 3 -1.05 1.7250381794570995 15.170581615306336 -1.140396317987694 0 0 0 +186 1 4 -0.95 4.367461639099476 16.369358398717548 -1.0092749502092584 0 0 0 +187 1 3 -1.05 5.713635253023561 14.376511780801248 -3.2389931627830233 0 0 0 +188 1 3 -1.05 1.973481145796077 16.552121393137707 -3.240751532677078 0 0 0 +189 1 3 -1.05 3.1241173355918086 14.224652737956877 -3.049169549865817 0 0 0 +190 1 5 0.425 4.875951715036274 17.248464394280933 -1.1479261082563532 0 0 0 +192 1 2 2.1 4.490901958219151 13.549935878555889 -2.7573283335925183 0 0 0 +193 1 2 2.1 4.411093600085744 10.594811332515622 -2.7652867782753123 0 0 0 +194 1 3 -1.05 4.632260285297237 13.47909860671924 -1.1390658297944274 0 0 0 +195 1 3 -1.05 4.276871012971121 10.687668403574609 -1.1403818756309079 0 0 0 +196 1 4 -0.95 1.7592596338728619 11.886397590084986 -1.009298531294947 0 0 0 +197 1 3 -1.05 3.105482884951323 9.893602100379187 -3.238987419660816 0 0 0 +198 1 3 -1.05 4.525313593280881 12.06930177005012 -3.240741819055666 0 0 0 +199 1 3 -1.05 5.675938131533806 9.741747191339616 -3.049167537714908 0 0 0 +200 1 5 0.425 2.2677804650185784 12.765472764023784 -1.1482141972846076 0 0 0 +222 1 2 2.1 7.042742997773182 9.06709065048177 -2.757312355283716 0 0 0 +223 1 2 2.1 7.01927329241666 15.077757124097925 -2.7652861994385667 0 0 0 +225 1 3 -1.05 6.885038862750001 15.170581286095011 -1.1403927842743506 0 0 0 +228 1 3 -1.05 7.133482597267005 16.552144500578226 -3.24075083839441 0 0 0 +229 1 3 -1.05 8.284105934673054 14.224659825474038 -3.049171073901001 0 0 0 +230 1 5 0.425 -10.604031977218472 17.248462460723108 -1.1478989225176974 1 0 0 +236 1 4 -0.95 6.919266019605498 11.886407164285622 -1.0092956224084197 0 0 0 +237 1 3 -1.05 8.26546077991545 9.893590106942362 -3.2389873515609295 0 0 0 +240 1 5 0.425 7.427791685687939 12.765474859759728 -1.1481709088514993 0 0 0 +306 1 4 -0.95 -0.7925404329129417 16.36935978204208 -1.0092685234917038 1 0 0 +307 1 3 -1.05 0.5536617436637812 14.376527208127765 -3.238995583926612 1 0 0 +312 1 2 2.1 -0.669105152749097 13.549928062179351 -2.757324364009518 1 0 0 +313 1 2 2.1 -0.7489012635521384 10.594821654108735 -2.7652800741043446 1 0 0 +314 1 3 -1.05 -0.5277440456768847 13.479098585859202 -1.1390561242138784 1 0 0 +315 1 3 -1.05 -0.8831307002281701 10.687669040991018 -1.140377271846452 1 0 0 +318 1 3 -1.05 -0.6346900144781458 12.069295967999206 -3.240739844147214 1 0 0 +319 1 3 -1.05 0.5159499812446136 9.741740306242114 -3.0491689677433413 1 0 0 +642 1 2 2.1 5.086881183770259 9.046425911742144 -6.432550150515191 0 0 1 +762 1 2 2.1 -0.07312306646521094 9.046418567335255 -6.432546469896677 1 0 1 +801 1 1 1.575 4.353573033337209 10.636979042290022 -9.189385175536927 0 0 1 +803 1 2 2.1 5.166664061273867 12.001532910814223 -6.424587670201428 0 0 1 +804 1 3 -1.05 4.945520713988733 9.117292956379355 -8.050824916593145 0 0 1 +805 1 3 -1.05 5.3008973690342565 11.908723709427026 -8.049484733585496 0 0 1 +806 1 4 -0.95 2.6584959262536785 10.709971246531975 -8.18057805604267 0 0 1 +807 1 3 -1.05 1.3122728430088326 12.702785257177823 -5.950885474338808 0 0 1 +808 1 3 -1.05 5.052469129605173 10.527151794250187 -5.949135352014368 0 0 1 +809 1 3 -1.05 3.9018297035645837 12.854632641977553 -6.1406947910058385 0 0 1 +810 1 5 0.425 2.1500052491708654 9.8309136506718 -8.041687402015095 0 0 1 +811 1 1 1.575 6.961748976029714 15.11986798997648 -9.189384482358879 0 0 1 +812 1 2 2.1 2.535042687523287 13.529297970232026 -6.4325661347972565 0 0 1 +813 1 2 2.1 2.614849740198629 16.48442051022965 -6.424579413308912 0 0 1 +814 1 3 -1.05 2.3936882153036816 13.60021257463752 -8.050837163771524 0 0 1 +815 1 3 -1.05 2.7490658487965707 16.391631453766433 -8.049468717992344 0 0 1 +816 1 4 -0.95 5.266637005508615 15.192836965901162 -8.18060636473161 0 0 1 +817 1 3 -1.05 3.920436314988857 17.185683167433734 -5.950877119319536 0 0 1 +818 1 3 -1.05 2.5006316587760526 15.010130254796007 -5.949122772496109 0 0 1 +819 1 3 -1.05 1.3499937471668204 17.337547912493786 -6.140696589330803 0 0 1 +820 1 5 0.425 4.758148953436567 14.313745713621554 -8.04199471755077 0 0 1 +821 1 1 1.575 0.9754132074566062 16.260504016462573 9.189383008011989 0 0 0 +831 1 1 1.575 -1.632738974614906 11.777562083519168 9.18938401267412 0 0 0 +841 1 1 1.575 9.513580811205571 10.636976340496151 -9.189383726764085 0 0 1 +844 1 3 -1.05 10.105516190294331 9.117293135343541 -8.050815507300172 0 0 1 +846 1 4 -0.95 7.818489772082437 10.709962301298077 -8.180580332939817 0 0 1 +847 1 3 -1.05 6.472296617251764 12.702798330503999 -5.950885979768901 0 0 1 +849 1 3 -1.05 9.061839113308423 12.85462734482412 -6.140696399395345 0 0 1 +850 1 5 0.425 7.309994958903577 9.830911036100812 -8.041728700792529 0 0 1 +852 1 2 2.1 7.695026696212686 13.529300436217842 -6.432563050299859 0 0 1 +853 1 2 2.1 7.774830524031032 16.484429805628476 -6.4245820389886 0 0 1 +854 1 3 -1.05 7.553690820056456 13.600211019443176 -8.050833425414133 0 0 1 +855 1 3 -1.05 7.90906499883684 16.391631831273738 -8.049471300477997 0 0 1 +857 1 3 -1.05 9.080458785689217 17.18569631092181 -5.950879504982494 0 0 1 +858 1 3 -1.05 7.660630099640052 15.010108645803708 -5.949123539735778 0 0 1 +859 1 3 -1.05 6.510007127986675 17.337539652224454 -6.140695210856152 0 0 1 +861 1 1 1.575 6.135421058173524 16.260500644067545 9.18938364090062 0 0 0 +871 1 1 1.575 3.5272538816622223 11.777564383420493 9.189382493802137 0 0 0 +921 1 1 1.575 -0.806419090335142 10.6369764569408 -9.189383713930892 1 0 1 +923 1 2 2.1 0.006672498480664757 12.001542880171968 -6.424580735660621 1 0 1 +924 1 3 -1.05 -0.21448392537694794 9.11729318150578 -8.050815514872385 1 0 1 +925 1 3 -1.05 0.1408954660648405 11.908724218193651 -8.04948002701215 1 0 1 +928 1 3 -1.05 -0.10753447832841978 10.52714708281717 -5.9491334742097575 1 0 1 +929 1 3 -1.05 -1.2581612100588444 12.854627509935817 -6.140696384375066 1 0 1 +930 1 5 0.425 -3.0100053531165374 9.830910509518201 -8.041728647622413 1 0 1 +931 1 1 1.575 1.8017562269381084 15.11986483513633 -9.189383868712579 1 0 1 +936 1 4 -0.95 0.1066350030095542 15.192838843972428 -8.18059994641965 1 0 1 +937 1 3 -1.05 -1.2395403472651836 17.185696825023822 -5.950879632472325 1 0 1 +940 1 5 0.425 -0.4018339418639325 14.313743809074847 -8.041964435447152 1 0 1 +941 1 1 1.575 -4.184578902062027 16.260500609273645 9.18938369837054 1 0 0 +202 1 2 2.1 -12.25907142254093 -18.03284292800733 2.7573256699420927 1 1 0 +242 1 2 2.1 -7.099067653695995 -18.032834660131254 2.757321949202108 1 1 0 +282 1 2 2.1 -1.9390718808592098 -18.032841752433647 2.7573260151961314 1 1 0 +363 1 2 2.1 -12.17925475263019 -15.077717311058388 2.765285829341023 1 1 0 +365 1 3 -1.05 -12.045034136641693 -15.17058638521851 1.140380284474201 1 1 0 +368 1 3 -1.05 -12.293483708242846 -16.552230120486666 3.2407464397069514 1 1 0 +371 1 1 1.575 -10.384193742378155 -11.959425939444456 0.00048685249345048476 1 1 0 +376 1 4 -0.95 -12.079303598777397 -11.886440401085338 1.0092857120331082 1 1 0 +380 1 5 0.425 8.05223641778737 -12.765497942560684 1.1480754613364113 0 1 0 +401 1 1 1.575 -7.832364735753668 -16.442302159158654 0.0004887404677624829 1 1 0 +403 1 2 2.1 -7.0192621716443195 -15.077726395299486 2.7652785593097526 1 1 0 +404 1 3 -1.05 -7.240423003632657 -17.962013523315086 1.1390571695805392 1 1 0 +405 1 3 -1.05 -6.88503232555705 -15.170587037025818 1.1403751249754315 1 1 0 +406 1 4 -0.95 -9.52742392394671 -16.369313757792415 1.0092944834335125 1 1 0 +407 1 3 -1.05 -10.873642789988851 -14.376520147083449 3.238990044343934 1 1 0 +408 1 3 -1.05 -7.133480193318928 -16.55222721372571 3.240744460593115 1 1 0 +409 1 3 -1.05 -8.284118927456472 -14.224648350829375 3.0491654057163835 1 1 0 +410 1 5 0.425 -10.035955666435909 -17.24840570373585 1.1481612082133505 1 1 0 +411 1 1 1.575 -5.22420079757442 -11.959422800114778 0.00048645201034069885 1 1 0 +412 1 2 2.1 -9.650890626615093 -13.549995402180539 2.7573154149176524 1 1 0 +413 1 2 2.1 -9.571101081554449 -10.59487844511782 2.7652931515755252 1 1 0 +414 1 3 -1.05 -9.79225658896784 -13.479085120313027 1.1390395402492484 1 1 0 +415 1 3 -1.05 -9.43688362912373 -10.687658124577233 1.1404014440605117 1 1 0 +416 1 4 -0.95 -6.919301425568876 -11.886441838598635 1.0092790865815378 1 1 0 +417 1 3 -1.05 -8.26551829041191 -9.893604601648414 3.23898900788749 1 1 0 +418 1 3 -1.05 -9.68530634639146 -12.06916633578844 3.2407391018412675 1 1 0 +419 1 3 -1.05 -10.835946889124276 -9.741756916972987 3.0491804516627496 1 1 0 +420 1 5 0.425 -7.427780896515371 -12.765496313753136 1.148045263075046 1 1 0 +421 1 1 1.575 -7.816647968665705 -10.455164560774879 -0.0004897073322478462 1 1 0 +431 1 1 1.575 -10.424802848423498 -14.93805036694446 -0.0004859056005166451 1 1 0 +441 1 1 1.575 -2.672356985637104 -16.442304661398456 0.0004900967852954352 1 1 0 +443 1 2 2.1 -1.859254766057532 -15.077715497267564 2.7652856485819424 1 1 0 +444 1 3 -1.05 -2.080427517107452 -17.96201341457353 1.1390669364709627 1 1 0 +445 1 3 -1.05 -1.7250341319023796 -15.170586397099527 1.1403800605829684 1 1 0 +446 1 4 -0.95 -4.367430017015452 -16.369322564554952 1.009292140799971 1 1 0 +447 1 3 -1.05 -5.713619531094013 -14.376507547027593 3.2389899091549132 1 1 0 +448 1 3 -1.05 -1.9734837675643213 -16.552233362306946 3.2407464072217795 1 1 0 +449 1 3 -1.05 -3.124107903450609 -14.224654635100869 3.0491639457169573 1 1 0 +450 1 5 0.425 -4.875966104966851 -17.24840844446412 1.1481201137788588 1 1 0 +452 1 2 2.1 -4.490906768877101 -13.549992289876716 2.7573182139731394 1 1 0 +453 1 2 2.1 -4.411121706661005 -10.594869786965624 2.7652903571435505 1 1 0 +454 1 3 -1.05 -4.632254153063665 -13.479086778990123 1.1390428907981356 1 1 0 +455 1 3 -1.05 -4.276884510841384 -10.687657631952451 1.140398470795125 1 1 0 +456 1 4 -0.95 -1.759303475003291 -11.886440295102556 1.0092857335364354 1 1 0 +457 1 3 -1.05 -3.1054929638501587 -9.893589696855543 3.238986417750816 1 1 0 +458 1 3 -1.05 -4.5253080460816815 -12.069187701329469 3.240738460509835 1 1 0 +459 1 3 -1.05 -5.6759330828412295 -9.741765530480448 3.049181725968289 1 1 0 +461 1 1 1.575 -2.6566402814800893 -10.455168034309882 -0.0004892102139120169 1 1 0 +471 1 1 1.575 -5.264810010804999 -14.938048096264206 -0.000487296179734642 1 1 0 +904 1 3 -1.05 -10.161854685695545 -18.083120592662027 8.050830551575691 1 1 0 +944 1 3 -1.05 -5.0018574844249635 -18.08311895062402 8.050827904580979 1 1 0 +1026 1 4 -0.95 -12.978516922048088 -10.709978874953269 8.180583642606527 1 1 0 +1027 1 3 -1.05 -11.63229786651004 -12.702775234356054 5.950880154637874 1 1 0 +1032 1 2 2.1 -12.855048702475655 -13.529314203926136 6.432556100815381 1 1 0 +1033 1 2 2.1 -12.934828913170918 -16.484422806588526 6.424584210403742 1 1 0 +1034 1 3 -1.05 -12.713689435153274 -13.600211848270453 8.050834252924775 1 1 0 +1035 1 3 -1.05 -13.069060384643878 -16.39164043985936 8.04947631781359 1 1 0 +1038 1 3 -1.05 -12.820637718787896 -15.010116398098276 5.949134768177112 1 1 0 +1039 1 3 -1.05 -11.66999655483321 -17.337541956749448 6.140690977240167 1 1 0 +1062 1 2 2.1 -10.303209469423026 -18.012230849774276 6.432564214130078 1 1 0 +1063 1 2 2.1 -10.326696781513265 -12.001574504575316 6.424588327366143 1 1 0 +1065 1 3 -1.05 -10.46091070201314 -11.908711333552967 8.04949252107611 1 1 0 +1066 1 4 -0.95 -7.818513991591397 -10.709979211148491 8.180577084620788 1 1 0 +1067 1 3 -1.05 -6.472326004187935 -12.702791670718351 5.9508827006348675 1 1 0 +1068 1 3 -1.05 -10.212461185927816 -10.527079940778258 5.9491249291990975 1 1 0 +1069 1 3 -1.05 -9.061822886101059 -12.854652084676546 6.140708572507496 1 1 0 +1070 1 5 0.425 -7.309988942363981 -9.83088467575225 8.041736795520984 1 1 0 +1072 1 2 2.1 -7.695041897866641 -13.529307071510981 6.432551577978835 1 1 0 +1073 1 2 2.1 -7.774834793344618 -16.484434939947647 6.424577603346634 1 1 0 +1074 1 3 -1.05 -7.553685084393516 -13.60021181043779 8.050823752129004 1 1 0 +1075 1 3 -1.05 -7.90905859095466 -16.39164117482209 8.049471719485046 1 1 0 +1076 1 4 -0.95 -10.426647145797157 -15.192866902601361 8.180586106999344 1 1 0 +1077 1 3 -1.05 -9.080429028015885 -17.18569724875303 5.950884797992369 1 1 0 +1078 1 3 -1.05 -7.660633858427131 -15.010107503057135 5.949133029699604 1 1 0 +1079 1 3 -1.05 -6.510008950950501 -17.337534855285654 6.140692414529196 1 1 0 +1080 1 5 0.425 -9.918176441334206 -14.313808476712383 8.041767318325997 1 1 0 +1102 1 2 2.1 -5.143192688598495 -18.01223469020368 6.43256163865242 1 1 0 +1103 1 2 2.1 -5.166674659914734 -12.001581247949602 6.42459150745173 1 1 0 +1105 1 3 -1.05 -5.30091011073581 -11.90871163472071 8.049495593212923 1 1 0 +1106 1 4 -0.95 -2.6585163778844034 -10.709977949816722 8.18058344571133 1 1 0 +1108 1 3 -1.05 -5.052460029806349 -10.527060001226648 5.949125945371383 1 1 0 +1109 1 3 -1.05 -3.9018353937420382 -12.854644320517036 6.140707119099776 1 1 0 +1110 1 5 0.425 -2.149972520489074 -9.830886800682975 8.041764004644078 1 1 0 +1112 1 2 2.1 -2.535048249868213 -13.529314790919454 6.4325557752826 1 1 0 +1113 1 2 2.1 -2.614828592705191 -16.484424453660697 6.424584582139241 1 1 0 +1114 1 3 -1.05 -2.3936893961900143 -13.60021174374376 8.050833520746858 1 1 0 +1115 1 3 -1.05 -2.7490603036216505 -16.391640281660283 8.049476486761636 1 1 0 +1116 1 4 -0.95 -5.26664099130191 -15.192857666732323 8.180588944438727 1 1 0 +1117 1 3 -1.05 -3.92045104663105 -17.18570897645179 5.950884811850528 1 1 0 +1118 1 3 -1.05 -2.5006374505687665 -15.010113340479645 5.949134814048831 1 1 0 +1120 1 5 0.425 -4.758164912029445 -14.313806137693177 8.041810512450352 1 1 0 +162 1 2 2.1 3.2209340369483392 -18.032833757481832 2.7573218012303666 0 1 0 +321 1 1 1.575 2.4876351465059408 -16.442302154784315 0.0004888543282763891 0 1 0 +323 1 2 2.1 3.300736726127152 -15.077726538699793 2.7652785057231757 0 1 0 +324 1 3 -1.05 3.079577082904713 -17.962013555013442 1.1390567851692257 0 1 0 +325 1 3 -1.05 3.4349676885715965 -15.17058703603631 1.1403750102570616 0 1 0 +326 1 4 -0.95 0.7925763172482458 -16.369313154371326 1.0092944072974692 0 1 0 +327 1 3 -1.05 -0.5536431863284719 -14.376520315382402 3.2389901201446865 0 1 0 +328 1 3 -1.05 3.186519855031559 -16.552227249856152 3.240744515086007 0 1 0 +329 1 3 -1.05 2.0358818673234893 -14.224648915409531 3.0491654939012367 0 1 0 +330 1 5 0.425 0.2840440343041557 -17.248406206415737 1.1481612674588284 0 1 0 +331 1 1 1.575 5.095799077392401 -11.95942278124406 0.0004864450394208575 0 1 0 +332 1 2 2.1 0.669109464863638 -13.549995278717017 2.757315210932033 0 1 0 +333 1 2 2.1 0.7488989807994404 -10.594878651163997 2.765293220622354 0 1 0 +334 1 3 -1.05 0.5277433362522466 -13.479085130961657 1.1390393870012794 0 1 0 +335 1 3 -1.05 0.8831162860029735 -10.68765806499949 1.1404014490301666 0 1 0 +336 1 4 -0.95 3.4006984714464785 -11.886441915318503 1.009279146529078 0 1 0 +337 1 3 -1.05 2.05448186886235 -9.89360444283963 3.2389889279451722 0 1 0 +338 1 3 -1.05 0.6346935230510518 -12.069165638686224 3.240739110626995 0 1 0 +339 1 3 -1.05 -0.5159464707475578 -9.741757212644893 3.0491802687542187 0 1 0 +340 1 5 0.425 2.892219107482818 -12.76549622369036 1.1480452697934158 0 1 0 +341 1 1 1.575 2.5033520639114464 -10.45516467544924 -0.0004897125671803337 0 1 0 +351 1 1 1.575 -0.10480281833843286 -14.938050279884052 -0.00048588915579017566 0 1 0 +361 1 1 1.575 7.6476430809013145 -16.442304686736612 0.00049038023432324 0 1 0 +364 1 3 -1.05 8.239572543766378 -17.962013382666825 1.1390663747804606 0 1 0 +366 1 4 -0.95 5.952569848038642 -16.369322613306085 1.009292148021041 0 1 0 +367 1 3 -1.05 4.60638206298816 -14.376506603214507 3.2389897937288765 0 1 0 +369 1 3 -1.05 7.195891896594336 -14.224654560331162 3.049163809309336 0 1 0 +370 1 5 0.425 5.444033924435217 -17.248408369389658 1.14812008570199 0 1 0 +372 1 2 2.1 5.829092303814921 -13.54999199012334 2.757318610834842 0 1 0 +373 1 2 2.1 5.90887749264844 -10.594869830017455 2.7652903021076654 0 1 0 +374 1 3 -1.05 5.6877459594373825 -13.479086764973678 1.1390432596793474 0 1 0 +375 1 3 -1.05 6.043115538450444 -10.687657699182285 1.1403981434855215 0 1 0 +377 1 3 -1.05 7.214508812427347 -9.893588586527766 3.2389864017305623 0 1 0 +378 1 3 -1.05 5.794691970346413 -12.069189428924563 3.240738236405365 0 1 0 +379 1 3 -1.05 4.644066780347256 -9.741765351175818 3.0491815811632907 0 1 0 +381 1 1 1.575 7.663359745050162 -10.455168066687937 -0.0004892393395401484 0 1 0 +391 1 1 1.575 5.055189932412219 -14.938048027032371 -0.0004872860180871186 0 1 0 +451 1 1 1.575 -0.06419364919500303 -11.959425986989281 0.0004869662395279306 1 1 0 +460 1 5 0.425 -2.2677636282949134 -12.76549803894805 1.148075362714911 1 1 0 +824 1 3 -1.05 0.1581453413464491 -18.083120565372205 8.050830868008626 0 1 0 +864 1 3 -1.05 5.318142529558035 -18.083119031532103 8.050827887575677 0 1 0 +982 1 2 2.1 0.01679048455388532 -18.01223102881715 6.432564492236768 0 1 0 +983 1 2 2.1 -0.0066952224139544825 -12.001572696194678 6.424588219561466 0 1 0 +985 1 3 -1.05 -0.14091080181982996 -11.90871125431586 8.049492287832761 0 1 0 +986 1 4 -0.95 2.501485979919419 -10.709979047674722 8.180577187844913 0 1 0 +987 1 3 -1.05 3.8476721766409234 -12.702792935609825 5.9508827991561954 0 1 0 +988 1 3 -1.05 0.10753867741342837 -10.527082522856396 5.949125116992775 0 1 0 +989 1 3 -1.05 1.2581766829054715 -12.854651901566331 6.140708678514933 0 1 0 +990 1 5 0.425 3.010011029658287 -9.83088482771142 8.041736728168713 0 1 0 +992 1 2 2.1 2.624959204051148 -13.529306355082204 6.43255142132813 0 1 0 +993 1 2 2.1 2.545164633028703 -16.48443503403599 6.42457746374043 0 1 0 +994 1 3 -1.05 2.7663148605711587 -13.600211881262618 8.050823710986089 0 1 0 +995 1 3 -1.05 2.4109413776533053 -16.391641111187187 8.049471733472652 0 1 0 +996 1 4 -0.95 -0.1066473719781591 -15.192867242576972 8.180586172547835 0 1 0 +997 1 3 -1.05 1.2395719193215697 -17.185696569519184 5.950884733429518 0 1 0 +998 1 3 -1.05 2.659366057346901 -15.010107680149012 5.949132937132072 0 1 0 +999 1 3 -1.05 3.809991544530318 -17.337535214117086 6.140692372135046 0 1 0 +1000 1 5 0.425 0.40182382453956933 -14.313808115235338 8.041767358684657 0 1 0 +1022 1 2 2.1 5.176806602000145 -18.01223478887661 6.432561551916441 0 1 0 +1023 1 2 2.1 5.153325970226241 -12.001581171312154 6.424591592957077 0 1 0 +1025 1 3 -1.05 5.019090010771908 -11.908711671328648 8.04949592036308 0 1 0 +1028 1 3 -1.05 5.267540194467005 -10.527058835775907 5.9491260060330475 0 1 0 +1029 1 3 -1.05 6.418164130946273 -12.854644032937445 6.140707132764247 0 1 0 +1030 1 5 0.425 -12.469972004930579 -9.830886011965626 8.04176389538827 1 1 0 +1036 1 4 -0.95 5.053359381870932 -15.192857077416427 8.18058886353535 0 1 0 +1037 1 3 -1.05 6.399549290938857 -17.18570880002915 5.95088487910323 0 1 0 +1040 1 5 0.425 5.561834684837105 -14.313806813221188 8.041810608039574 0 1 0 +1107 1 3 -1.05 -1.3122999739981296 -12.70277646304137 5.950880254538765 1 1 0 +1119 1 3 -1.05 -1.3499973134743044 -17.337541538579533 6.140690843939177 1 1 0 +362 1 2 2.1 -12.202744771981509 -9.067064814200972 2.7573119706868425 1 1 0 +402 1 2 2.1 -7.04273866849521 -9.067055088426352 2.757307939589456 1 1 0 +442 1 2 2.1 -1.8827442162924566 -9.067062967529901 2.7573120570116707 1 1 0 +523 1 2 2.1 -12.122921769822408 -6.111929983949841 2.765291027036895 1 1 0 +525 1 3 -1.05 -11.988703560905819 -6.204760843795567 1.1403957796501452 1 1 0 +528 1 3 -1.05 -12.237149424227058 -7.5863238195216365 3.2407527420150988 1 1 0 +531 1 1 1.575 -10.327858771543793 -2.9935731989533902 0.0004888265444922268 1 1 0 +536 1 4 -0.95 -12.022931150481051 -2.920580487258352 1.0093004267478776 1 1 0 +540 1 5 0.425 8.108558594315166 -3.7996512054948504 1.148212040044502 0 1 0 +561 1 1 1.575 -7.776025262711729 -7.4765049298112505 0.0004881991149989773 1 1 0 +563 1 2 2.1 -6.962930286971465 -6.111939824800135 2.765283881410019 1 1 0 +564 1 3 -1.05 -7.184085023643926 -8.99618073281571 1.1390399029648446 1 1 0 +565 1 3 -1.05 -6.828701808117627 -6.204761586454861 1.1403901964143177 1 1 0 +566 1 4 -0.95 -9.471118879929094 -7.403529138856177 1.0092745530484954 1 1 0 +567 1 3 -1.05 -10.817325824488437 -5.4107065420677785 3.2389947847718457 1 1 0 +568 1 3 -1.05 -7.07714607080063 -7.586320418897543 3.240750734425326 1 1 0 +569 1 3 -1.05 -8.227783475860987 -5.258830022264039 3.0491700040117298 1 1 0 +570 1 5 0.425 -9.979621696041477 -8.282638958710844 1.14794634211753 1 1 0 +571 1 1 1.575 -5.167865926094019 -2.9935700449751117 0.0004884982994557419 1 1 0 +572 1 2 2.1 -9.594553208812169 -4.584114830084795 2.7573247242772645 1 1 0 +573 1 2 2.1 -9.514752964904952 -1.6290031107655665 2.76528557294675 1 1 0 +574 1 3 -1.05 -9.735924934424778 -4.5132754309991014 1.1390582524844408 1 1 0 +575 1 3 -1.05 -9.380536654292207 -1.7218462684346072 1.140382320970689 1 1 0 +576 1 4 -0.95 -6.862929549230458 -2.92058282567767 1.0092939379568069 1 1 0 +577 1 3 -1.05 -8.209153404187742 -0.9277818004229132 3.238988388541747 1 1 0 +578 1 3 -1.05 -9.62897585444286 -3.103458654146092 3.240740428512092 1 1 0 +579 1 3 -1.05 -10.779616213891364 -0.7759196991747856 3.049168880743542 1 1 0 +580 1 5 0.425 -7.371458392180491 -3.799648965132267 1.1481816702921979 1 1 0 +581 1 1 1.575 -7.760307453210186 -1.4893294292081407 -0.000487591834845702 1 1 0 +591 1 1 1.575 -10.368475011993443 -5.972216450792448 -0.0004866657689603926 1 1 0 +601 1 1 1.575 -2.6160175804425334 -7.476507331401965 0.000489554412521187 1 1 0 +603 1 2 2.1 -1.8029234809179044 -6.111929469506379 2.765290938817035 1 1 0 +604 1 3 -1.05 -2.0240895156060503 -8.996180475658797 1.1390497095690097 1 1 0 +605 1 3 -1.05 -1.6687035111919997 -6.204760947315812 1.1403951214536825 1 1 0 +606 1 4 -0.95 -4.311125240437589 -7.40353853925054 1.0092720638271473 1 1 0 +607 1 3 -1.05 -5.657299784603431 -5.4106924097233176 3.2389943904105305 1 1 0 +608 1 3 -1.05 -1.9171495422821216 -7.586325941483118 3.240752466809111 1 1 0 +609 1 3 -1.05 -3.0677722086720367 -5.258836440196976 3.049168256994184 1 1 0 +610 1 5 0.425 -4.819631740010255 -8.28264108406943 1.147905363302021 1 1 0 +612 1 2 2.1 -4.434571167364412 -4.584112138551882 2.757328005439472 1 1 0 +613 1 2 2.1 -4.35477408472807 -1.6289950360376295 2.765282611037348 1 1 0 +614 1 3 -1.05 -4.575922294211849 -4.513277152884633 1.1390617721334308 1 1 0 +615 1 3 -1.05 -4.220537459731567 -1.7218458350906225 1.1403790499316706 1 1 0 +616 1 4 -0.95 -1.7029315494609438 -2.920581286855313 1.009300527997441 1 1 0 +617 1 3 -1.05 -3.049127074634125 -0.9277662650700513 3.238986045411176 1 1 0 +618 1 3 -1.05 -4.468977447340732 -3.1034809491568094 3.2407397732533028 1 1 0 +619 1 3 -1.05 -5.619602858246474 -0.7759278254115536 3.049170295315518 1 1 0 +621 1 1 1.575 -2.600299716589224 -1.4893328740682321 -0.0004870341551566071 1 1 0 +631 1 1 1.575 -5.208482201015078 -5.972214134547372 -0.00048794280472819196 1 1 0 +1064 1 3 -1.05 -10.105516488566403 -9.1172864545765 8.050812017095526 1 1 0 +1104 1 3 -1.05 -4.94551950969972 -9.117284875156573 8.050809425873682 1 1 0 +1186 1 4 -0.95 -12.922163181072401 -1.7441480862858398 8.180580989048229 1 1 0 +1187 1 3 -1.05 -11.57594038211662 -3.736962709780661 5.950884644536357 1 1 0 +1192 1 2 2.1 -12.798703976852076 -4.563477223935855 6.4325671062022085 1 1 0 +1193 1 2 2.1 -12.87850157426794 -7.518594866582022 6.4245838131711785 1 1 0 +1194 1 3 -1.05 -12.657357075069436 -4.634391310577776 8.050841207753615 1 1 0 +1195 1 3 -1.05 -13.012730287598115 -7.425811152679454 8.049471003457095 1 1 0 +1198 1 3 -1.05 -12.764298791768608 -6.044310212087346 5.949124918515372 1 1 0 +1199 1 3 -1.05 -11.613660044905913 -8.371724217557164 6.1406940776695595 1 1 0 +1222 1 2 2.1 -10.246880790109062 -9.046458105402944 6.432550667813766 1 1 0 +1223 1 2 2.1 -10.270345504929077 -3.0357170181300006 6.4245835383340175 1 1 0 +1225 1 3 -1.05 -10.404563440926587 -2.942901247124354 8.049482833097352 1 1 0 +1226 1 4 -0.95 -7.762161044690064 -1.744149770475424 8.180574812461733 1 1 0 +1227 1 3 -1.05 -6.415966116593134 -3.73697764701976 5.9508871807473405 1 1 0 +1228 1 3 -1.05 -10.156132949827974 -1.561327845464941 5.94913393800597 1 1 0 +1229 1 3 -1.05 -9.005492655293018 -3.8888143704750213 6.140697709391448 1 1 0 +1230 1 5 0.425 -7.2536721407950715 -0.8650918391092688 8.041681601019869 1 1 0 +1232 1 2 2.1 -7.638697839125399 -4.56346976541589 6.432562555622445 1 1 0 +1233 1 2 2.1 -7.718506635912108 -7.518604577415662 6.424576758095252 1 1 0 +1234 1 3 -1.05 -7.497352805807434 -4.634391240549473 8.05083153086409 1 1 0 +1235 1 3 -1.05 -7.852728552110221 -7.425811958730723 8.04946655754084 1 1 0 +1236 1 4 -0.95 -10.370302228905821 -6.227020102282678 8.180601687842392 1 1 0 +1237 1 3 -1.05 -9.024099865185532 -8.219863735211376 5.950878868492627 1 1 0 +1238 1 3 -1.05 -7.604295208524743 -6.044303920000971 5.949123349425319 1 1 0 +1239 1 3 -1.05 -6.453672021320111 -8.371717534647717 6.140695622062312 1 1 0 +1240 1 5 0.425 -9.861830566687258 -5.347927183164366 8.041954081983627 1 1 0 +1262 1 2 2.1 -5.086864118735355 -9.046460824361695 6.432547962331752 1 1 0 +1263 1 2 2.1 -5.110325060390764 -3.0357264779902415 6.4245866001619785 1 1 0 +1265 1 3 -1.05 -5.2445627758652025 -2.942901539041836 8.049486192414905 1 1 0 +1266 1 4 -0.95 -2.6021638449486817 -1.7441493140479167 8.180581250684453 1 1 0 +1268 1 3 -1.05 -4.996131563332382 -1.561304142262447 5.949134847146771 1 1 0 +1269 1 3 -1.05 -3.845505208560062 -3.888806435255866 6.140696375915624 1 1 0 +1270 1 5 0.425 -2.0936552768300416 -0.8650931226092098 8.041708750676337 1 1 0 +1272 1 2 2.1 -2.478705083415977 -4.563478530750267 6.432566771448263 1 1 0 +1273 1 2 2.1 -2.5585004268402667 -7.518594042662386 6.424583683031381 1 1 0 +1274 1 3 -1.05 -2.337357058590765 -4.6343911683899695 8.050841730975934 1 1 0 +1275 1 3 -1.05 -2.692730278146584 -7.4258112933116 8.049471495897967 1 1 0 +1276 1 4 -0.95 -5.210295954483267 -6.227010516473889 8.180604547038248 1 1 0 +1277 1 3 -1.05 -3.86412183624161 -8.219875620457026 5.950879044037668 1 1 0 +1278 1 3 -1.05 -2.4442987304969392 -6.044309813785304 5.949125141897687 1 1 0 +1280 1 5 0.425 -4.701819271798077 -5.3479252345724095 8.041997228930375 1 1 0 +322 1 2 2.1 3.2772610098674093 -9.067055693067015 2.7573080000114008 0 1 0 +481 1 1 1.575 2.5439746362228775 -7.476504835357023 0.000488107312015984 0 1 0 +483 1 2 2.1 3.3570675650653765 -6.1119409913516805 2.765283658968608 0 1 0 +484 1 3 -1.05 3.1359149983488273 -8.996180653887704 1.1390399010220378 0 1 0 +485 1 3 -1.05 3.4912982864764803 -6.204761586269585 1.1403902809350122 0 1 0 +486 1 4 -0.95 0.848880935670131 -7.403529456906714 1.0092745179848404 0 1 0 +487 1 3 -1.05 -0.4973223654953767 -5.4107043825084595 3.2389946445963442 0 1 0 +488 1 3 -1.05 3.2428541215929787 -7.586319587782093 3.2407506030975757 0 1 0 +489 1 3 -1.05 2.0922173637001134 -5.258830509141244 3.049170069120102 0 1 0 +490 1 5 0.425 0.34037849430467304 -8.282638585494373 1.1479463507303809 0 1 0 +491 1 1 1.575 5.152133947531471 -2.9935699784041603 0.0004881234041693716 0 1 0 +492 1 2 2.1 0.7254445666488039 -4.58411548963857 2.7573250239711573 0 1 0 +493 1 2 2.1 0.8052450939405311 -1.6290034167113632 2.7652851130343965 0 1 0 +494 1 3 -1.05 0.5840751961211161 -4.513275537476634 1.139058491185871 0 1 0 +495 1 3 -1.05 0.9394635072903537 -1.7218461634728612 1.1403819751435265 0 1 0 +496 1 4 -0.95 3.457070537219302 -2.9205829409048096 1.0092940260285932 0 1 0 +497 1 3 -1.05 2.1108492600941755 -0.9277801182782781 3.2389884882436597 0 1 0 +498 1 3 -1.05 0.6910242115697844 -3.103459227797069 3.240740522829608 0 1 0 +499 1 3 -1.05 -0.45961486738614177 -0.7759203181741157 3.049168941857401 0 1 0 +500 1 5 0.425 2.9485416775480626 -3.7996487609851872 1.148181457575495 0 1 0 +501 1 1 1.575 2.5596923414053414 -1.4893294760794085 -0.00048791691717831043 0 1 0 +511 1 1 1.575 -0.048475029845510775 -5.9722163822729275 -0.000486610148280775 0 1 0 +521 1 1 1.575 7.703982404569835 -7.47650743047992 0.0004894906663004406 0 1 0 +524 1 3 -1.05 8.295910521844277 -8.996180577499706 1.1390498499011983 0 1 0 +526 1 4 -0.95 6.008874247849864 -7.403539256615227 1.009272347205897 0 1 0 +527 1 3 -1.05 4.662703122988951 -5.410690550977408 3.238994424282833 0 1 0 +529 1 3 -1.05 7.25222715620599 -5.258835994797554 3.0491686153219923 0 1 0 +530 1 5 0.425 5.500368804549042 -8.28264025862713 1.1479051971829524 0 1 0 +532 1 2 2.1 5.885427782213334 -4.584112735919096 2.7573281465655857 0 1 0 +533 1 2 2.1 5.965226467012172 -1.628993361989938 2.7652825479136283 0 1 0 +534 1 3 -1.05 5.74407777792679 -4.5132770146071675 1.1390625883841388 0 1 0 +535 1 3 -1.05 6.0994626404410575 -1.7218459113188977 1.1403796100854198 0 1 0 +537 1 3 -1.05 7.270871098481489 -0.9277673866200544 3.2389858821101694 0 1 0 +538 1 3 -1.05 5.8510226007914845 -3.103482215879012 3.240739632064548 0 1 0 +539 1 3 -1.05 4.7003977117094315 -0.7759282649032926 3.0491702761655297 0 1 0 +541 1 1 1.575 7.71970016267743 -1.4893327968293981 -0.0004872882145861013 0 1 0 +551 1 1 1.575 5.111517638351028 -5.972214169324591 -0.00048815723114969956 0 1 0 +611 1 1 1.575 -0.0078587283292606 -2.9935732581044245 0.0004888922267749507 1 1 0 +620 1 5 0.425 -2.211440966107384 -3.7996504259139385 1.1482119261243664 1 1 0 +984 1 3 -1.05 0.21448346242866556 -9.117286626928372 8.050812484857788 0 1 0 +1024 1 3 -1.05 5.374480667058524 -9.1172847465118 8.050809703370929 0 1 0 +1142 1 2 2.1 0.07311912259206466 -9.046457539168534 6.432550791979782 0 1 0 +1143 1 2 2.1 0.049653605296850145 -3.0357192834675537 6.424583355385289 0 1 0 +1145 1 3 -1.05 -0.0845634540740896 -2.9429011311913413 8.049482748208861 0 1 0 +1146 1 4 -0.95 2.5578388527215647 -1.7441499838946513 8.180574701561904 0 1 0 +1147 1 3 -1.05 3.904034789903749 -3.7369772016518894 5.950886974857758 0 1 0 +1148 1 3 -1.05 0.1638671308906794 -1.5613255896995106 5.949134016742535 0 1 0 +1149 1 3 -1.05 1.3145065630773924 -3.8888138098807907 6.140697691304018 0 1 0 +1150 1 5 0.425 3.0663280045002406 -0.8650917177898982 8.041681658582819 0 1 0 +1152 1 2 2.1 2.681301696828717 -4.563470951334201 6.432562677791433 0 1 0 +1153 1 2 2.1 2.6014924415491016 -7.518605844210883 6.42457670393048 0 1 0 +1154 1 3 -1.05 2.8226473559855396 -4.634391279296553 8.050831493737332 0 1 0 +1155 1 3 -1.05 2.467271583937638 -7.425811889492945 8.049466671649265 0 1 0 +1156 1 4 -0.95 -0.050302377863054915 -6.227020357623573 8.18060186788391 0 1 0 +1157 1 3 -1.05 1.2959016487701724 -8.219862803300558 5.950878899595457 0 1 0 +1158 1 3 -1.05 2.715705046173925 -6.0443024015894125 5.949123183070215 0 1 0 +1159 1 3 -1.05 3.866328082096553 -8.371717396852594 6.14069563666442 0 1 0 +1160 1 5 0.425 0.45816960045136135 -5.347926987828261 8.041953993827088 0 1 0 +1182 1 2 2.1 5.233134204042219 -9.046462615939763 6.432548060968001 0 1 0 +1183 1 2 2.1 5.209674691687077 -3.035725777150798 6.42458687581861 0 1 0 +1185 1 3 -1.05 5.075437238345243 -2.9429016177355063 8.0494859105205 0 1 0 +1188 1 3 -1.05 5.323868516938102 -1.5613057900237983 5.949134653550393 0 1 0 +1189 1 3 -1.05 6.474494628261173 -3.8888064118199335 6.140696088678768 0 1 0 +1190 1 5 0.425 -12.413655946990202 -0.8650943030890836 8.041708999745945 1 1 0 +1196 1 4 -0.95 5.109703718829179 -6.227011235494134 8.180604680522054 0 1 0 +1197 1 3 -1.05 6.455880799282653 -8.219873725450045 5.950878826176888 0 1 0 +1200 1 5 0.425 5.618181133036563 -5.347924456811681 8.0419973088112 0 1 0 +1267 1 3 -1.05 -1.255938118796582 -3.736961290112877 5.95088464227411 1 1 0 +1279 1 3 -1.05 -1.293660683856988 -8.371724040207935 6.140694136436416 1 1 0 +43 1 2 2.1 -12.066603589864952 2.8538562635186473 2.765296766562731 1 0 0 +45 1 3 -1.05 -11.932382242280934 2.7610755310900856 1.1404071534983906 1 0 0 +48 1 3 -1.05 -12.180808606969753 1.3795772494375846 3.2407446680776673 1 0 0 +51 1 1 1.575 -10.271517178678891 5.972243043476983 0.0004897808600361486 1 0 0 +56 1 4 -0.95 -11.966601677508535 6.045223061069063 1.0092868522084313 1 0 0 +60 1 5 0.425 8.164880641989306 5.166117461622548 1.1480432930863778 0 0 0 +81 1 1 1.575 -7.719695704081541 1.489301984598221 0.00048620424004042206 1 0 0 +83 1 2 2.1 -6.906611830118906 2.853845289659727 2.765289513921303 1 0 0 +84 1 3 -1.05 -7.1277496439282455 -0.03034856107450068 1.1390300687638835 1 0 0 +85 1 3 -1.05 -6.772380470440207 2.7610747346770914 1.1404021270114981 1 0 0 +86 1 4 -0.95 -9.414807068563295 1.5622817433164506 1.0092737068243878 1 0 0 +87 1 3 -1.05 -10.761016708915546 3.5551257680232844 3.2389908036271358 1 0 0 +88 1 3 -1.05 -7.020805055588193 1.3795831051103633 3.2407426091838403 1 0 0 +89 1 3 -1.05 -8.171444330233143 3.706977523559388 3.0491815779021074 1 0 0 +90 1 5 0.425 -9.92326936174715 0.6832053400645641 1.1479687029894574 1 0 0 +91 1 1 1.575 -5.111524290678346 5.972246284049945 0.0004894123271252937 1 0 0 +92 1 2 2.1 -9.53822873762858 4.381702400713934 2.7573155444923465 1 0 0 +93 1 2 2.1 -9.458410238013869 7.33682068730624 2.765285098253017 1 0 0 +94 1 3 -1.05 -9.679588290829658 4.45254795692637 1.139053534311218 1 0 0 +95 1 3 -1.05 -9.324195367300185 7.243969020237035 1.140384524236497 1 0 0 +96 1 4 -0.95 -6.806599492258481 6.045221693669681 1.009280187757005 1 0 0 +97 1 3 -1.05 -8.152808576570859 8.038030966821896 3.2389942981918 1 0 0 +98 1 3 -1.05 -9.572645830808028 5.862365851389207 3.2407504502190747 1 0 0 +99 1 3 -1.05 -10.723283583484175 8.18991092687909 3.0491638601374564 1 0 0 +100 1 5 0.425 -7.315136856531225 5.166118893998583 1.1480131499026296 1 0 0 +101 1 1 1.575 -7.703976890906669 7.476521396452124 -0.0004869418693242977 1 0 0 +111 1 1 1.575 -10.312141649453219 2.9935784879242497 -0.0004884588124696165 1 0 0 +121 1 1 1.575 -2.5596879766050353 1.4892994022197463 0.0004876271542961774 1 0 0 +123 1 2 2.1 -1.7466035808200573 2.8538558037971953 2.7652965023822897 1 0 0 +124 1 3 -1.05 -1.9677540888021028 -0.03034837344207375 1.1390396821506403 1 0 0 +126 1 4 -0.95 -4.25481364789182 1.5622718466590193 1.009271365196117 1 0 0 +127 1 3 -1.05 -5.6009918988674805 3.55513912460707 3.2389905823258385 1 0 0 +128 1 3 -1.05 -1.860808583710753 1.3795779024677586 3.240744602344904 1 0 0 +129 1 3 -1.05 -3.0114343038263804 3.7069717528642663 3.04917999216428 1 0 0 +130 1 5 0.425 -4.763279165866927 0.6832035674608612 1.1479273728367207 1 0 0 +132 1 2 2.1 -4.378245338999152 4.38170538944145 2.7573185647135965 1 0 0 +133 1 2 2.1 -4.298430131568423 7.336829694841267 2.765282372169972 1 0 0 +134 1 3 -1.05 -4.519585788114593 4.452546370179327 1.1390572508955117 1 0 0 +135 1 3 -1.05 -4.164196247857843 7.243969567402647 1.1403816035253787 1 0 0 +137 1 3 -1.05 -2.992784551459188 8.03804507272551 3.238991727054886 1 0 0 +138 1 3 -1.05 -4.412647569640212 5.86234321615736 3.240749786939869 1 0 0 +139 1 3 -1.05 -5.563270268907923 8.189902706625428 3.049165190799913 1 0 0 +141 1 1 1.575 -2.543969087429206 7.476517985115045 -0.0004862871680106906 1 0 0 +151 1 1 1.575 -5.1521487769677075 2.9935806762264434 -0.0004901006836668387 1 0 0 +522 1 2 2.1 -12.14640321562067 -0.10127600189234087 2.7573120460055964 1 1 0 +562 1 2 2.1 -6.986398207724864 -0.1012687687102094 2.757308016998312 1 1 0 +602 1 2 2.1 -1.826404614623005 -0.10127704899093004 2.757311825180798 1 1 0 +706 1 4 -0.95 -12.865796022624288 7.221714492772325 8.180601207822123 1 0 0 +707 1 3 -1.05 -11.519585625908963 5.228867443825511 5.950880624420204 1 0 0 +712 1 2 2.1 -12.74236927286661 4.402289337375663 6.432558526054466 1 0 0 +713 1 2 2.1 -12.822179576740067 1.447177436755961 6.424591364431221 1 0 0 +714 1 3 -1.05 -12.601019912922663 4.331444765726772 8.050823657348074 1 0 0 +715 1 3 -1.05 -12.9564089613341 1.5400239285377246 8.049490036152141 1 0 0 +718 1 3 -1.05 -12.707959347486435 2.9216270213267173 5.949122287867622 1 0 0 +719 1 3 -1.05 -11.557321491073434 0.5940835324113181 6.140706214927789 1 0 0 +742 1 2 2.1 -10.190553470512224 -0.08060471526624369 6.432549047518183 1 0 0 +743 1 2 2.1 -10.214006128678415 5.930145239857023 6.424577571546896 1 0 0 +744 1 3 -1.05 -9.992850905501367 8.814341090685996 8.050837610229921 1 0 0 +745 1 3 -1.05 -10.348222732989003 6.022917560970665 8.049466320772085 1 0 0 +746 1 4 -0.95 -7.705793610382077 7.221713363185231 8.180594909971527 1 0 0 +747 1 3 -1.05 -6.35961233585525 5.228852016801575 5.9508831678014555 1 0 0 +748 1 3 -1.05 -10.099798574650077 7.404404225630994 5.9491289200407405 1 0 0 +749 1 3 -1.05 -8.949159917306673 5.077015823046679 6.140691533239114 1 0 0 +750 1 5 0.425 -7.197339347246486 8.100783891909586 8.041891733507144 1 0 0 +752 1 2 2.1 -7.582363175185557 4.40229803595107 6.4325545097206405 1 0 0 +753 1 2 2.1 -7.66218773373044 1.447166595599981 6.424584171396804 1 0 0 +754 1 3 -1.05 -7.441015439936198 4.33144478045222 8.050813764214633 1 0 0 +755 1 3 -1.05 -7.796407081280542 1.5400232594796925 8.04948484594699 1 0 0 +756 1 4 -0.95 -10.314003547549689 2.7387701229844446 8.180589327508356 1 0 0 +757 1 3 -1.05 -8.96779392146795 0.7459625241507837 5.950878464150257 1 0 0 +758 1 3 -1.05 -7.547955743292837 2.9216319108547033 5.949120478977855 1 0 0 +759 1 3 -1.05 -6.3973326315710946 0.5940898261857264 6.140707747743219 1 0 0 +760 1 5 0.425 -9.805482010992158 3.617879643362997 8.041844519342556 1 0 0 +782 1 2 2.1 -5.03053752216915 -0.08060905658925677 6.432546163227055 1 0 0 +783 1 2 2.1 -5.053985551934414 5.930137706277755 6.424580953091224 1 0 0 +784 1 3 -1.05 -4.832853756870046 8.814342658293356 8.050834914156995 1 0 0 +785 1 3 -1.05 -5.188222049286011 6.022917177987889 8.049469621886916 1 0 0 +786 1 4 -0.95 -2.54579593414841 7.221714309210501 8.180601184681164 1 0 0 +788 1 3 -1.05 -4.939797262145818 7.404425893631185 5.949129853518169 1 0 0 +789 1 3 -1.05 -3.7891712049732114 5.0770228189984365 6.140690056642459 1 0 0 +790 1 5 0.425 -2.0373227716303397 8.10078202006681 8.041919069850836 1 0 0 +792 1 2 2.1 -2.4223707917632114 4.4022905679840285 6.432558910582037 1 0 0 +793 1 2 2.1 -2.502180902736985 1.4471777819542027 6.424591034963024 1 0 0 +794 1 3 -1.05 -2.281019735917397 4.331444825132113 8.05082378781694 1 0 0 +795 1 3 -1.05 -2.636408851343842 1.5400238125742547 8.049489411652035 1 0 0 +796 1 4 -0.95 -5.15399710310596 2.7387798895711164 8.180592034792975 1 0 0 +797 1 3 -1.05 -3.8078161189370086 0.7459504928387659 5.950878669052271 1 0 0 +798 1 3 -1.05 -2.3879594652644123 2.921624344781165 5.949122369199204 1 0 0 +800 1 5 0.425 -4.645471004162632 3.6178812975920884 8.04188765083781 1 0 0 +902 1 2 2.1 -10.13421204796398 8.885266130047459 6.432562263168402 1 0 0 +942 1 2 2.1 -4.974195624506329 8.885262572218696 6.432559619577779 1 0 0 +1224 1 3 -1.05 -10.049182067561132 -0.1514710749753796 8.050819995602664 1 1 0 +1264 1 3 -1.05 -4.889184949315168 -0.15146950774747125 8.050816947112386 1 1 0 +1 1 1 1.575 2.6003042619820214 1.489301985987158 0.00048617890439572875 0 0 0 +3 1 2 2.1 3.413388030780098 2.853846993110462 2.7652894411413254 0 0 0 +4 1 3 -1.05 3.192250473216461 -0.030348573470597273 1.1390305483599548 0 0 0 +5 1 3 -1.05 3.5476195187832538 2.7610747149761714 1.1404022330734858 0 0 0 +6 1 4 -0.95 0.9051929855174699 1.5622817529618267 1.0092737393651063 0 0 0 +7 1 3 -1.05 -0.44101638859063463 3.5551258758509157 3.238990910738922 0 0 0 +8 1 3 -1.05 3.2991948449339965 1.3795809791892317 3.240742744153204 0 0 0 +9 1 3 -1.05 2.1485570809573353 3.706976696393621 3.0491817246607322 0 0 0 +10 1 5 0.425 0.39673058856233645 0.6832052188137645 1.1479686487541496 0 0 0 +11 1 1 1.575 5.208475641812152 5.972246199066358 0.0004892910943752327 0 0 0 +12 1 2 2.1 0.7817706041936265 4.38170355408236 2.7573153336218788 0 0 0 +13 1 2 2.1 0.8615899292224434 7.336820998825839 2.7652848366160274 0 0 0 +14 1 3 -1.05 0.6404116649888252 4.452548002411522 1.1390536212036224 0 0 0 +15 1 3 -1.05 0.9958046216279932 7.243969023731864 1.1403840581284594 0 0 0 +16 1 4 -0.95 3.513400407792714 6.045221555334809 1.0092802794076725 0 0 0 +17 1 3 -1.05 2.1671922380352218 8.038031393312668 3.238994437034231 0 0 0 +18 1 3 -1.05 0.7473539186626681 5.862364303969713 3.2407507095117243 0 0 0 +19 1 3 -1.05 -0.40328409120112596 8.189911245998463 3.049163882753348 0 0 0 +20 1 5 0.425 3.004863256879574 5.166119171117636 1.148013285968382 0 0 0 +21 1 1 1.575 2.6160231450750047 7.476521308095869 -0.0004868964845172741 0 0 0 +31 1 1 1.575 0.007858486069929782 2.9935784838606985 -0.0004886229058236324 0 0 0 +41 1 1 1.575 7.7603119800836495 1.4892994847808687 0.0004876409956668937 0 0 0 +44 1 3 -1.05 8.352245958178369 -0.03034852708280411 1.1390398478533665 0 0 0 +46 1 4 -0.95 6.065186680101519 1.5622726468860186 1.0092713469534331 0 0 0 +47 1 3 -1.05 4.719006945859089 3.5551385256659387 3.2389905613454264 0 0 0 +49 1 3 -1.05 7.308566510413744 3.7069713740300188 3.0491800116188568 0 0 0 +50 1 5 0.425 5.55672029157318 0.6832027274139385 1.1479273774212633 0 0 0 +52 1 2 2.1 5.941754691612481 4.381705907161461 2.7573185281719876 0 0 0 +53 1 2 2.1 6.021569156148551 7.336829473327171 2.765282260596237 0 0 0 +54 1 3 -1.05 5.800414227629432 4.452546389521338 1.1390571190245424 0 0 0 +55 1 3 -1.05 6.155803761978959 7.243969506923385 1.1403815022420147 0 0 0 +57 1 3 -1.05 7.327216945332573 8.038046038385499 3.2389918433149454 0 0 0 +58 1 3 -1.05 5.90735237093768 5.8623431966956865 3.2407497213706336 0 0 0 +59 1 3 -1.05 4.756730041223639 8.189902512772619 3.0491652979454305 0 0 0 +61 1 1 1.575 7.776030915957783 7.4765179856259465 -0.00048630439592045605 0 0 0 +71 1 1 1.575 5.1678511824652595 2.993580880857408 -0.0004901999814084235 0 0 0 +125 1 3 -1.05 -1.6123821854260463 2.761075314653997 1.1404070154366295 1 0 0 +131 1 1 1.575 0.04848294209673121 5.972242935779885 0.000490037179456948 1 0 0 +136 1 4 -0.95 -1.6466013343857568 6.0452237602823296 1.009286692899492 1 0 0 +140 1 5 0.425 -2.1551197559159263 5.166116856956847 1.1480434751617512 1 0 0 +482 1 2 2.1 3.333600286305689 -0.10126833528058299 2.7573081542690936 0 1 0 +662 1 2 2.1 0.1294481221970365 -0.0806049883627118 6.432548631586574 0 0 0 +663 1 2 2.1 0.10599334680137673 5.930145648028347 6.424577282515786 0 0 0 +664 1 3 -1.05 0.3271491025311537 8.814340912836013 8.050837562340648 0 0 0 +665 1 3 -1.05 -0.02822261760979572 6.022917492990221 8.049465873202388 0 0 0 +666 1 4 -0.95 2.6142064843337565 7.221713579662975 8.180594739766134 0 0 0 +667 1 3 -1.05 3.9603863448931165 5.228851139494434 5.950883261310734 0 0 0 +668 1 3 -1.05 0.22020140066630312 7.404402962508492 5.9491292107555385 0 0 0 +669 1 3 -1.05 1.370839907147385 5.0770158086882695 6.140691527571985 0 0 0 +670 1 5 0.425 3.122660489762424 8.10078354117201 8.041891815867482 0 0 0 +672 1 2 2.1 2.7376378509887616 4.402297453839999 6.432554269024626 0 0 0 +673 1 2 2.1 2.657814143905327 1.4471664246497618 6.4245845586209835 0 0 0 +674 1 3 -1.05 2.8789843747672226 4.331444892670223 8.050813478586486 0 0 0 +675 1 3 -1.05 2.5235929276811255 1.5400231451238362 8.049485296604866 0 0 0 +676 1 4 -0.95 0.005996469612352229 2.7387702634425644 8.180589156812536 0 0 0 +677 1 3 -1.05 1.35220333360148 0.7459606544277797 5.9508784991572234 0 0 0 +678 1 3 -1.05 2.772044403479459 2.921633738753382 5.949120410621978 0 0 0 +679 1 3 -1.05 3.9226663496817054 0.5940905195502779 6.140707539547966 0 0 0 +680 1 5 0.425 0.5145179216393423 3.6178796125064707 8.041844457004787 0 0 0 +702 1 2 2.1 5.2894644486370055 -0.08060750115534532 6.432546281853535 0 0 0 +703 1 2 2.1 5.266015505409589 5.930137771849527 6.424580792396204 0 0 0 +704 1 3 -1.05 5.487146353425208 8.814342589672009 8.050834969489033 0 0 0 +705 1 3 -1.05 5.131778084715114 6.022917199990019 8.049469586914329 0 0 0 +708 1 3 -1.05 5.380202829540677 7.404425443770609 5.949129963862305 0 0 0 +709 1 3 -1.05 6.530827312385206 5.077023734536393 6.14069010936865 0 0 0 +710 1 5 0.425 -12.357322833708851 8.100781892429946 8.041919043812685 1 0 0 +716 1 4 -0.95 5.166002670346272 2.7387795855326154 8.180592061839635 0 0 0 +717 1 3 -1.05 6.512180758213354 0.7459485736595788 5.95087865003107 0 0 0 +720 1 5 0.425 5.674529320524975 3.6178817424015435 8.041887704595718 0 0 0 +787 1 3 -1.05 -1.1995843894029825 5.228868218664132 5.950880728607876 1 0 0 +799 1 3 -1.05 -1.2373222098897543 0.594084068338713 6.140706246118169 1 0 0 +822 1 2 2.1 0.18578805441223878 8.885266915444351 6.432562197797376 0 0 0 +862 1 2 2.1 5.345804278048195 8.885262760376076 6.43255959737005 0 0 0 +1144 1 3 -1.05 0.2708179763475371 -0.15147101591706758 8.050819426100988 0 1 0 +1184 1 3 -1.05 5.430815094845618 -0.1514693876244877 8.050816796018031 0 1 0 +42 1 2 2.1 -12.09006260584677 8.864589177393615 2.7573256224033216 1 0 0 +82 1 2 2.1 -6.930057050782033 8.864597863376343 2.75732164447156 1 0 0 +122 1 2 2.1 -1.7700616265708433 8.864589076582348 2.757325473341277 1 0 0 +203 1 2 2.1 -12.010265577762329 11.819715092153334 2.765291360502074 1 0 0 +205 1 3 -1.05 -11.87604343181911 11.726896004685383 1.1403924325820025 1 0 0 +208 1 3 -1.05 -12.124473345038734 10.345319342352141 3.2407384604436444 1 0 0 +211 1 1 1.575 -10.215182621282619 14.938036557984727 0.00048802927475399827 1 0 0 +216 1 4 -0.95 -11.910303760846821 15.011010860547525 1.009271885162475 1 0 0 +220 1 5 0.425 8.221226954671451 14.131915169973812 1.1479074717195452 0 0 0 +241 1 1 1.575 -7.6633658312451125 10.455150665603007 0.00048674450156838134 1 0 0 +243 1 2 2.1 -6.850274802226975 11.819704709836884 2.765284103462397 1 0 0 +244 1 3 -1.05 -7.071418315464653 8.935464760159874 1.1390470826570223 1 0 0 +245 1 3 -1.05 -6.716041717351411 11.726895262208341 1.1403871825276504 1 0 0 +246 1 4 -0.95 -9.358442661161195 10.528143597063906 1.0092939108167762 1 0 0 +247 1 3 -1.05 -10.704668560055994 12.520955558501814 3.238986452455382 1 0 0 +248 1 3 -1.05 -6.964469919678031 10.345323343327664 3.240736447098582 1 0 0 +249 1 3 -1.05 -8.115109303492375 12.672804410356928 3.0491772060606532 1 0 0 +250 1 5 0.425 -9.86693405976031 9.649084784979703 1.1481844532688399 1 0 0 +251 1 1 1.575 -5.055189905145113 14.938039766588904 0.00048748737759041205 1 0 0 +252 1 2 2.1 -9.481895140515539 13.347470616334537 2.7573054912819455 1 0 0 +253 1 2 2.1 -9.402089479039432 16.30259246114154 2.7652923834422634 1 0 0 +254 1 3 -1.05 -9.623250645681043 13.418384253474247 1.1390345417105667 1 0 0 +255 1 3 -1.05 -9.267873053886584 16.209803149203925 1.1404029722314721 1 0 0 +256 1 4 -0.95 -6.750301966919627 15.011008793832236 1.0092655645710042 1 0 0 +257 1 3 -1.05 -8.096502014882379 17.003855189336587 3.2389947631306786 1 0 0 +258 1 3 -1.05 -9.516307273099796 14.828301501434499 3.2407492682947225 1 0 0 +259 1 3 -1.05 -10.666945431166095 17.155719681097477 3.049175296686906 1 0 0 +260 1 5 0.425 -7.258790173497739 14.131917299137772 1.1478771519031028 1 0 0 +261 1 1 1.575 -7.647647928463388 16.442332287146424 -0.000488821392487182 1 0 0 +271 1 1 1.575 -10.255800064478802 11.959390414342575 -0.0004879826669004217 1 0 0 +281 1 1 1.575 -2.5033581396528186 10.455148150790972 0.0004880508656999183 1 0 0 +283 1 2 2.1 -1.6902660097883206 11.819714042088183 2.765291463935222 1 0 0 +284 1 3 -1.05 -1.9114228701738583 8.935464925204077 1.1390564976711524 1 0 0 +286 1 4 -0.95 -4.198449098102025 10.528133983598376 1.0092915759019423 1 0 0 +287 1 3 -1.05 -5.544642042234459 12.520970094327684 3.238986094778216 1 0 0 +288 1 3 -1.05 -1.8044733565556257 10.345320519191759 3.240738331572988 1 0 0 +289 1 3 -1.05 -2.9550994549008944 12.672798772092221 3.049175605830589 1 0 0 +290 1 5 0.425 -4.706943981189049 9.649082683942861 1.1481432534485005 1 0 0 +292 1 2 2.1 -4.321912545284045 13.347472426595289 2.7573087615778036 1 0 0 +293 1 2 2.1 -4.242109237221538 16.302601544499094 2.765289869034204 1 0 0 +294 1 3 -1.05 -4.463248142025066 13.418382607713053 1.139038490940214 1 0 0 +295 1 3 -1.05 -4.107873920942626 16.209803614349052 1.140400471186064 1 0 0 +297 1 3 -1.05 -2.9364789165855 17.003868750171616 3.2389922567955534 1 0 0 +298 1 3 -1.05 -4.356308934359183 14.828279979627037 3.240748540873943 1 0 0 +299 1 3 -1.05 -5.506931509883287 17.155711082805635 3.049176629135548 1 0 0 +301 1 1 1.575 -2.487640147269909 16.442328816418733 -0.0004883367723138576 1 0 0 +311 1 1 1.575 -5.095807262339934 11.959392592244821 -0.0004895582360084916 1 0 0 +866 1 4 -0.95 -12.809479347924743 16.18753152254509 8.180603331692547 1 0 0 +867 1 3 -1.05 -11.463275683185296 14.194699747323508 5.950876300797919 1 0 0 +872 1 2 2.1 -12.686044332292026 13.368099433193311 6.432547920762799 1 0 0 +873 1 2 2.1 -12.765838937433683 10.41299358974631 6.424592111086573 1 0 0 +874 1 3 -1.05 -12.544682823574899 13.297270345420436 8.050815957208696 1 0 0 +875 1 3 -1.05 -12.900069677630333 10.5058408528262 8.049494880803467 1 0 0 +878 1 3 -1.05 -12.651628832221212 11.887467867095268 5.9491319671647975 1 0 0 +879 1 3 -1.05 -11.500988895646206 9.559912285205048 6.140702884052089 1 0 0 +903 1 2 2.1 -10.15768664396984 14.895937931749142 6.4245820529389 1 0 0 +905 1 3 -1.05 -10.29190066164717 14.988753381285374 8.049475307293187 1 0 0 +906 1 4 -0.95 -7.649476933430761 16.18753061890442 8.180596856550588 1 0 0 +907 1 3 -1.05 -6.303303846872613 14.194683336474714 5.950878682998182 1 0 0 +908 1 3 -1.05 -10.043457772052527 16.370291645323054 5.949120441329194 1 0 0 +909 1 3 -1.05 -8.892821020091677 14.04282418271502 6.140702337720942 1 0 0 +910 1 5 0.425 -7.140987432429453 17.0666357882375 8.041945898966334 1 0 0 +912 1 2 2.1 -7.526036934568197 13.368107272741312 6.4325434453563695 1 0 0 +913 1 2 2.1 -7.605845200775825 10.412982355545267 6.424585246251738 1 0 0 +914 1 3 -1.05 -7.384678562846151 13.297270278808188 8.05080567732344 1 0 0 +915 1 3 -1.05 -7.740067845155377 10.505840284961845 8.049490055106476 1 0 0 +916 1 4 -0.95 -10.257679256880722 11.704569167331858 8.180573427892023 1 0 0 +917 1 3 -1.05 -8.911455963414486 9.711773926551299 5.950884469289509 1 0 0 +918 1 3 -1.05 -7.491625174558855 11.887475216511863 5.949130124537872 1 0 0 +919 1 3 -1.05 -6.341000561150384 9.559918847426733 6.140704372264844 1 0 0 +920 1 5 0.425 -9.749158350506987 12.583644683468911 8.041657698866539 1 0 0 +943 1 2 2.1 -4.997664858400203 14.895930244835991 6.424585607692931 1 0 0 +945 1 3 -1.05 -5.131900069757357 14.98875297667713 8.049478722615055 1 0 0 +946 1 4 -0.95 -2.4894793258925247 16.187531494753113 8.18060331455739 1 0 0 +948 1 3 -1.05 -4.883456385777334 16.37031336364964 5.94912114642281 1 0 0 +949 1 3 -1.05 -3.73283340764449 14.042831878604474 6.140700750137892 1 0 0 +950 1 5 0.425 -1.980970887878998 17.06663388813786 8.041972936030264 1 0 0 +952 1 2 2.1 -2.366044208847562 13.368099483410205 6.432548100519332 1 0 0 +953 1 2 2.1 -2.4458390869474993 10.41299379338496 6.424592232856112 1 0 0 +954 1 3 -1.05 -2.2246828141337502 13.297270286553317 8.050815880203343 1 0 0 +955 1 3 -1.05 -2.580069543536629 10.505840925682026 8.04949477487744 1 0 0 +956 1 4 -0.95 -5.097672768092682 11.704578991964201 8.180576300900766 1 0 0 +957 1 3 -1.05 -3.7514781712957763 9.711761903734494 5.950884468909207 1 0 0 +958 1 3 -1.05 -2.331628855394513 11.887467661981258 5.949131825798181 1 0 0 +960 1 5 0.425 -4.589147136714461 12.583646517910942 8.04170102397289 1 0 0 +2 1 2 2.1 3.3899422731256763 8.864597132006875 2.7573215253057963 0 0 0 +161 1 1 1.575 2.6566341651466274 10.455150667308867 0.00048679133445439504 0 0 0 +163 1 2 2.1 3.469725611191784 11.819703973175962 2.7652842672918947 0 0 0 +164 1 3 -1.05 3.248581755192575 8.935464749145556 1.139046873812159 0 0 0 +165 1 3 -1.05 3.603958382521821 11.726895331931285 1.1403873843045993 0 0 0 +166 1 4 -0.95 0.9615570622632923 10.528143207441804 1.009293869122109 0 0 0 +167 1 3 -1.05 -0.38466753745755256 12.520956284187964 3.23898646303282 0 0 0 +168 1 3 -1.05 3.3555302887687066 10.345324712237673 3.240736497944564 0 0 0 +169 1 3 -1.05 2.2048898258862977 12.672804867739217 3.0491771504206753 0 0 0 +170 1 5 0.425 0.4530661645712417 9.649085132213575 1.1481844303253137 0 0 0 +171 1 1 1.575 5.26481011625984 14.93803969584685 0.00048748398004327953 0 0 0 +172 1 2 2.1 0.8381050018951335 13.34746956355098 2.7573056112375216 0 0 0 +173 1 2 2.1 0.917912298353329 16.302592791352506 2.7652928068938305 0 0 0 +174 1 3 -1.05 0.6967492081717079 13.418384331397867 1.139034719167146 0 0 0 +175 1 3 -1.05 1.0521268357949296 16.209803156886135 1.140403453608636 0 0 0 +176 1 4 -0.95 3.5696981167895174 15.011008782764922 1.0092654803188346 0 0 0 +177 1 3 -1.05 2.2234949075541905 17.003853295689932 3.238994790958916 0 0 0 +178 1 3 -1.05 0.8036927614304492 14.828302477588569 3.24074919764605 0 0 0 +179 1 3 -1.05 -0.346945556400458 17.155719849990422 3.049175316321957 0 0 0 +180 1 5 0.425 3.0612098947390933 14.131917293649405 1.1478771488772814 0 0 0 +181 1 1 1.575 2.672352132552927 16.44233222954824 -0.00048893065522293 0 0 0 +191 1 1 1.575 0.06419990661849084 11.959390323353912 -0.00048797842278069936 0 0 0 +201 1 1 1.575 7.816641875063663 10.455148129073955 0.00048818953626117434 0 0 0 +204 1 3 -1.05 8.408577227951085 8.935464962316985 1.1390566838791205 0 0 0 +206 1 4 -0.95 6.121550861150677 10.528133978087624 1.0092915549395602 0 0 0 +207 1 3 -1.05 4.775356948747724 12.520969520585183 3.2389861172974115 0 0 0 +209 1 3 -1.05 7.364900489893909 12.672798839171666 3.0491755919080212 0 0 0 +210 1 5 0.425 5.61305603153591 9.649082778114593 1.1481433153431642 0 0 0 +212 1 2 2.1 5.998088138826827 13.347473093028178 2.757308731405036 0 0 0 +213 1 2 2.1 6.077891819286037 16.30260193787044 2.765289869913314 0 0 0 +214 1 3 -1.05 5.856751818752759 13.418382710971027 1.1390384696391855 0 0 0 +215 1 3 -1.05 6.212125957670043 16.209803423133504 1.1404002385609324 0 0 0 +217 1 3 -1.05 7.383520043027556 17.003868000384625 3.2389922772992907 0 0 0 +218 1 3 -1.05 5.963690997777473 14.828279007765143 3.2407484945718856 0 0 0 +219 1 3 -1.05 4.81306751453938 17.15571169886196 3.049176642461564 0 0 0 +221 1 1 1.575 7.832359959333601 16.442328863944606 -0.00048820826442330656 0 0 0 +231 1 1 1.575 5.224192755472295 11.9593926983067 -0.0004895452984730042 0 0 0 +285 1 3 -1.05 -1.5560434921058235 11.7268960105203 1.1403925019894405 1 0 0 +291 1 1 1.575 0.10481734176789637 14.938036477244676 0.0004878663631728841 1 0 0 +296 1 4 -0.95 -1.5903037992881508 15.011010762815896 1.0092720957491625 1 0 0 +300 1 5 0.425 -2.098773053271172 14.131915286048635 1.1479073358599035 1 0 0 +823 1 2 2.1 0.16231342663308013 14.895938457827786 6.424582090151786 0 0 0 +825 1 3 -1.05 0.028099381747342278 14.988753389323616 8.049475282091281 0 0 0 +826 1 4 -0.95 2.6705230911081035 16.187530512616757 8.180596938009801 0 0 0 +827 1 3 -1.05 4.01669641754593 14.194683450538559 5.950878753854479 0 0 0 +828 1 3 -1.05 0.27654220878196156 16.37029094078053 5.94912022193599 0 0 0 +829 1 3 -1.05 1.4271792302815047 14.04282401595217 6.140702235142053 0 0 0 +830 1 5 0.425 3.1790125843134582 17.06663589040934 8.04194593753139 0 0 0 +832 1 2 2.1 2.79396271801315 13.368107134362024 6.432543570568198 0 0 0 +833 1 2 2.1 2.714155029782006 10.41298252966632 6.424585299216295 0 0 0 +834 1 3 -1.05 2.9353214295031798 13.297270348704007 8.050805811867614 0 0 0 +835 1 3 -1.05 2.5799322816150294 10.50584016157514 8.049490292842309 0 0 0 +836 1 4 -0.95 0.06232056055442037 11.704568865571552 8.180573482122442 0 0 0 +837 1 3 -1.05 1.4085436097406188 9.711773557309051 5.950884536837345 0 0 0 +838 1 3 -1.05 2.8283748897425482 11.88747503498794 5.949130056988739 0 0 0 +839 1 3 -1.05 3.9789992841497615 9.559918964820955 6.140704411314749 0 0 0 +840 1 5 0.425 0.570841912948957 12.58364508171616 8.04165770071049 0 0 0 +863 1 2 2.1 5.322334696359004 14.895930247507533 6.424585536161565 0 0 0 +865 1 3 -1.05 5.18810009801547 14.988752969144539 8.049478754913183 0 0 0 +868 1 3 -1.05 5.4365436852018405 16.370313581914186 5.949121173646114 0 0 0 +869 1 3 -1.05 6.587166884334117 14.04283169075152 6.140700804578795 0 0 0 +870 1 5 0.425 -12.300970922825949 17.06663390893986 8.041972903281911 1 0 0 +876 1 4 -0.95 5.222326990852581 11.704578679828789 8.180576328926842 0 0 0 +877 1 3 -1.05 6.5685219413996165 9.71176198584331 5.950884504654278 0 0 0 +880 1 5 0.425 5.7308529675779845 12.58364688731844 8.041700994215164 0 0 0 +947 1 3 -1.05 -1.14327593789511 14.194699627136107 5.950876109055651 1 0 0 +959 1 3 -1.05 -1.1809884299906663 9.55991198753285 6.140702871950106 1 0 0 Velocities -1 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -2 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -3 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -4 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -5 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -6 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -7 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -8 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -9 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -10 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -11 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -12 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -13 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -14 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -15 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -16 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -17 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -18 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -19 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -20 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -21 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -22 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -23 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -24 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -25 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -26 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -27 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -28 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -29 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -30 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -31 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -32 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -33 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -34 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -35 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -36 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -37 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -38 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -39 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -40 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -41 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -42 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -43 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -44 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -45 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -46 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -47 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -48 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -49 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -50 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -51 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -52 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -53 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -54 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -55 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -56 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -57 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -58 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -59 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -60 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -61 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -62 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -63 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -64 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -65 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -66 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -67 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -68 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -69 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -70 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -71 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -72 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -73 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -74 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -75 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -76 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -77 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -78 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -79 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -80 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -81 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -82 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -83 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -84 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -85 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -86 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -87 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -88 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -89 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -90 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -91 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -92 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -93 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -94 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -95 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -96 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -97 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -98 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -99 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -100 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -101 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -102 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -103 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -104 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -105 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -106 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -107 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -108 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -109 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -110 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -111 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -112 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -113 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -114 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -115 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -116 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -117 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -118 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -119 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -120 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -121 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -122 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -123 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -124 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -125 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -126 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -127 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -128 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -129 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -130 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -131 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -132 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -133 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -134 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -135 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -136 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -137 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -138 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -139 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -140 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -141 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -142 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -143 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -144 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -145 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -146 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -147 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -148 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -149 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -150 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -151 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -152 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -153 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -154 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -155 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -156 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -157 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -158 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -159 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -160 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -161 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -162 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -163 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -164 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -165 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -166 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -167 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -168 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -169 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -170 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -171 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -172 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -173 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -174 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -175 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -176 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -177 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -178 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -179 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -180 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -181 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -182 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -183 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -184 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -185 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -186 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -187 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -188 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -189 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -190 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -191 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -192 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -193 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -194 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -195 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -196 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -197 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -198 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -199 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -200 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -201 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -202 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -203 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -204 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -205 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -206 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -207 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -208 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -209 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -210 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -211 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -212 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -213 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -214 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -215 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -216 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -217 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -218 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -219 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -220 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -221 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -222 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -223 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -224 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -225 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -226 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -227 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -228 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -229 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -230 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -231 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -232 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -233 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -234 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -235 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -236 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -237 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -238 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -239 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -240 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -241 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -242 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -243 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -244 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -245 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -246 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -247 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -248 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -249 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -250 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -251 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -252 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -253 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -254 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -255 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -256 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -257 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -258 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -259 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -260 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -261 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -262 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -263 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -264 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -265 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -266 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -267 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -268 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -269 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -270 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -271 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -272 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -273 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -274 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -275 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -276 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -277 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -278 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -279 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -280 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -281 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -282 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -283 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -284 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -285 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -286 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -287 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -288 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -289 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -290 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -291 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -292 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -293 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -294 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -295 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -296 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -297 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -298 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -299 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -300 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -301 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -302 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -303 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -304 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -305 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -306 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -307 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -308 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -309 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -310 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -311 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -312 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -313 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -314 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -315 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -316 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -317 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -318 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -319 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -320 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -321 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -322 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -323 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -324 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -325 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -326 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -327 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -328 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -329 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -330 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -331 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -332 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -333 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -334 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -335 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -336 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -337 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -338 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -339 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -340 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -341 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -342 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -343 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -344 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -345 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -346 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -347 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -348 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -349 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -350 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -351 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -352 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -353 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -354 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -355 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -356 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -357 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -358 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -359 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -360 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -361 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -362 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -363 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -364 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -365 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -366 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -367 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -368 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -369 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -370 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -371 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -372 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -373 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -374 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -375 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -376 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -377 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -378 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -379 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -380 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -381 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -382 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -383 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -384 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -385 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -386 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -387 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -388 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -389 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -390 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -391 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -392 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -393 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -394 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -395 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -396 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -397 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -398 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -399 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -400 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -401 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -402 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -403 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -404 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -405 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -406 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -407 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -408 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -409 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -410 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -411 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -412 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -413 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -414 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -415 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -416 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -417 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -418 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -419 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -420 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -421 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -422 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -423 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -424 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -425 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -426 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -427 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -428 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -429 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -430 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -431 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -432 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -433 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -434 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -435 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -436 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -437 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -438 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -439 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -440 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -441 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -442 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -443 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -444 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -445 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -446 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -447 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -448 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -449 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -450 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -451 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -452 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -453 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -454 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -455 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -456 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -457 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -458 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -459 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -460 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -461 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -462 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -463 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -464 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -465 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -466 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -467 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -468 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -469 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -470 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -471 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -472 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -473 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -474 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -475 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -476 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -477 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -478 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -479 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -480 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -481 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -482 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -483 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -484 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -485 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -486 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -487 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -488 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -489 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -490 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -491 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -492 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -493 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -494 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -495 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -496 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -497 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -498 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -499 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -500 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -501 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -502 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -503 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -504 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -505 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -506 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -507 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -508 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -509 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -510 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -511 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -512 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -513 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -514 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -515 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -516 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -517 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -518 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -519 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -520 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -521 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -522 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -523 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -524 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -525 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -526 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -527 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -528 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -529 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -530 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -531 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -532 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -533 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -534 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -535 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -536 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -537 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -538 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -539 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -540 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -541 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -542 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -543 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -544 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -545 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -546 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -547 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -548 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -549 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -550 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -551 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -552 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -553 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -554 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -555 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -556 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -557 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -558 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -559 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -560 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -561 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -562 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -563 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -564 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -565 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -566 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -567 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -568 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -569 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -570 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -571 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -572 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -573 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -574 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -575 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -576 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -577 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -578 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -579 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -580 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -581 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -582 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -583 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -584 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -585 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -586 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -587 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -588 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -589 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -590 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -591 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -592 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -593 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -594 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -595 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -596 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -597 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -598 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -599 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -600 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -601 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -602 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -603 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -604 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -605 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -606 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -607 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -608 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -609 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -610 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -611 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -612 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -613 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -614 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -615 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -616 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -617 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -618 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -619 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -620 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -621 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -622 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -623 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -624 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -625 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -626 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -627 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -628 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -629 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -630 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -631 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -632 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -633 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -634 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -635 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -636 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -637 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -638 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -639 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -640 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -641 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -642 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -643 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -644 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -645 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -646 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -647 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -648 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -649 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -650 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -651 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -652 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -653 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -654 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -655 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -656 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -657 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -658 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -659 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -660 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -661 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -662 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -663 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -664 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -665 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -666 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -667 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -668 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -669 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -670 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -671 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -672 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -673 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -674 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -675 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -676 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -677 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -678 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -679 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -680 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -681 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -682 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -683 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -684 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -685 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -686 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -687 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -688 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -689 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -690 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -691 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -692 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -693 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -694 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -695 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -696 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -697 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -698 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -699 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -700 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -701 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -702 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -703 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -704 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -705 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -706 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -707 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -708 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -709 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -710 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -711 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -712 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -713 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -714 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -715 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -716 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -717 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -718 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -719 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -720 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -721 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -722 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -723 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -724 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -725 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -726 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -727 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -728 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -729 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -730 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -731 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -732 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -733 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -734 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -735 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -736 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -737 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -738 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -739 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -740 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -741 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -742 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -743 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -744 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -745 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -746 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -747 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -748 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -749 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -750 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -751 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -752 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -753 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -754 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -755 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -756 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -757 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -758 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -759 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -760 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -761 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -762 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -763 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -764 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -765 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -766 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -767 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -768 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -769 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -770 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -771 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -772 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -773 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -774 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -775 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -776 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -777 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -778 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -779 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -780 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -781 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -782 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -783 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -784 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -785 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -786 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -787 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -788 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -789 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -790 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -791 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -792 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -793 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -794 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -795 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -796 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -797 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -798 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -799 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -800 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -801 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -802 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -803 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -804 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -805 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -806 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -807 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -808 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -809 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -810 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -811 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -812 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -813 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -814 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -815 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -816 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -817 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -818 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -819 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -820 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -821 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -822 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -823 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -824 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -825 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -826 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -827 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -828 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -829 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -830 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -831 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -832 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -833 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -834 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -835 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -836 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -837 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -838 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -839 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -840 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -841 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -842 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -843 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -844 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -845 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -846 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -847 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -848 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -849 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -850 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -851 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -852 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -853 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -854 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -855 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -856 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -857 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -858 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -859 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -860 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -861 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -862 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -863 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -864 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -865 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -866 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -867 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -868 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -869 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -870 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -871 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -872 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -873 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -874 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -875 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -876 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -877 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -878 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -879 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -880 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -881 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -882 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -883 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -884 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -885 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -886 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -887 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -888 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -889 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -890 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -891 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -892 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -893 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -894 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -895 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -896 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -897 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -898 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -899 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -900 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -901 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -902 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -903 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -904 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -905 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -906 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -907 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -908 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -909 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -910 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -911 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -912 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -913 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -914 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -915 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -916 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -917 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -918 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -919 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -920 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -921 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -922 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -923 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -924 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -925 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -926 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -927 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -928 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -929 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -930 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -931 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -932 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -933 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -934 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -935 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -936 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -937 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -938 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -939 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -940 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -941 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -942 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -943 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -944 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -945 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -946 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -947 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -948 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -949 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -950 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -951 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -952 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -953 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -954 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -955 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -956 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -957 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -958 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -959 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -960 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -961 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -962 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -963 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -964 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -965 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -966 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -967 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -968 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -969 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -970 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -971 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -972 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -973 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -974 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -975 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -976 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -977 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -978 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -979 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -980 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -981 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -982 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -983 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -984 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -985 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -986 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -987 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -988 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -989 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -990 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -991 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -992 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -993 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -994 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -995 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -996 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -997 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -998 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -999 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1000 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1001 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1002 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1003 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1004 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1005 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1006 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1007 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1008 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1009 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1010 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1011 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1012 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1013 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1014 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1015 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1016 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1017 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1018 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1019 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1020 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1021 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1022 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1023 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1024 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1025 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1026 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1027 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1028 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1029 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1030 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1031 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1032 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1033 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1034 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1035 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1036 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1037 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1038 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1039 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1040 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1041 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1042 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1043 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1044 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1045 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1046 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1047 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1048 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1049 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1050 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1051 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1052 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1053 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1054 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1055 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1056 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1057 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1058 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1059 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1060 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1061 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1062 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1063 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1064 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1065 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1066 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1067 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1068 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1069 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1070 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1071 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1072 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1073 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1074 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1075 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1076 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1077 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1078 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1079 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1080 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1081 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1082 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1083 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1084 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1085 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1086 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1087 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1088 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1089 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1090 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1091 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1092 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1093 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1094 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1095 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1096 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1097 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1098 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1099 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1100 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1101 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1102 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1103 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1104 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1105 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1106 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1107 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1108 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1109 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1110 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1111 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1112 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1113 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1114 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1115 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1116 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1117 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1118 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1119 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1120 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1121 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1122 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1123 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1124 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1125 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1126 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1127 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1128 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1129 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1130 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1131 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1132 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1133 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1134 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1135 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1136 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1137 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1138 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1139 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1140 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1141 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1142 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1143 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1144 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1145 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1146 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1147 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1148 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1149 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1150 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1151 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1152 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1153 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1154 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1155 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1156 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1157 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1158 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1159 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1160 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1161 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1162 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1163 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1164 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1165 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1166 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1167 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1168 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1169 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1170 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1171 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1172 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1173 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1174 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1175 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1176 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1177 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1178 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1179 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1180 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1181 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1182 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1183 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1184 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1185 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1186 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1187 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1188 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1189 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1190 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1191 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1192 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1193 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1194 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1195 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1196 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1197 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1198 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1199 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1200 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1201 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1202 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1203 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1204 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1205 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1206 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1207 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1208 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1209 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1210 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1211 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1212 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1213 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1214 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1215 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1216 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1217 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1218 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1219 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1220 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1221 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1222 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1223 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1224 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1225 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1226 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1227 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1228 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1229 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1230 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1231 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1232 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1233 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1234 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1235 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1236 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1237 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1238 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1239 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1240 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1241 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1242 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1243 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1244 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1245 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1246 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1247 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1248 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1249 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1250 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1251 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1252 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1253 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1254 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1255 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1256 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1257 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1258 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1259 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1260 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1261 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1262 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1263 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1264 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1265 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1266 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1267 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1268 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1269 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1270 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1271 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1272 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1273 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1274 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1275 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1276 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1277 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1278 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1279 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -1280 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 +264 0 0 0 +304 0 0 0 +386 0 0 0 +387 0 0 0 +392 0 0 0 +393 0 0 0 +394 0 0 0 +395 0 0 0 +398 0 0 0 +399 0 0 0 +422 0 0 0 +423 0 0 0 +425 0 0 0 +426 0 0 0 +427 0 0 0 +428 0 0 0 +429 0 0 0 +430 0 0 0 +432 0 0 0 +433 0 0 0 +434 0 0 0 +435 0 0 0 +436 0 0 0 +437 0 0 0 +438 0 0 0 +439 0 0 0 +440 0 0 0 +462 0 0 0 +463 0 0 0 +465 0 0 0 +468 0 0 0 +469 0 0 0 +470 0 0 0 +476 0 0 0 +477 0 0 0 +480 0 0 0 +842 0 0 0 +882 0 0 0 +1003 0 0 0 +1005 0 0 0 +1008 0 0 0 +1011 0 0 0 +1016 0 0 0 +1020 0 0 0 +1041 0 0 0 +1043 0 0 0 +1044 0 0 0 +1045 0 0 0 +1046 0 0 0 +1047 0 0 0 +1048 0 0 0 +1049 0 0 0 +1050 0 0 0 +1051 0 0 0 +1052 0 0 0 +1053 0 0 0 +1054 0 0 0 +1055 0 0 0 +1056 0 0 0 +1057 0 0 0 +1058 0 0 0 +1059 0 0 0 +1060 0 0 0 +1061 0 0 0 +1071 0 0 0 +1086 0 0 0 +1087 0 0 0 +1092 0 0 0 +1093 0 0 0 +1094 0 0 0 +1095 0 0 0 +1098 0 0 0 +1099 0 0 0 +1111 0 0 0 +184 0 0 0 +224 0 0 0 +342 0 0 0 +343 0 0 0 +345 0 0 0 +346 0 0 0 +347 0 0 0 +348 0 0 0 +349 0 0 0 +350 0 0 0 +352 0 0 0 +353 0 0 0 +354 0 0 0 +355 0 0 0 +356 0 0 0 +357 0 0 0 +358 0 0 0 +359 0 0 0 +360 0 0 0 +382 0 0 0 +383 0 0 0 +385 0 0 0 +388 0 0 0 +389 0 0 0 +390 0 0 0 +396 0 0 0 +397 0 0 0 +400 0 0 0 +466 0 0 0 +467 0 0 0 +472 0 0 0 +473 0 0 0 +474 0 0 0 +475 0 0 0 +478 0 0 0 +479 0 0 0 +802 0 0 0 +922 0 0 0 +961 0 0 0 +963 0 0 0 +964 0 0 0 +965 0 0 0 +966 0 0 0 +967 0 0 0 +968 0 0 0 +969 0 0 0 +970 0 0 0 +971 0 0 0 +972 0 0 0 +973 0 0 0 +974 0 0 0 +975 0 0 0 +976 0 0 0 +977 0 0 0 +978 0 0 0 +979 0 0 0 +980 0 0 0 +981 0 0 0 +991 0 0 0 +1001 0 0 0 +1004 0 0 0 +1006 0 0 0 +1007 0 0 0 +1009 0 0 0 +1010 0 0 0 +1012 0 0 0 +1013 0 0 0 +1014 0 0 0 +1015 0 0 0 +1017 0 0 0 +1018 0 0 0 +1019 0 0 0 +1021 0 0 0 +1031 0 0 0 +1081 0 0 0 +1083 0 0 0 +1084 0 0 0 +1085 0 0 0 +1088 0 0 0 +1089 0 0 0 +1090 0 0 0 +1091 0 0 0 +1096 0 0 0 +1097 0 0 0 +1100 0 0 0 +1101 0 0 0 +424 0 0 0 +464 0 0 0 +546 0 0 0 +547 0 0 0 +552 0 0 0 +553 0 0 0 +554 0 0 0 +555 0 0 0 +558 0 0 0 +559 0 0 0 +582 0 0 0 +583 0 0 0 +585 0 0 0 +586 0 0 0 +587 0 0 0 +588 0 0 0 +589 0 0 0 +590 0 0 0 +592 0 0 0 +593 0 0 0 +594 0 0 0 +595 0 0 0 +596 0 0 0 +597 0 0 0 +598 0 0 0 +599 0 0 0 +600 0 0 0 +622 0 0 0 +623 0 0 0 +625 0 0 0 +628 0 0 0 +629 0 0 0 +630 0 0 0 +636 0 0 0 +637 0 0 0 +640 0 0 0 +1002 0 0 0 +1042 0 0 0 +1163 0 0 0 +1165 0 0 0 +1168 0 0 0 +1171 0 0 0 +1176 0 0 0 +1180 0 0 0 +1201 0 0 0 +1203 0 0 0 +1204 0 0 0 +1205 0 0 0 +1206 0 0 0 +1207 0 0 0 +1208 0 0 0 +1209 0 0 0 +1210 0 0 0 +1211 0 0 0 +1212 0 0 0 +1213 0 0 0 +1214 0 0 0 +1215 0 0 0 +1216 0 0 0 +1217 0 0 0 +1218 0 0 0 +1219 0 0 0 +1220 0 0 0 +1221 0 0 0 +1231 0 0 0 +1246 0 0 0 +1247 0 0 0 +1252 0 0 0 +1253 0 0 0 +1254 0 0 0 +1255 0 0 0 +1258 0 0 0 +1259 0 0 0 +1271 0 0 0 +344 0 0 0 +384 0 0 0 +502 0 0 0 +503 0 0 0 +505 0 0 0 +506 0 0 0 +507 0 0 0 +508 0 0 0 +509 0 0 0 +510 0 0 0 +512 0 0 0 +513 0 0 0 +514 0 0 0 +515 0 0 0 +516 0 0 0 +517 0 0 0 +518 0 0 0 +519 0 0 0 +520 0 0 0 +542 0 0 0 +543 0 0 0 +545 0 0 0 +548 0 0 0 +549 0 0 0 +550 0 0 0 +556 0 0 0 +557 0 0 0 +560 0 0 0 +626 0 0 0 +627 0 0 0 +632 0 0 0 +633 0 0 0 +634 0 0 0 +635 0 0 0 +638 0 0 0 +639 0 0 0 +962 0 0 0 +1082 0 0 0 +1121 0 0 0 +1123 0 0 0 +1124 0 0 0 +1125 0 0 0 +1126 0 0 0 +1127 0 0 0 +1128 0 0 0 +1129 0 0 0 +1130 0 0 0 +1131 0 0 0 +1132 0 0 0 +1133 0 0 0 +1134 0 0 0 +1135 0 0 0 +1136 0 0 0 +1137 0 0 0 +1138 0 0 0 +1139 0 0 0 +1140 0 0 0 +1141 0 0 0 +1151 0 0 0 +1161 0 0 0 +1164 0 0 0 +1166 0 0 0 +1167 0 0 0 +1169 0 0 0 +1170 0 0 0 +1172 0 0 0 +1173 0 0 0 +1174 0 0 0 +1175 0 0 0 +1177 0 0 0 +1178 0 0 0 +1179 0 0 0 +1181 0 0 0 +1191 0 0 0 +1241 0 0 0 +1243 0 0 0 +1244 0 0 0 +1245 0 0 0 +1248 0 0 0 +1249 0 0 0 +1250 0 0 0 +1251 0 0 0 +1256 0 0 0 +1257 0 0 0 +1260 0 0 0 +1261 0 0 0 +66 0 0 0 +67 0 0 0 +72 0 0 0 +73 0 0 0 +74 0 0 0 +75 0 0 0 +78 0 0 0 +79 0 0 0 +102 0 0 0 +103 0 0 0 +105 0 0 0 +106 0 0 0 +107 0 0 0 +108 0 0 0 +109 0 0 0 +110 0 0 0 +112 0 0 0 +113 0 0 0 +114 0 0 0 +115 0 0 0 +116 0 0 0 +117 0 0 0 +118 0 0 0 +119 0 0 0 +120 0 0 0 +142 0 0 0 +143 0 0 0 +145 0 0 0 +148 0 0 0 +149 0 0 0 +150 0 0 0 +156 0 0 0 +157 0 0 0 +160 0 0 0 +584 0 0 0 +624 0 0 0 +683 0 0 0 +685 0 0 0 +688 0 0 0 +691 0 0 0 +696 0 0 0 +700 0 0 0 +721 0 0 0 +723 0 0 0 +724 0 0 0 +725 0 0 0 +726 0 0 0 +727 0 0 0 +728 0 0 0 +729 0 0 0 +730 0 0 0 +731 0 0 0 +732 0 0 0 +733 0 0 0 +734 0 0 0 +735 0 0 0 +736 0 0 0 +737 0 0 0 +738 0 0 0 +739 0 0 0 +740 0 0 0 +741 0 0 0 +751 0 0 0 +766 0 0 0 +767 0 0 0 +772 0 0 0 +773 0 0 0 +774 0 0 0 +775 0 0 0 +778 0 0 0 +779 0 0 0 +791 0 0 0 +1162 0 0 0 +1202 0 0 0 +22 0 0 0 +23 0 0 0 +25 0 0 0 +26 0 0 0 +27 0 0 0 +28 0 0 0 +29 0 0 0 +30 0 0 0 +32 0 0 0 +33 0 0 0 +34 0 0 0 +35 0 0 0 +36 0 0 0 +37 0 0 0 +38 0 0 0 +39 0 0 0 +40 0 0 0 +62 0 0 0 +63 0 0 0 +65 0 0 0 +68 0 0 0 +69 0 0 0 +70 0 0 0 +76 0 0 0 +77 0 0 0 +80 0 0 0 +146 0 0 0 +147 0 0 0 +152 0 0 0 +153 0 0 0 +154 0 0 0 +155 0 0 0 +158 0 0 0 +159 0 0 0 +504 0 0 0 +544 0 0 0 +641 0 0 0 +643 0 0 0 +644 0 0 0 +645 0 0 0 +646 0 0 0 +647 0 0 0 +648 0 0 0 +649 0 0 0 +650 0 0 0 +651 0 0 0 +652 0 0 0 +653 0 0 0 +654 0 0 0 +655 0 0 0 +656 0 0 0 +657 0 0 0 +658 0 0 0 +659 0 0 0 +660 0 0 0 +661 0 0 0 +671 0 0 0 +681 0 0 0 +684 0 0 0 +686 0 0 0 +687 0 0 0 +689 0 0 0 +690 0 0 0 +692 0 0 0 +693 0 0 0 +694 0 0 0 +695 0 0 0 +697 0 0 0 +698 0 0 0 +699 0 0 0 +701 0 0 0 +711 0 0 0 +761 0 0 0 +763 0 0 0 +764 0 0 0 +765 0 0 0 +768 0 0 0 +769 0 0 0 +770 0 0 0 +771 0 0 0 +776 0 0 0 +777 0 0 0 +780 0 0 0 +781 0 0 0 +1122 0 0 0 +1242 0 0 0 +104 0 0 0 +144 0 0 0 +226 0 0 0 +227 0 0 0 +232 0 0 0 +233 0 0 0 +234 0 0 0 +235 0 0 0 +238 0 0 0 +239 0 0 0 +262 0 0 0 +263 0 0 0 +265 0 0 0 +266 0 0 0 +267 0 0 0 +268 0 0 0 +269 0 0 0 +270 0 0 0 +272 0 0 0 +273 0 0 0 +274 0 0 0 +275 0 0 0 +276 0 0 0 +277 0 0 0 +278 0 0 0 +279 0 0 0 +280 0 0 0 +302 0 0 0 +303 0 0 0 +305 0 0 0 +308 0 0 0 +309 0 0 0 +310 0 0 0 +316 0 0 0 +317 0 0 0 +320 0 0 0 +682 0 0 0 +722 0 0 0 +843 0 0 0 +845 0 0 0 +848 0 0 0 +851 0 0 0 +856 0 0 0 +860 0 0 0 +881 0 0 0 +883 0 0 0 +884 0 0 0 +885 0 0 0 +886 0 0 0 +887 0 0 0 +888 0 0 0 +889 0 0 0 +890 0 0 0 +891 0 0 0 +892 0 0 0 +893 0 0 0 +894 0 0 0 +895 0 0 0 +896 0 0 0 +897 0 0 0 +898 0 0 0 +899 0 0 0 +900 0 0 0 +901 0 0 0 +911 0 0 0 +926 0 0 0 +927 0 0 0 +932 0 0 0 +933 0 0 0 +934 0 0 0 +935 0 0 0 +938 0 0 0 +939 0 0 0 +951 0 0 0 +24 0 0 0 +64 0 0 0 +182 0 0 0 +183 0 0 0 +185 0 0 0 +186 0 0 0 +187 0 0 0 +188 0 0 0 +189 0 0 0 +190 0 0 0 +192 0 0 0 +193 0 0 0 +194 0 0 0 +195 0 0 0 +196 0 0 0 +197 0 0 0 +198 0 0 0 +199 0 0 0 +200 0 0 0 +222 0 0 0 +223 0 0 0 +225 0 0 0 +228 0 0 0 +229 0 0 0 +230 0 0 0 +236 0 0 0 +237 0 0 0 +240 0 0 0 +306 0 0 0 +307 0 0 0 +312 0 0 0 +313 0 0 0 +314 0 0 0 +315 0 0 0 +318 0 0 0 +319 0 0 0 +642 0 0 0 +762 0 0 0 +801 0 0 0 +803 0 0 0 +804 0 0 0 +805 0 0 0 +806 0 0 0 +807 0 0 0 +808 0 0 0 +809 0 0 0 +810 0 0 0 +811 0 0 0 +812 0 0 0 +813 0 0 0 +814 0 0 0 +815 0 0 0 +816 0 0 0 +817 0 0 0 +818 0 0 0 +819 0 0 0 +820 0 0 0 +821 0 0 0 +831 0 0 0 +841 0 0 0 +844 0 0 0 +846 0 0 0 +847 0 0 0 +849 0 0 0 +850 0 0 0 +852 0 0 0 +853 0 0 0 +854 0 0 0 +855 0 0 0 +857 0 0 0 +858 0 0 0 +859 0 0 0 +861 0 0 0 +871 0 0 0 +921 0 0 0 +923 0 0 0 +924 0 0 0 +925 0 0 0 +928 0 0 0 +929 0 0 0 +930 0 0 0 +931 0 0 0 +936 0 0 0 +937 0 0 0 +940 0 0 0 +941 0 0 0 +202 0 0 0 +242 0 0 0 +282 0 0 0 +363 0 0 0 +365 0 0 0 +368 0 0 0 +371 0 0 0 +376 0 0 0 +380 0 0 0 +401 0 0 0 +403 0 0 0 +404 0 0 0 +405 0 0 0 +406 0 0 0 +407 0 0 0 +408 0 0 0 +409 0 0 0 +410 0 0 0 +411 0 0 0 +412 0 0 0 +413 0 0 0 +414 0 0 0 +415 0 0 0 +416 0 0 0 +417 0 0 0 +418 0 0 0 +419 0 0 0 +420 0 0 0 +421 0 0 0 +431 0 0 0 +441 0 0 0 +443 0 0 0 +444 0 0 0 +445 0 0 0 +446 0 0 0 +447 0 0 0 +448 0 0 0 +449 0 0 0 +450 0 0 0 +452 0 0 0 +453 0 0 0 +454 0 0 0 +455 0 0 0 +456 0 0 0 +457 0 0 0 +458 0 0 0 +459 0 0 0 +461 0 0 0 +471 0 0 0 +904 0 0 0 +944 0 0 0 +1026 0 0 0 +1027 0 0 0 +1032 0 0 0 +1033 0 0 0 +1034 0 0 0 +1035 0 0 0 +1038 0 0 0 +1039 0 0 0 +1062 0 0 0 +1063 0 0 0 +1065 0 0 0 +1066 0 0 0 +1067 0 0 0 +1068 0 0 0 +1069 0 0 0 +1070 0 0 0 +1072 0 0 0 +1073 0 0 0 +1074 0 0 0 +1075 0 0 0 +1076 0 0 0 +1077 0 0 0 +1078 0 0 0 +1079 0 0 0 +1080 0 0 0 +1102 0 0 0 +1103 0 0 0 +1105 0 0 0 +1106 0 0 0 +1108 0 0 0 +1109 0 0 0 +1110 0 0 0 +1112 0 0 0 +1113 0 0 0 +1114 0 0 0 +1115 0 0 0 +1116 0 0 0 +1117 0 0 0 +1118 0 0 0 +1120 0 0 0 +162 0 0 0 +321 0 0 0 +323 0 0 0 +324 0 0 0 +325 0 0 0 +326 0 0 0 +327 0 0 0 +328 0 0 0 +329 0 0 0 +330 0 0 0 +331 0 0 0 +332 0 0 0 +333 0 0 0 +334 0 0 0 +335 0 0 0 +336 0 0 0 +337 0 0 0 +338 0 0 0 +339 0 0 0 +340 0 0 0 +341 0 0 0 +351 0 0 0 +361 0 0 0 +364 0 0 0 +366 0 0 0 +367 0 0 0 +369 0 0 0 +370 0 0 0 +372 0 0 0 +373 0 0 0 +374 0 0 0 +375 0 0 0 +377 0 0 0 +378 0 0 0 +379 0 0 0 +381 0 0 0 +391 0 0 0 +451 0 0 0 +460 0 0 0 +824 0 0 0 +864 0 0 0 +982 0 0 0 +983 0 0 0 +985 0 0 0 +986 0 0 0 +987 0 0 0 +988 0 0 0 +989 0 0 0 +990 0 0 0 +992 0 0 0 +993 0 0 0 +994 0 0 0 +995 0 0 0 +996 0 0 0 +997 0 0 0 +998 0 0 0 +999 0 0 0 +1000 0 0 0 +1022 0 0 0 +1023 0 0 0 +1025 0 0 0 +1028 0 0 0 +1029 0 0 0 +1030 0 0 0 +1036 0 0 0 +1037 0 0 0 +1040 0 0 0 +1107 0 0 0 +1119 0 0 0 +362 0 0 0 +402 0 0 0 +442 0 0 0 +523 0 0 0 +525 0 0 0 +528 0 0 0 +531 0 0 0 +536 0 0 0 +540 0 0 0 +561 0 0 0 +563 0 0 0 +564 0 0 0 +565 0 0 0 +566 0 0 0 +567 0 0 0 +568 0 0 0 +569 0 0 0 +570 0 0 0 +571 0 0 0 +572 0 0 0 +573 0 0 0 +574 0 0 0 +575 0 0 0 +576 0 0 0 +577 0 0 0 +578 0 0 0 +579 0 0 0 +580 0 0 0 +581 0 0 0 +591 0 0 0 +601 0 0 0 +603 0 0 0 +604 0 0 0 +605 0 0 0 +606 0 0 0 +607 0 0 0 +608 0 0 0 +609 0 0 0 +610 0 0 0 +612 0 0 0 +613 0 0 0 +614 0 0 0 +615 0 0 0 +616 0 0 0 +617 0 0 0 +618 0 0 0 +619 0 0 0 +621 0 0 0 +631 0 0 0 +1064 0 0 0 +1104 0 0 0 +1186 0 0 0 +1187 0 0 0 +1192 0 0 0 +1193 0 0 0 +1194 0 0 0 +1195 0 0 0 +1198 0 0 0 +1199 0 0 0 +1222 0 0 0 +1223 0 0 0 +1225 0 0 0 +1226 0 0 0 +1227 0 0 0 +1228 0 0 0 +1229 0 0 0 +1230 0 0 0 +1232 0 0 0 +1233 0 0 0 +1234 0 0 0 +1235 0 0 0 +1236 0 0 0 +1237 0 0 0 +1238 0 0 0 +1239 0 0 0 +1240 0 0 0 +1262 0 0 0 +1263 0 0 0 +1265 0 0 0 +1266 0 0 0 +1268 0 0 0 +1269 0 0 0 +1270 0 0 0 +1272 0 0 0 +1273 0 0 0 +1274 0 0 0 +1275 0 0 0 +1276 0 0 0 +1277 0 0 0 +1278 0 0 0 +1280 0 0 0 +322 0 0 0 +481 0 0 0 +483 0 0 0 +484 0 0 0 +485 0 0 0 +486 0 0 0 +487 0 0 0 +488 0 0 0 +489 0 0 0 +490 0 0 0 +491 0 0 0 +492 0 0 0 +493 0 0 0 +494 0 0 0 +495 0 0 0 +496 0 0 0 +497 0 0 0 +498 0 0 0 +499 0 0 0 +500 0 0 0 +501 0 0 0 +511 0 0 0 +521 0 0 0 +524 0 0 0 +526 0 0 0 +527 0 0 0 +529 0 0 0 +530 0 0 0 +532 0 0 0 +533 0 0 0 +534 0 0 0 +535 0 0 0 +537 0 0 0 +538 0 0 0 +539 0 0 0 +541 0 0 0 +551 0 0 0 +611 0 0 0 +620 0 0 0 +984 0 0 0 +1024 0 0 0 +1142 0 0 0 +1143 0 0 0 +1145 0 0 0 +1146 0 0 0 +1147 0 0 0 +1148 0 0 0 +1149 0 0 0 +1150 0 0 0 +1152 0 0 0 +1153 0 0 0 +1154 0 0 0 +1155 0 0 0 +1156 0 0 0 +1157 0 0 0 +1158 0 0 0 +1159 0 0 0 +1160 0 0 0 +1182 0 0 0 +1183 0 0 0 +1185 0 0 0 +1188 0 0 0 +1189 0 0 0 +1190 0 0 0 +1196 0 0 0 +1197 0 0 0 +1200 0 0 0 +1267 0 0 0 +1279 0 0 0 +43 0 0 0 +45 0 0 0 +48 0 0 0 +51 0 0 0 +56 0 0 0 +60 0 0 0 +81 0 0 0 +83 0 0 0 +84 0 0 0 +85 0 0 0 +86 0 0 0 +87 0 0 0 +88 0 0 0 +89 0 0 0 +90 0 0 0 +91 0 0 0 +92 0 0 0 +93 0 0 0 +94 0 0 0 +95 0 0 0 +96 0 0 0 +97 0 0 0 +98 0 0 0 +99 0 0 0 +100 0 0 0 +101 0 0 0 +111 0 0 0 +121 0 0 0 +123 0 0 0 +124 0 0 0 +126 0 0 0 +127 0 0 0 +128 0 0 0 +129 0 0 0 +130 0 0 0 +132 0 0 0 +133 0 0 0 +134 0 0 0 +135 0 0 0 +137 0 0 0 +138 0 0 0 +139 0 0 0 +141 0 0 0 +151 0 0 0 +522 0 0 0 +562 0 0 0 +602 0 0 0 +706 0 0 0 +707 0 0 0 +712 0 0 0 +713 0 0 0 +714 0 0 0 +715 0 0 0 +718 0 0 0 +719 0 0 0 +742 0 0 0 +743 0 0 0 +744 0 0 0 +745 0 0 0 +746 0 0 0 +747 0 0 0 +748 0 0 0 +749 0 0 0 +750 0 0 0 +752 0 0 0 +753 0 0 0 +754 0 0 0 +755 0 0 0 +756 0 0 0 +757 0 0 0 +758 0 0 0 +759 0 0 0 +760 0 0 0 +782 0 0 0 +783 0 0 0 +784 0 0 0 +785 0 0 0 +786 0 0 0 +788 0 0 0 +789 0 0 0 +790 0 0 0 +792 0 0 0 +793 0 0 0 +794 0 0 0 +795 0 0 0 +796 0 0 0 +797 0 0 0 +798 0 0 0 +800 0 0 0 +902 0 0 0 +942 0 0 0 +1224 0 0 0 +1264 0 0 0 +1 0 0 0 +3 0 0 0 +4 0 0 0 +5 0 0 0 +6 0 0 0 +7 0 0 0 +8 0 0 0 +9 0 0 0 +10 0 0 0 +11 0 0 0 +12 0 0 0 +13 0 0 0 +14 0 0 0 +15 0 0 0 +16 0 0 0 +17 0 0 0 +18 0 0 0 +19 0 0 0 +20 0 0 0 +21 0 0 0 +31 0 0 0 +41 0 0 0 +44 0 0 0 +46 0 0 0 +47 0 0 0 +49 0 0 0 +50 0 0 0 +52 0 0 0 +53 0 0 0 +54 0 0 0 +55 0 0 0 +57 0 0 0 +58 0 0 0 +59 0 0 0 +61 0 0 0 +71 0 0 0 +125 0 0 0 +131 0 0 0 +136 0 0 0 +140 0 0 0 +482 0 0 0 +662 0 0 0 +663 0 0 0 +664 0 0 0 +665 0 0 0 +666 0 0 0 +667 0 0 0 +668 0 0 0 +669 0 0 0 +670 0 0 0 +672 0 0 0 +673 0 0 0 +674 0 0 0 +675 0 0 0 +676 0 0 0 +677 0 0 0 +678 0 0 0 +679 0 0 0 +680 0 0 0 +702 0 0 0 +703 0 0 0 +704 0 0 0 +705 0 0 0 +708 0 0 0 +709 0 0 0 +710 0 0 0 +716 0 0 0 +717 0 0 0 +720 0 0 0 +787 0 0 0 +799 0 0 0 +822 0 0 0 +862 0 0 0 +1144 0 0 0 +1184 0 0 0 +42 0 0 0 +82 0 0 0 +122 0 0 0 +203 0 0 0 +205 0 0 0 +208 0 0 0 +211 0 0 0 +216 0 0 0 +220 0 0 0 +241 0 0 0 +243 0 0 0 +244 0 0 0 +245 0 0 0 +246 0 0 0 +247 0 0 0 +248 0 0 0 +249 0 0 0 +250 0 0 0 +251 0 0 0 +252 0 0 0 +253 0 0 0 +254 0 0 0 +255 0 0 0 +256 0 0 0 +257 0 0 0 +258 0 0 0 +259 0 0 0 +260 0 0 0 +261 0 0 0 +271 0 0 0 +281 0 0 0 +283 0 0 0 +284 0 0 0 +286 0 0 0 +287 0 0 0 +288 0 0 0 +289 0 0 0 +290 0 0 0 +292 0 0 0 +293 0 0 0 +294 0 0 0 +295 0 0 0 +297 0 0 0 +298 0 0 0 +299 0 0 0 +301 0 0 0 +311 0 0 0 +866 0 0 0 +867 0 0 0 +872 0 0 0 +873 0 0 0 +874 0 0 0 +875 0 0 0 +878 0 0 0 +879 0 0 0 +903 0 0 0 +905 0 0 0 +906 0 0 0 +907 0 0 0 +908 0 0 0 +909 0 0 0 +910 0 0 0 +912 0 0 0 +913 0 0 0 +914 0 0 0 +915 0 0 0 +916 0 0 0 +917 0 0 0 +918 0 0 0 +919 0 0 0 +920 0 0 0 +943 0 0 0 +945 0 0 0 +946 0 0 0 +948 0 0 0 +949 0 0 0 +950 0 0 0 +952 0 0 0 +953 0 0 0 +954 0 0 0 +955 0 0 0 +956 0 0 0 +957 0 0 0 +958 0 0 0 +960 0 0 0 +2 0 0 0 +161 0 0 0 +163 0 0 0 +164 0 0 0 +165 0 0 0 +166 0 0 0 +167 0 0 0 +168 0 0 0 +169 0 0 0 +170 0 0 0 +171 0 0 0 +172 0 0 0 +173 0 0 0 +174 0 0 0 +175 0 0 0 +176 0 0 0 +177 0 0 0 +178 0 0 0 +179 0 0 0 +180 0 0 0 +181 0 0 0 +191 0 0 0 +201 0 0 0 +204 0 0 0 +206 0 0 0 +207 0 0 0 +209 0 0 0 +210 0 0 0 +212 0 0 0 +213 0 0 0 +214 0 0 0 +215 0 0 0 +217 0 0 0 +218 0 0 0 +219 0 0 0 +221 0 0 0 +231 0 0 0 +285 0 0 0 +291 0 0 0 +296 0 0 0 +300 0 0 0 +823 0 0 0 +825 0 0 0 +826 0 0 0 +827 0 0 0 +828 0 0 0 +829 0 0 0 +830 0 0 0 +832 0 0 0 +833 0 0 0 +834 0 0 0 +835 0 0 0 +836 0 0 0 +837 0 0 0 +838 0 0 0 +839 0 0 0 +840 0 0 0 +863 0 0 0 +865 0 0 0 +868 0 0 0 +869 0 0 0 +870 0 0 0 +876 0 0 0 +877 0 0 0 +880 0 0 0 +947 0 0 0 +959 0 0 0 Bonds -1 1 6 10 -2 1 16 20 -3 1 26 30 -4 1 36 40 -5 1 46 50 -6 1 56 60 -7 1 66 70 -8 1 76 80 -9 1 86 90 -10 1 96 100 -11 1 106 110 -12 1 116 120 -13 1 126 130 -14 1 136 140 -15 1 146 150 -16 1 156 160 -17 1 166 170 -18 1 176 180 -19 1 186 190 -20 1 196 200 -21 1 206 210 -22 1 216 220 -23 1 226 230 -24 1 236 240 -25 1 246 250 -26 1 256 260 -27 1 266 270 -28 1 276 280 -29 1 286 290 -30 1 296 300 -31 1 306 310 -32 1 316 320 -33 1 326 330 -34 1 336 340 -35 1 346 350 -36 1 356 360 -37 1 366 370 -38 1 376 380 -39 1 386 390 -40 1 396 400 -41 1 406 410 -42 1 416 420 -43 1 426 430 -44 1 436 440 -45 1 446 450 -46 1 456 460 -47 1 466 470 -48 1 476 480 -49 1 486 490 -50 1 496 500 -51 1 506 510 -52 1 516 520 -53 1 526 530 -54 1 536 540 -55 1 546 550 -56 1 556 560 -57 1 566 570 -58 1 576 580 -59 1 586 590 -60 1 596 600 -61 1 606 610 -62 1 616 620 -63 1 626 630 -64 1 636 640 -65 1 646 650 -66 1 656 660 -67 1 666 670 -68 1 676 680 -69 1 686 690 -70 1 696 700 -71 1 706 710 -72 1 716 720 -73 1 726 730 -74 1 736 740 -75 1 746 750 -76 1 756 760 -77 1 766 770 -78 1 776 780 -79 1 786 790 -80 1 796 800 -81 1 806 810 -82 1 816 820 -83 1 826 830 -84 1 836 840 -85 1 846 850 -86 1 856 860 -87 1 866 870 -88 1 876 880 -89 1 886 890 -90 1 896 900 -91 1 906 910 -92 1 916 920 -93 1 926 930 -94 1 936 940 -95 1 946 950 -96 1 956 960 -97 1 966 970 -98 1 976 980 -99 1 986 990 -100 1 996 1000 -101 1 1006 1010 -102 1 1016 1020 -103 1 1026 1030 -104 1 1036 1040 -105 1 1046 1050 -106 1 1056 1060 -107 1 1066 1070 -108 1 1076 1080 -109 1 1086 1090 -110 1 1096 1100 -111 1 1106 1110 -112 1 1116 1120 -113 1 1126 1130 -114 1 1136 1140 -115 1 1146 1150 -116 1 1156 1160 -117 1 1166 1170 -118 1 1176 1180 -119 1 1186 1190 -120 1 1196 1200 -121 1 1206 1210 -122 1 1216 1220 -123 1 1226 1230 -124 1 1236 1240 -125 1 1246 1250 -126 1 1256 1260 -127 1 1266 1270 -128 1 1276 1280 +1 1 386 390 +2 1 426 430 +3 1 436 440 +4 1 476 480 +5 1 1016 1020 +6 1 1046 1050 +7 1 1056 1060 +8 1 1086 1090 +9 1 346 350 +10 1 356 360 +11 1 396 400 +12 1 466 470 +13 1 966 970 +14 1 976 980 +15 1 1006 1010 +16 1 1096 1100 +17 1 546 550 +18 1 586 590 +19 1 596 600 +20 1 636 640 +21 1 1176 1180 +22 1 1206 1210 +23 1 1216 1220 +24 1 1246 1250 +25 1 506 510 +26 1 516 520 +27 1 556 560 +28 1 626 630 +29 1 1126 1130 +30 1 1136 1140 +31 1 1166 1170 +32 1 1256 1260 +33 1 66 70 +34 1 106 110 +35 1 116 120 +36 1 156 160 +37 1 696 700 +38 1 726 730 +39 1 736 740 +40 1 766 770 +41 1 26 30 +42 1 36 40 +43 1 76 80 +44 1 146 150 +45 1 646 650 +46 1 656 660 +47 1 686 690 +48 1 776 780 +49 1 226 230 +50 1 266 270 +51 1 276 280 +52 1 316 320 +53 1 856 860 +54 1 886 890 +55 1 896 900 +56 1 926 930 +57 1 186 190 +58 1 196 200 +59 1 236 240 +60 1 306 310 +61 1 806 810 +62 1 816 820 +63 1 846 850 +64 1 936 940 +65 1 376 380 +66 1 406 410 +67 1 416 420 +68 1 446 450 +69 1 456 460 +70 1 1026 1030 +71 1 1066 1070 +72 1 1076 1080 +73 1 1106 1110 +74 1 1116 1120 +75 1 326 330 +76 1 336 340 +77 1 366 370 +78 1 986 990 +79 1 996 1000 +80 1 1036 1040 +81 1 536 540 +82 1 566 570 +83 1 576 580 +84 1 606 610 +85 1 616 620 +86 1 1186 1190 +87 1 1226 1230 +88 1 1236 1240 +89 1 1266 1270 +90 1 1276 1280 +91 1 486 490 +92 1 496 500 +93 1 526 530 +94 1 1146 1150 +95 1 1156 1160 +96 1 1196 1200 +97 1 56 60 +98 1 86 90 +99 1 96 100 +100 1 126 130 +101 1 706 710 +102 1 746 750 +103 1 756 760 +104 1 786 790 +105 1 796 800 +106 1 6 10 +107 1 16 20 +108 1 46 50 +109 1 136 140 +110 1 666 670 +111 1 676 680 +112 1 716 720 +113 1 216 220 +114 1 246 250 +115 1 256 260 +116 1 286 290 +117 1 866 870 +118 1 906 910 +119 1 916 920 +120 1 946 950 +121 1 956 960 +122 1 166 170 +123 1 176 180 +124 1 206 210 +125 1 296 300 +126 1 826 830 +127 1 836 840 +128 1 876 880 diff --git a/tools/msi2lmp/test/reference/ethane-oplsaa.data2 b/tools/msi2lmp/test/reference/ethane-oplsaa.data2 index 7932115e6e..cf058a4ca9 100644 --- a/tools/msi2lmp/test/reference/ethane-oplsaa.data2 +++ b/tools/msi2lmp/test/reference/ethane-oplsaa.data2 @@ -1,4 +1,4 @@ -LAMMPS data file via write_data, version 24 Oct 2015-ICMS, timestep = 60 +LAMMPS data file via write_data, version 29 Aug 2024, timestep = 64, units = real 8 atoms 2 atom types @@ -9,13 +9,13 @@ LAMMPS data file via write_data, version 24 Oct 2015-ICMS, timestep = 60 9 dihedrals 1 dihedral types --5.0000000000000000e+00 5.0000000000000000e+00 xlo xhi --5.0000000000000000e+00 5.0000000000000000e+00 ylo yhi --5.0000000000000000e+00 5.0000000000000000e+00 zlo zhi +-5 5 xlo xhi +-5 5 ylo yhi +-5 5 zlo zhi Masses -1 12.0112 +1 12.01115 2 1.00797 Pair Coeffs # lj/cut/coul/cut @@ -39,25 +39,25 @@ Dihedral Coeffs # opls Atoms # full -1 1 1 -1.7999999999999999e-01 4.4520961794662339e+00 -4.8331316055118139e+00 4.9921953697666774e+00 0 1 -1 -2 1 1 -1.7999999999999999e-01 -4.0208267456132498e+00 -4.9408970766661060e+00 -4.9962668603193716e+00 1 1 0 -3 1 2 5.9999999999999998e-02 4.1241637410618237e+00 -3.9187715229527775e+00 4.4953501727331462e+00 0 1 -1 -4 1 2 5.9999999999999998e-02 3.9927847754165149e+00 4.3246628372301563e+00 4.4708516600772406e+00 0 0 -1 -5 1 2 5.9999999999999998e-02 4.0591846610420355e+00 -4.8176278098672096e+00 -3.9904372631272924e+00 0 1 0 -6 1 2 5.9999999999999998e-02 -3.5762405194770461e+00 -4.0716394256255244e+00 -4.5137080084717223e+00 1 1 0 -7 1 2 5.9999999999999998e-02 -3.6936651570105905e+00 4.1740778258698201e+00 -4.4511582014826949e+00 1 0 0 -8 1 2 5.9999999999999998e-02 -3.6211369348857190e+00 4.9967667775234554e+00 3.9920131308240276e+00 1 0 -1 +1 1 1 -0.18 4.450690671089314 -4.83133647736812 4.992428212375868 0 1 -1 +2 1 1 -0.18 -4.022291647324266 -4.942324611094984 -4.995836359730008 1 1 0 +3 1 2 0.06 4.1294147504666165 -3.9160894986014068 4.495489435092607 0 1 -1 +4 1 2 0.06 3.9961668185396193 4.3269540139496785 4.470098784261843 0 0 -1 +5 1 2 0.06 4.059021520856406 -4.816007407764785 -3.990885907539094 0 1 0 +6 1 2 0.06 -3.5752328984263166 -4.071671221696795 -4.5159098704661425 1 1 0 +7 1 2 0.06 -3.6980390300174055 4.172581688097945 -4.448408184535642 1 0 0 +8 1 2 0.06 -3.6233701851839633 4.991333514478468 3.9918638905405723 1 0 -1 Velocities -1 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -2 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -3 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -4 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -5 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -6 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -7 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 -8 0.0000000000000000e+00 0.0000000000000000e+00 0.0000000000000000e+00 +1 0 0 0 +2 0 0 0 +3 0 0 0 +4 0 0 0 +5 0 0 0 +6 0 0 0 +7 0 0 0 +8 0 0 0 Bonds diff --git a/tools/msi2lmp/test/runtests.sh b/tools/msi2lmp/test/runtests.sh index b81c8637cd..2cfbcb9f72 100755 --- a/tools/msi2lmp/test/runtests.sh +++ b/tools/msi2lmp/test/runtests.sh @@ -2,8 +2,8 @@ MSI2LMP_LIBRARY=../frc_files VALGRIND='valgrind -v --track-origins=yes --show-reachable=yes --leak-check=full' -MSI2LMP=../src/msi2lmp.exe -LAMMPS=../../../src/lmp_serial +MSI2LMP=../../../build-test/msi2lmp +LAMMPS=../../../build-test/lmp CHECKDATA=./data-compare.pl if [ ! -x $MSI2LMP ] From bafe7c91fa723665d2a3c0b07d4c32a56b1a8c51 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Fri, 6 Sep 2024 08:29:55 -0500 Subject: [PATCH 226/355] switch to using config.yaml with 4 procs for testing --- .github/workflows/full-regression.yml | 2 +- .github/workflows/quick-regression.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index 106bda9d2e..c565ab8d88 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -77,7 +77,7 @@ jobs: source linuxenv/bin/activate python3 tools/regression-tests/run_tests.py \ --lmp-bin=build/lmp \ - --config-file=tools/regression-tests/config_serial.yaml \ + --config-file=tools/regression-tests/config.yaml \ --examples-top-level=examples --analyze --num-workers=4 python3 tools/regression-tests/run_tests.py \ diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index 0d432044b0..d20bf8b364 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -77,7 +77,7 @@ jobs: source linuxenv/bin/activate python3 tools/regression-tests/run_tests.py \ --lmp-bin=build/lmp \ - --config-file=tools/regression-tests/config_serial.yaml \ + --config-file=tools/regression-tests/config.yaml \ --examples-top-level=examples --quick --quick-branch=origin/develop --quick-max=100 --num-workers=4 if [ -f input-list-${{ matrix.idx }}.txt ] From e1d6bb91a843d5cb1b601ca9e95054853541a0e3 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Fri, 6 Sep 2024 09:06:45 -0500 Subject: [PATCH 227/355] get reference walltime from running bench/in.lj, guess the default config file if not specified from the command line args --- tools/regression-tests/run_tests.py | 77 ++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index eb2cf02816..51a0a60f7c 100644 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -851,6 +851,69 @@ def execute(lmp_binary, config, input_file_name, generate_ref_yaml=False): return cmd_str, "", "", -1 +''' + get the reference walltime by running the lmp_binary with config with an input script in the bench/ folder + in.lj is suitable as it doesn't need any potential file, nor any extra packages +''' +def get_reference_walltime(lmp_binary, config): + cmd_str = "" + # check if mpiexec/mpirun is used + if config['mpiexec']: + cmd_str += config['mpiexec'] + " " + config['mpiexec_numproc_flag'] + " " + config['nprocs'] + " " + + # guess the build folder path + lmp_build_folder = lmp_binary.rsplit('/', 1)[0] + + # guess the bench folder + lmp_bench_folder = lmp_build_folder + "/../bench/" + + # run with replicate for a copple of seconds long run + cmd_str += lmp_binary + " -in " + lmp_bench_folder + "in.lj -v x 2 -v y 2 -v z 1 " + config['args'] + + logger.info(f" Executing for reference walltime: {cmd_str}") + + # walltime = -1 indicates some timeout (issues) + walltime = -1 + + # set a timeout for this reference run + timeout = 60 + output = "" + try: + p = subprocess.run(cmd_str, shell=True, text=True, capture_output=True, timeout=timeout) + output = p.stdout + + except subprocess.TimeoutExpired: + msg = f" Timeout for: {cmd_str} ({timeout}s expired)" + logger.info(msg) + print(msg) + + for line in output.split('\n'): + if "Total wall time" in line: + walltime_str = line.split('time:')[1] + hms = walltime_str.split(':') + hours = float(hms[0]) + minutes = float(hms[1]) + seconds = float(hms[2]) + walltime = hours * 3600.0 + minutes * 60.0 + seconds + + logger.info(f" Reference walltime = {walltime}") + + return walltime + +''' + infer the tools/regression-tests folder from the absolute path to lmp_binary + return the default config file path tools/regression-tests/config.yaml +''' +def get_default_config(lmp_binary): + # guess the build folder path + lmp_build_folder = lmp_binary.rsplit('/', 1)[0] + + # guess the tools/regression-tests folder + regression_tests_folder = lmp_build_folder + "/../tools/regression-tests/" + + defaultConfigFile = regression_tests_folder + "config.yaml" + return defaultConfigFile + ''' split a list into a list of N sublists @@ -978,7 +1041,7 @@ if __name__ == "__main__": # parse the arguments parser = ArgumentParser() parser.add_argument("--lmp-bin", dest="lmp_binary", default="", help="LAMMPS binary") - parser.add_argument("--config-file", dest="config_file", default=configFileName, help="Configuration YAML file") + parser.add_argument("--config-file", dest="config_file", default="", help="Configuration YAML file") parser.add_argument("--examples-top-level", dest="example_toplevel", default="", help="Examples top-level") parser.add_argument("--example-folders", dest="example_folders", default="", help="Example subfolders") parser.add_argument("--list-input", dest="list_input", default="", help="File that lists the input scripts") @@ -1009,7 +1072,11 @@ if __name__ == "__main__": args = parser.parse_args() lmp_binary = os.path.abspath(args.lmp_binary) - configFileName = args.config_file + if len(args.config_file) > 0: + configFileName = args.config_file + else: + configFileName = get_default_config(lmp_binary) + output_file = args.output if int(args.num_workers) > 0: num_workers = int(args.num_workers) @@ -1044,6 +1111,9 @@ if __name__ == "__main__": if example_toplevel != "": print("\nTop-level example folder:") print(f" {example_toplevel}") + if list_input != "": + print("\nInput scripts to test as listed in the file:") + print(f" {list_input}") # Using in place input scripts inplace_input = True @@ -1251,6 +1321,9 @@ if __name__ == "__main__": except Exception: print(f" Cannot open progress file {progress_file_abs} to resume, rerun all the tests") + # get a reference walltime + walltime_ref = get_reference_walltime(lmp_binary, config) + # record all the failure cases (overwrite if the file exists) failure_file_abs = pwd + "/" + failure_file failure = open(failure_file_abs, "w") From de8dc828017e3fc00e984f42e1081a6ae99e1076 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Fri, 6 Sep 2024 09:19:00 -0500 Subject: [PATCH 228/355] report walltime normalized by the reference walltime for completed runs in the progress.yaml file --- tools/regression-tests/run_tests.py | 30 +++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index 51a0a60f7c..767828795f 100644 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -133,8 +133,7 @@ class TestResult: input_folder : the absolute path to the input files input_list : list of the input scripts under the input_folder config : the dict that contains the test configuration - - output_buf: placeholder for storing the output of a given worker + walltime_ref : reference walltime return results : a list of TestResult objects @@ -142,8 +141,9 @@ class TestResult: progress_file: yaml file that stores the tested input script and status failure_file : file that reports the failed runs (a subset of progress_file) last_progress: the dictionary that shows the status of the last tests + output_buf: placeholder for storing the output of a given worker ''' -def iterate(lmp_binary, input_folder, input_list, config, results, progress_file, failure_file, verbose=False, last_progress=None, output_buf=None): +def iterate(lmp_binary, input_folder, input_list, config, results, progress_file, failure_file, walltime_ref=1, verbose=False, last_progress=None, output_buf=None): num_tests = len(input_list) num_completed = 0 @@ -420,6 +420,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file continue # NOTE: Total wall time could be 00:00:00 whereas Loop time is non-zero seconds + walltime_norm = 1.0 for line in output.split('\n'): if "Total wall time" in line: walltime_str = line.split('time:')[1] @@ -428,6 +429,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file minutes = float(hms[1]) seconds = float(hms[2]) walltime = hours * 3600.0 + minutes * 60.0 + seconds + walltime_norm = float(walltime) / float(walltime_ref) break # if there is no Step or no Loop printed out @@ -439,7 +441,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file logger.info(f"\n Output:\n{output}") logger.info(f"\n Error:\n{error}") - msg = f"{input}: {{ folder: {input_folder}, status: \"completed, but no Step nor Loop in the output.\", walltime: {walltime} }}\n" + msg = f"{input}: {{ folder: {input_folder}, status: \"completed, but no Step nor Loop in the output.\", walltime: {walltime}, walltime_norm: {walltime_norm} }}\n" progress.write(msg) progress.close() failure.write(msg) @@ -467,7 +469,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file result.status = msg + ", error parsing log.lammps into YAML" results.append(result) - progress.write(f"{input}: {{ folder: {input_folder}, status: \"{result.status}\", walltime: {walltime} }}\n") + progress.write(f"{input}: {{ folder: {input_folder}, status: \"{result.status}\", walltime: {walltime}, walltime_norm: {walltime_norm} }}\n") progress.close() if verbose == True: @@ -489,7 +491,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file logger.info(f" failed, error parsing the reference log file {thermo_ref_file}.") result.status = "skipped numerical checks due to parsing the reference log file" results.append(result) - progress.write(f"{input}: {{ folder: {input_folder}, status: \"completed, numerical checks skipped, unsupported log file format\", walltime: {walltime} }}\n") + progress.write(f"{input}: {{ folder: {input_folder}, status: \"completed, numerical checks skipped, unsupported log file format\", walltime: {walltime}, walltime_norm: {walltime_norm} }}\n") progress.close() num_error = num_error + 1 test_id = test_id + 1 @@ -505,12 +507,12 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file thermo_ref = extract_thermo(thermo_ref_file) num_runs_ref = len(thermo_ref) else: - # mostly will come to here if the reference log file does not exist + # most likely to reach here if the reference log file does not exist logger.info(f" {thermo_ref_file} also does not exist in the working directory.") result.status = "skipped due to missing the reference log file" results.append(result) - msg = f"{input}: {{ folder: {input_folder}, status: \"completed, numerical checks skipped due to missing the reference log file\", walltime: {walltime} }}\n" + msg = f"{input}: {{ folder: {input_folder}, status: \"completed, numerical checks skipped due to missing the reference log file\", walltime: {walltime}, walltime_norm: {walltime_norm} }}\n" progress.write(msg) progress.close() failure.write(msg) @@ -527,7 +529,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file " Check README in the folder, possibly due to using mpirun with partitions or parsing the wrong reference log file.") result.status = "failed, incomplete runs" results.append(result) - progress.write(f"{input}: {{ folder: {input_folder}, status: \"{result.status}\", walltime: {walltime} }}\n") + progress.write(f"{input}: {{ folder: {input_folder}, status: \"{result.status}\", walltime: {walltime}, walltime_norm: {walltime_norm} }}\n") progress.close() num_error = num_error + 1 test_id = test_id + 1 @@ -543,7 +545,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file logger.info(f" Check both log files for more details.") result.status = "failed, mismatched columns in the log files" results.append(result) - progress.write(f"{input}: {{ folder: {input_folder}, status: \"{result.status}\", walltime: {walltime} }}\n") + progress.write(f"{input}: {{ folder: {input_folder}, status: \"{result.status}\", walltime: {walltime}, walltime_norm: {walltime_norm} }}\n") progress.close() num_error = num_error + 1 test_id = test_id + 1 @@ -673,12 +675,12 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file msg += ", memory leaks detected" num_memleak = num_memleak + 1 - progress.write(f"{input}: {{ folder: {input_folder}, status: \"{msg}\", walltime: {walltime} }}\n") + progress.write(f"{input}: {{ folder: {input_folder}, status: \"{msg}\", walltime: {walltime}, walltime_norm: {walltime_norm} }}\n") progress.close() # write to failure if there is any numerical failed check if num_abs_failed > 0 or num_rel_failed > 0: - failure.write(f"{input}: {{ folder: {input_folder}, status: \"{msg}\", walltime: {walltime} }}\n") + failure.write(f"{input}: {{ folder: {input_folder}, status: \"{msg}\", walltime: {walltime}, walltime_norm: {walltime_norm} }}\n") # count the number of completed runs num_completed = num_completed + 1 @@ -896,7 +898,7 @@ def get_reference_walltime(lmp_binary, config): seconds = float(hms[2]) walltime = hours * 3600.0 + minutes * 60.0 + seconds - logger.info(f" Reference walltime = {walltime}") + logger.info(f" Reference walltime, sec = {walltime}") return walltime @@ -1382,7 +1384,7 @@ if __name__ == "__main__": # iterate through the input scripts results = [] stat = iterate(lmp_binary, directory, input_list, config, - results, progress_file_abs, failure_file_abs, verbose, last_progress) + results, progress_file_abs, failure_file_abs, walltime_ref, verbose, last_progress) completed_tests += stat['num_completed'] skipped_tests += stat['num_skipped'] From b2cc2582e11f70cef2c87e38c86bc60400b14d90 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Fri, 6 Sep 2024 09:33:23 -0500 Subject: [PATCH 229/355] switch to config.yaml in actual runs in quick and full tests --- .github/workflows/full-regression.yml | 2 +- .github/workflows/quick-regression.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index c565ab8d88..6060f2cef5 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -82,7 +82,7 @@ jobs: python3 tools/regression-tests/run_tests.py \ --lmp-bin=build/lmp \ - --config-file=tools/regression-tests/config_serial.yaml \ + --config-file=tools/regression-tests/config.yaml \ --list-input=input-list-${{ matrix.idx }}.txt \ --output-file=output-${{ matrix.idx }}.xml \ --progress-file=progress-${{ matrix.idx }}.yaml \ diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index d20bf8b364..fe7640004f 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -84,7 +84,7 @@ jobs: then \ python3 tools/regression-tests/run_tests.py \ --lmp-bin=build/lmp \ - --config-file=tools/regression-tests/config_serial.yaml \ + --config-file=tools/regression-tests/config.yaml \ --list-input=input-list-${{ matrix.idx }}.txt \ --output-file=output-${{ matrix.idx }}.xml \ --progress-file=progress-${{ matrix.idx }}.yaml \ From 78342e5b6f06bc3d76a5897aaa5790fbf7efdb45 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 6 Sep 2024 12:28:46 -0400 Subject: [PATCH 230/355] fix typo --- src/KOKKOS/fft3d_kokkos.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/KOKKOS/fft3d_kokkos.cpp b/src/KOKKOS/fft3d_kokkos.cpp index a4cc4eeb07..f4c5fa5028 100644 --- a/src/KOKKOS/fft3d_kokkos.cpp +++ b/src/KOKKOS/fft3d_kokkos.cpp @@ -157,7 +157,7 @@ public: KOKKOS_INLINE_FUNCTION void operator() (const int &i) const { -#if defined(FFT_KOKKOS_FFTW3) || defined(FFT_KOKKOS_CUFFT) || defined(FFT_KOKKOS_HIPFFT) || defined(FFT_KOKKOS_MKL_GPU || defined(FFT_KOKKOS_NVPL) +#if defined(FFT_KOKKOS_FFTW3) || defined(FFT_KOKKOS_CUFFT) || defined(FFT_KOKKOS_HIPFFT) || defined(FFT_KOKKOS_MKL_GPU) || defined(FFT_KOKKOS_NVPL) FFT_SCALAR* out_ptr = (FFT_SCALAR *)(d_out.data()+i); *(out_ptr++) *= norm; *(out_ptr++) *= norm; From 932f10e3b6f6c65034a1af974216fb1bbb187840 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Fri, 6 Sep 2024 12:46:42 -0500 Subject: [PATCH 231/355] update README for syntax and added arguments in run_tests.py --- tools/regression-tests/README | 118 ++++++++++++++++++---------- tools/regression-tests/run_tests.py | 30 +++---- 2 files changed, 92 insertions(+), 56 deletions(-) diff --git a/tools/regression-tests/README b/tools/regression-tests/README index eec11c19ff..1342e50310 100644 --- a/tools/regression-tests/README +++ b/tools/regression-tests/README @@ -1,5 +1,5 @@ The script `run_tests.py` in this folder is used to perform regression tests -using in-place example scripts. +using in-place example scripts and provided log files as reference. What this single script does is to launch the selected LAMMPS binary using a testing configuration defined in a `.yaml` file (e.g., `config.yaml`) @@ -19,25 +19,43 @@ within the specified tolerances in the test configuration `config.yaml` file. With the current features, users can: + specify which LAMMPS binary version to test (e.g., the version from a commit, or those from `lammps-testing`) - + specify the examples subfolders (thus the reference log files) seperately (e.g. from other LAMMPS versions or commits) - + specify tolerances for individual quantities for any input script to override the global values - + launch tests with `mpirun` with all supported command line features (multiple procs, multiple paritions, and suffices) - + skip certain input files if not interested, or no reference log file exists - + simplify the main LAMMPS builds, as long as a LAMMPS binary is available + + specify the examples subfolders (thus the reference log files) seperately (e.g. from other LAMMPS versions or commits), or + + specify a file that lists of the examples input scripts to test + + specify tolerances for individual quantities for any input script to override the global values in the config file + + launch tests with `mpirun` with all supported command line features (multiple procs, multiple paritions, and suffixes) + + skip certain input files (whose names match specified patterns) if not interested, or packaged not installed, or no reference log file exists + + set a timeout for every input script run if they may take too long + + skip numerical checks if the goal is just to check if the runs do not fail + +Some benefits include: + + + separating regression testing from building LAMMPS + + performing quick and full regression tests + + keeping track of the testing progress to resume the testing from the last checkpoint (skipping completed runs) + + distributing the input list across multiple processes by + splitting the list of input scripts into separate runs (there are ~800 input scripts under the top-level examples) + +Input arguments: + + + the path to a LAMMPS binary (can be relative to the working directory) + + a test configuration file (see tools/regression-tests/config.yaml for an example) + + a text file that lists of folders where the input scripts reside and how many of them line by line, or + a text file that list of input scripts, or + the path to the top-level examples + +Output: + + + failure.yaml : a dictionary of the failed runs and reasons + + progress.yaml: full testing results of the tested input scripts with the status (completed, failed or skipped) + with error messages (for failed runs), and walltime (in seconds) + + output.xml : testing results in the JUnit XML format + + run.log : screen output and error of individual runs Limitations: - - input scripts use thermo style multi (e.g., examples/peptide) do not work with the expected thermo output format - - input scripts that require partition runs (e.g. examples/neb) need a separate config file, e.g. "args: --partition 2x1" - - testing accelerator packages (GPU, INTEL, KOKKOS, OPENMP) need separate config files, "args: -sf omp -pk omp 4" - -TODO: - - + keep track of the testing progress to resume the testing from the last checkpoint - + distribute the input list across multiple processes via multiprocessing, or - split the list of input scripts into separate runs (there are 800+ input script under the top-level examples) - + be able to be invoked from run_tests in the lammps-testing infrastruture - + + input scripts use thermo style multi (e.g., examples/peptide) do not work with the expected thermo output format + + input scripts that require partition runs (e.g. examples/neb) need a separate config file, e.g., args: "--partition 3x1" + + testing with accelerator packages (GPU, INTEL, KOKKOS, OPENMP) need separate config files, e.g., args: "-sf omp -pk omp 4" The following Python packages need to be installed into an activated environment: @@ -45,40 +63,44 @@ The following Python packages need to be installed into an activated environment source testing-env/bin/activate pip install numpy pyyaml junit_xml +For all the supported arguments, run: -Example uses: + python3 tools/regression-tests/run_tests.py -h + +Example uses (aka, tests for this script): 1) Simple use (using the provided tools/regression-tests/config.yaml and the examples/ folder at the top level) - python3 run_tests.py --lmp-bin=/path/to/lmp_binary + python3 run_tests.py --lmp-bin=build/lmp --config-file=tools/regression-tests/config.yaml 2) Use a custom testing configuration python3 run_tests.py --lmp-bin=/path/to/lmp_binary --config-file=/path/to/config/file/config.yaml 3) Specify a list of example folders python3 run_tests.py --lmp-bin=/path/to/lmp_binary --config-file=/path/to/config/file/config.yaml \ - --example-folders="/path/to/examples/folder1;/path/to/examples/folder2" + --example-folders="/path/to/examples/melt;/path/to/examples/rigid" - The example folders can also be loaded from a text file list_subfolders1.txt: + The example subfolders can also be loaded from a text file list_subfolders1.txt: python3 run_tests.py --lmp-bin=/path/to/lmp_binary --config-file=/path/to/config/file/config.yaml \ - --list-input=list_subfolders1.txt --output-file=output1.txt --progress-file=progress1.yaml \ + --list-subfolders=list_subfolders1.txt --output-file=output1.txt --progress-file=progress1.yaml \ --log-file=run1.log - 4) Test a LAMMPS binary with the whole top-level /examples folder in a LAMMPS source tree - python3 run_tests.py --lmp-bin=/path/to/lmp_binary --examples-top-level=/path/to/lammps/examples + 4) Specify a list of example input scripts (e.g. obtained from running tools/regression-tests/get-quick-list.py) + python3 run_tests.py --lmp-bin=/path/to/lmp_binary --config-file=/path/to/config/file/config.yaml \ + --list-input=input_list.txt - 5) Analyze (dry run) the LAMMPS binary and whole top-level /examples folder in a LAMMPS source tree + 5) Test a LAMMPS binary with the whole top-level /examples folder in a LAMMPS source tree + python3 run_tests.py --lmp-bin=/path/to/lmp_binary --examples-top-level=/path/to/lammps/examples + --config-file=tools/regression-tests/config.yaml + + 6) Analyze the LAMMPS binary and whole top-level /examples folder in a LAMMPS source tree and generate separate input lists for 8 workers: python3 run_tests.py --lmp-bin=/path/to/lmp_binary --examples-top-level=/path/to/lammps/examples \ --analyze --num-workers=8 - This is used for splitting the subfolders into separate input lists and launching different instances - of run_tests.py simultaneously. - - 6) Prepare (dry run) for a quick regression test run that only runs inputs with commands and styles that - have changes in the current branch versus the selected upstream branch. Curb at 40 runs and split and - write out separate lists for 4 workers: - python3 run_tests.py --lmp-bin=/path/to/lmp_binary --examples-top-level=/path/to/lammps/examples \ - --quick --quick-branch=origin/develop --quick-max= 40 --num-workers=4 + The output of this run is 8 files folder-list-[0-7].txt that lists the subfolders + and 8 files input-list-[0-7].txt that lists the input scripts under the top-level example folders. + With these lists, one can launch multiple instances of run_tests.py simultaneously + each with a list of example subfolders (Case 3), or with a list of input scripts (Case 4). An example of the test configuration `config.yaml` is given as below. @@ -113,17 +135,31 @@ An example of the test configuration `config.yaml` is given as below. abs: 1e-2 rel: 1e-4 skip: - [ in.rigid.poems3, - in.rigid.poems4, - in.rigid.poems5, + [ in.displ, + in.displ2, + in.*_imd*, ] nugget: 1.0 epsilon: 1e-16 + timeout: 180 -An example of the list of input scripts in a text file `list_subfolders1.txt` +An example of the list of example subfolders in a text file `list_subfolders1.txt` + + /home/codes/lammps/examples/melt 1 + /home/codes/lammps/examples/body 5 + /home/codes/lammps/examples/PACKAGES/dielectric 2 + /home/codes/lammps/examples/PACKAGES/tally 3 + +where the numbers are the number of input scripts (in.*) in the folders. + + +An example of the list of input scripts in a text file `input_list.txt` + + /home/codes/lammps/examples/melt/in.melt + /home/codes/lammps/examples/body/in.body + /home/codes/lammps/examples/body/in.cubes + /home/codes/lammps/examples/PACKAGES/dielectric/in.confined + /home/codes/lammps/examples/PACKAGES/tally/in.pe + /home/codes/lammps/examples/PACKAGES/tally/in.force -/home/codes/lammps/examples/melt -/home/codes/lammps/examples/body -/home/codes/lammps/examples/PACKAGES/dielectric -/home/codes/lammps/examples/PACKAGES/tally diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index 767828795f..1bb40b17df 100644 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -11,16 +11,19 @@ With the current features, users can: + specify the examples subfolders (thus the reference log files) seperately (e.g. from other LAMMPS versions or commits) + specify the list of examples input scripts to test + specify tolerances for individual quantities for any input script to override the global values - + launch tests with `mpirun` with all supported command line features (multiple procs, multiple paritions, and suffices) + + launch tests with `mpirun` with all supported command line features (multiple procs, multiple paritions, and suffixes) + skip certain input files (whose names match specified patterns) if not interested, or packaged not installed, or no reference log file exists + set a timeout for every input script run if they may take too long + skip numerical checks if the goal is just to check if the runs do not fail - + simplify the main LAMMPS builds, as long as a LAMMPS binary is available - + keep track of the testing progress to resume the testing from the last checkpoint (skipping completed runs) - + distribute the input list across multiple processes via multiprocessing, or - split the list of input scripts into separate runs (there are 800+ input script under the top-level examples) +Some benefits include: + + separating regression testing from building LAMMPS + + performing quick and full regression tests + + keeping track of the testing progress to resume the testing from the last checkpoint (skipping completed runs) + + distributing the input list across multiple processes by + splitting the list of input scripts into separate runs (there are ~800 input scripts under the top-level examples) + Input arguments: + the path to a LAMMPS binary (can be relative to the working directory) + a test configuration file (see tools/regression-tests/config.yaml for an example) @@ -40,9 +43,6 @@ Limitations: - input scripts that require partition runs (e.g. examples/neb) need a separate config file, e.g. args: "--partition 3x1" - testing accelerator packages (GPU, INTEL, KOKKOS, OPENMP) need separate config files, "args: -sf omp -pk omp 4" -TODO: - + be able to be invoked from run_tests in the lammps-testing infrastruture - The following Python packages need to be installed into an activated environment: python3 -m venv testing-env @@ -1049,12 +1049,6 @@ if __name__ == "__main__": parser.add_argument("--list-input", dest="list_input", default="", help="File that lists the input scripts") parser.add_argument("--list-subfolders", dest="list_subfolders", default="", help="File that lists the subfolders") parser.add_argument("--num-workers", dest="num_workers", default=1, help="Number of workers") - parser.add_argument("--gen-ref",dest="genref", action='store_true', default=False, - help="Generating reference data") - parser.add_argument("--verbose",dest="verbose", action='store_true', default=False, - help="Verbose output") - parser.add_argument("--resume",dest="resume", action='store_true', default=False, - help="Resume the test run") parser.add_argument("--output-file",dest="output", default=output_file, help="Output file") parser.add_argument("--log-file",dest="logfile", default=log_file, help="Log file") parser.add_argument("--progress-file",dest="progress_file", default=progress_file, help="Progress file") @@ -1069,7 +1063,13 @@ if __name__ == "__main__": parser.add_argument("--quick-max", dest="quick_max", default=50, help="Maximum number of inputs to randomly select") parser.add_argument("--skip-numerical-check",dest="skip_numerical_check", action='store_true', default=False, - help="Generating reference data") + help="Skip numerical checks") + parser.add_argument("--gen-ref",dest="genref", action='store_true', default=False, + help="Generating reference log files") + parser.add_argument("--verbose",dest="verbose", action='store_true', default=False, + help="Verbose screen output") + parser.add_argument("--resume",dest="resume", action='store_true', default=False, + help="Resume the test run from the list of inputs given the progress in progress.yaml") args = parser.parse_args() From 42a7294bc1f2937af3e7b14d22d999d0be9e8e1f Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 6 Sep 2024 14:00:27 -0400 Subject: [PATCH 232/355] silence compiler warnings --- fortran/lammps.f90 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fortran/lammps.f90 b/fortran/lammps.f90 index 6ced71be49..a2f28073e5 100644 --- a/fortran/lammps.f90 +++ b/fortran/lammps.f90 @@ -1443,7 +1443,7 @@ CONTAINS IF (SIZE_TAGINT == 8) THEN Cptr = C_LOC(id) ELSE - id32 = id + id32 = INT(id, c_int) Cptr = C_LOC(id32) END IF lmp_map_atom_big = lammps_map_atom(self%handle, Cptr) + 1 @@ -2604,6 +2604,8 @@ CONTAINS TYPE(c_ptr) :: Cid, Ctype, Cx, Cv, Cimage INTEGER(c_int) :: tagint_size, atoms_created + Ctype = c_null_ptr + Cx = c_null_ptr ! type is actually NOT optional, but we can't make id optional without it, ! so we check at run-time IF (.NOT. PRESENT(type)) THEN From 8fcdb5c2713b8c8632c0b1fcc02313ada718f756 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 6 Sep 2024 14:58:42 -0400 Subject: [PATCH 233/355] silence compiler warnings --- src/EXTRA-COMMAND/ndx_group.cpp | 3 ++- src/angle_write.cpp | 4 +++- src/dihedral_write.cpp | 4 +++- tools/msi2lmp/src/ReadMdfFile.c | 7 +++---- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/EXTRA-COMMAND/ndx_group.cpp b/src/EXTRA-COMMAND/ndx_group.cpp index c5b0d3cf8a..d006473bb8 100644 --- a/src/EXTRA-COMMAND/ndx_group.cpp +++ b/src/EXTRA-COMMAND/ndx_group.cpp @@ -33,7 +33,8 @@ static std::string find_section(FILE *fp, const std::string &name) { char linebuf[BUFLEN]; - fgets(linebuf, BUFLEN, fp); + if (!fgets(linebuf, BUFLEN, fp)) + throw TokenizerException("Read error", utils::getsyserror()); while (!feof(fp)) { if (utils::strmatch(linebuf, "^\\s*\\[.*\\]\\s*$")) { auto words = Tokenizer(linebuf).as_vector(); diff --git a/src/angle_write.cpp b/src/angle_write.cpp index 48420ae7be..1be5f1acac 100644 --- a/src/angle_write.cpp +++ b/src/angle_write.cpp @@ -148,8 +148,10 @@ void AngleWrite::command(int narg, char **arg) FILE *coeffs; char line[MAXLINE] = {'\0'}; coeffs = fopen(coeffs_file.c_str(), "r"); + if (!coeffs) + error->one(FLERR, "Unable to open temporary file {}: {}", coeffs_file, utils::getsyserror()); for (int i = 0; i < atom->nangletypes; ++i) { - fgets(line, MAXLINE, coeffs); + utils::sfgets(FLERR, line, MAXLINE, coeffs, coeffs_file.c_str(), error); writer->input->one(fmt::format("angle_coeff {}", line)); } fclose(coeffs); diff --git a/src/dihedral_write.cpp b/src/dihedral_write.cpp index dd1ca1de6a..1d0f908e2c 100644 --- a/src/dihedral_write.cpp +++ b/src/dihedral_write.cpp @@ -149,8 +149,10 @@ void DihedralWrite::command(int narg, char **arg) FILE *coeffs; char line[MAXLINE] = {'\0'}; coeffs = fopen(coeffs_file.c_str(), "r"); + if (!coeffs) + error->one(FLERR, "Unable to open temporary file {}: {}", utils::getsyserror()); for (int i = 0; i < atom->ndihedraltypes; ++i) { - fgets(line, MAXLINE, coeffs); + utils::sfgets(FLERR, line, MAXLINE, coeffs, coeffs_file.c_str(), error); writer->input->one(fmt::format("dihedral_coeff {}", line)); } fclose(coeffs); diff --git a/tools/msi2lmp/src/ReadMdfFile.c b/tools/msi2lmp/src/ReadMdfFile.c index 96a6a01ab2..253121d001 100644 --- a/tools/msi2lmp/src/ReadMdfFile.c +++ b/tools/msi2lmp/src/ReadMdfFile.c @@ -144,7 +144,7 @@ void ReadMdfFile(void) molecule[n].residue[j].end = i; molecule[n].residue[++j].start = i; - strncpy(molecule[n].residue[j].name,atoms[i].residue_string,MAX_NAME); + memcpy(molecule[n].residue[j].name,atoms[i].residue_string,MAX_NAME); } } molecule[n].residue[j].end = molecule[n].end; @@ -167,10 +167,9 @@ void ReadMdfFile(void) for (n=0; n < no_molecules; n++) { for (j=0; j < molecule[n].no_residues; j++) { - for (i=molecule[n].residue[j].start; i < molecule[n].residue[j].end; - i++) { + for (i=molecule[n].residue[j].start; i < molecule[n].residue[j].end; i++) { for (l=0; l < atoms[i].no_connect; l++) { - strncpy(temp_string,atoms[i].connections[l],MAX_STRING); + memcpy(temp_string,atoms[i].connections[l],MAX_STRING); temp_residue = strtok(temp_string,":"); temp_atom_name = strtok(NULL,"%"); From bca271a2867d0cda2d8b46ac7d7c46b2a1ad5db5 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Fri, 6 Sep 2024 17:34:35 -0500 Subject: [PATCH 234/355] mention regression tester in Build_development --- doc/src/Build_development.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/src/Build_development.rst b/doc/src/Build_development.rst index f315569b24..55a1b97ea5 100644 --- a/doc/src/Build_development.rst +++ b/doc/src/Build_development.rst @@ -145,6 +145,10 @@ of the LAMMPS project on GitHub. A few tests are also run as GitHub Actions and their configuration files are in the ``.github/workflows/`` folder of the LAMMPS git tree. +Regression tests can also be performed locally with the :doc:`regression tester tool `. +The tool checks if a given LAMMPS binary run with selected input examples +produces consistent thermo output with the provided log files. + The unit testing facility is integrated into the CMake build process of the LAMMPS source code distribution itself. It can be enabled by setting ``-D ENABLE_TESTING=on`` during the CMake configuration step. From 93b4e918014c4a97230051b4b5f4dbce9527cbe2 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 6 Sep 2024 20:51:56 -0400 Subject: [PATCH 235/355] update docs and add ref --- doc/src/Build_development.rst | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/doc/src/Build_development.rst b/doc/src/Build_development.rst index 55a1b97ea5..3adec76abb 100644 --- a/doc/src/Build_development.rst +++ b/doc/src/Build_development.rst @@ -138,16 +138,27 @@ during development: The status of this automated testing can be viewed on `https://ci.lammps.org `_. -The scripts and inputs for integration, run, and regression testing -are maintained in a -`separate repository `_ -of the LAMMPS project on GitHub. A few tests are also run as GitHub -Actions and their configuration files are in the ``.github/workflows/`` -folder of the LAMMPS git tree. +The scripts and inputs for integration, run, and legacy regression +testing are maintained in a `separate repository +`_ of the LAMMPS project on +GitHub. A few tests are also run as GitHub Actions and their +configuration files are in the ``.github/workflows/`` folder of the +LAMMPS git tree. -Regression tests can also be performed locally with the :doc:`regression tester tool `. -The tool checks if a given LAMMPS binary run with selected input examples -produces consistent thermo output with the provided log files. +Regression tests can also be performed locally with the :ref:`regression +tester tool `. The tool checks if a given LAMMPS binary run +with selected input examples produces thermo output that is consistent +with the provided log files. The script can be run in one pass over all +available input files, but it can also first create multiple lists of +inputs or folders that can then be run with multiple workers +concurrently to speed things up. Another mode allows to do a quick +check of inputs that contain commands that have changes in the current +checkout branch relative to a git branch. This works similar to the two +pass mode, but will select only shorter runs and no more than 100 inputs +that are chosen randomly. This ensures that this test runs +significantly faster compared to the full test run. These test runs can +also be performed with instrumented LAMMPS binaries (see previous +section). The unit testing facility is integrated into the CMake build process of the LAMMPS source code distribution itself. It can be enabled by From 6214182fd0ddd5b5a406995fa85be00376fd304b Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 6 Sep 2024 21:06:01 -0400 Subject: [PATCH 236/355] remove unused imports, reformat --- python/lammps/numpy_wrapper.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/python/lammps/numpy_wrapper.py b/python/lammps/numpy_wrapper.py index 6e8c7ebcd2..5b90cf03de 100644 --- a/python/lammps/numpy_wrapper.py +++ b/python/lammps/numpy_wrapper.py @@ -18,12 +18,10 @@ from ctypes import POINTER, c_void_p, c_char_p, c_double, c_int, c_int32, c_int64, cast -from .constants import LAMMPS_AUTODETECT, LAMMPS_INT, LAMMPS_INT_2D, \ - LAMMPS_DOUBLE, LAMMPS_DOUBLE_2D, LAMMPS_INT64, LAMMPS_INT64_2D, \ - LMP_STYLE_GLOBAL, LMP_STYLE_ATOM, LMP_STYLE_LOCAL, \ - LMP_TYPE_SCALAR, LMP_TYPE_VECTOR, LMP_TYPE_ARRAY, \ - LMP_SIZE_VECTOR, LMP_SIZE_ROWS, LMP_SIZE_COLS, \ - LMP_VAR_EQUAL, LMP_VAR_ATOM, LMP_VAR_VECTOR, LMP_VAR_STRING +from .constants import LAMMPS_AUTODETECT, LAMMPS_INT, LAMMPS_INT_2D, LAMMPS_DOUBLE, \ + LAMMPS_DOUBLE_2D, LAMMPS_INT64, LAMMPS_INT64_2D, LMP_STYLE_GLOBAL, LMP_STYLE_ATOM, \ + LMP_STYLE_LOCAL, LMP_TYPE_VECTOR, LMP_TYPE_ARRAY, LMP_SIZE_VECTOR, LMP_SIZE_ROWS, \ + LMP_SIZE_COLS, LMP_VAR_EQUAL, LMP_VAR_ATOM from .data import NeighList From afc9f72887a693e24291375f15270910fdbc4003 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 6 Sep 2024 21:18:31 -0400 Subject: [PATCH 237/355] whitespace --- tools/regression-tests/run_tests.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index 1bb40b17df..068f960752 100644 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -23,7 +23,7 @@ Some benefits include: + keeping track of the testing progress to resume the testing from the last checkpoint (skipping completed runs) + distributing the input list across multiple processes by splitting the list of input scripts into separate runs (there are ~800 input scripts under the top-level examples) - + Input arguments: + the path to a LAMMPS binary (can be relative to the working directory) + a test configuration file (see tools/regression-tests/config.yaml for an example) @@ -174,7 +174,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file progress = open(progress_file, "w") # walltime = -2: skipped tests - # -1: failed tests + # -1: failed tests # >= 0: walltime in seconds (e.g. in.melt walltime = 0.2 seconds) walltime = -2 @@ -348,7 +348,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file results.append(result) print(f"{result.status}") - + msg = f"{input}: {{ folder: {input_folder}, status: \"{result.status}\", walltime: {walltime} }}\n" progress.write(msg) progress.close() @@ -381,7 +381,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file # if skip numerical checks, then skip the rest if skip_numerical_check == True: - msg = "completed, skipping numerical checks" + msg = "completed, skipping numerical checks" if use_valgrind == True: if "All heap blocks were freed" in error: msg += ", no memory leak" From 6e0c44a25ce0052ae0d1c7a76f293dee5062efab Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 6 Sep 2024 21:18:58 -0400 Subject: [PATCH 238/355] temporarily run the full test with the pull request --- .github/workflows/full-regression.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index 6060f2cef5..161c480793 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -3,8 +3,10 @@ name: "Full Regression Test" on: push: - branches: - - develop +# branches: +# - develop + pull_request: + - develop workflow_dispatch: From 6cd710444de56ce9cbe85647ad143091f0688572 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 6 Sep 2024 21:34:10 -0400 Subject: [PATCH 239/355] add dedicated config for quick regression test. allow oversubscription. --- .github/workflows/quick-regression.yml | 2 +- tools/regression-tests/config_quick.yaml | 44 +++++++++++++++++++++++ tools/regression-tests/config_serial.yaml | 1 - 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 tools/regression-tests/config_quick.yaml diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index fe7640004f..e79a8e9737 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -77,7 +77,7 @@ jobs: source linuxenv/bin/activate python3 tools/regression-tests/run_tests.py \ --lmp-bin=build/lmp \ - --config-file=tools/regression-tests/config.yaml \ + --config-file=tools/regression-tests/config_quick.yaml \ --examples-top-level=examples --quick --quick-branch=origin/develop --quick-max=100 --num-workers=4 if [ -f input-list-${{ matrix.idx }}.txt ] diff --git a/tools/regression-tests/config_quick.yaml b/tools/regression-tests/config_quick.yaml new file mode 100644 index 0000000000..8f2ba9aaa6 --- /dev/null +++ b/tools/regression-tests/config_quick.yaml @@ -0,0 +1,44 @@ +--- + lmp_binary: "" + nprocs: "4" + args: "-cite none" + mpiexec: "mpirun" + mpiexec_numproc_flag: "--host localhost:4 -np" + tolerance: + PotEng: + abs: 1e-4 + rel: 1e-7 + TotEng: + abs: 1e-4 + rel: 1e-7 + Press: + abs: 1e-4 + rel: 1e-7 + Temp: + abs: 1e-4 + rel: 1e-7 + E_vdwl: + abs: 1e-3 + rel: 1e-7 + overrides: + in.rigid.tnr: + Temp: + abs: 1e-3 + rel: 1e-5 + Press: + abs: 1e-2 + rel: 1e-4 + skip: + [ + in.displ, + in.displ2, + in.dos, + in.*_imd*, + in.bucky-plus-cnt*, + ] + + timeout: 30 + nugget: 1.0 + epsilon: 1e-16 + + diff --git a/tools/regression-tests/config_serial.yaml b/tools/regression-tests/config_serial.yaml index fb79c301f1..1fe3f48353 100644 --- a/tools/regression-tests/config_serial.yaml +++ b/tools/regression-tests/config_serial.yaml @@ -36,7 +36,6 @@ in.bucky-plus-cnt*, ] - timeout: 30 nugget: 1.0 epsilon: 1e-16 From b6e78c1f205ad1cebafe4d99f8f20f5e804f18c7 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 6 Sep 2024 21:46:18 -0400 Subject: [PATCH 240/355] another attempt to avoid oversubscription error --- .github/workflows/quick-regression.yml | 2 +- tools/regression-tests/config.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index e79a8e9737..3cea960987 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -84,7 +84,7 @@ jobs: then \ python3 tools/regression-tests/run_tests.py \ --lmp-bin=build/lmp \ - --config-file=tools/regression-tests/config.yaml \ + --config-file=tools/regression-tests/config_quick.yaml \ --list-input=input-list-${{ matrix.idx }}.txt \ --output-file=output-${{ matrix.idx }}.xml \ --progress-file=progress-${{ matrix.idx }}.yaml \ diff --git a/tools/regression-tests/config.yaml b/tools/regression-tests/config.yaml index 372d0db10b..6b793d0ce8 100644 --- a/tools/regression-tests/config.yaml +++ b/tools/regression-tests/config.yaml @@ -3,7 +3,7 @@ nprocs: "4" args: "-cite none" mpiexec: "mpirun" - mpiexec_numproc_flag: "-np" + mpiexec_numproc_flag: "--host localhost:4 -np" tolerance: PotEng: abs: 1e-4 From c853b8d81ae96f10f4dddb74722cff4622adca47 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 6 Sep 2024 23:14:09 -0400 Subject: [PATCH 241/355] switch quick run back to serial --- tools/regression-tests/config_quick.yaml | 6 +++--- tools/regression-tests/config_serial.yaml | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tools/regression-tests/config_quick.yaml b/tools/regression-tests/config_quick.yaml index 8f2ba9aaa6..3c8cc4e51b 100644 --- a/tools/regression-tests/config_quick.yaml +++ b/tools/regression-tests/config_quick.yaml @@ -1,9 +1,9 @@ --- lmp_binary: "" - nprocs: "4" + nprocs: "1" args: "-cite none" - mpiexec: "mpirun" - mpiexec_numproc_flag: "--host localhost:4 -np" + mpiexec: "" + mpiexec_numproc_flag: "" tolerance: PotEng: abs: 1e-4 diff --git a/tools/regression-tests/config_serial.yaml b/tools/regression-tests/config_serial.yaml index 1fe3f48353..b55cc1547d 100644 --- a/tools/regression-tests/config_serial.yaml +++ b/tools/regression-tests/config_serial.yaml @@ -29,7 +29,8 @@ abs: 1e-2 rel: 1e-4 skip: - [ in.displ, + [ + in.displ, in.displ2, in.dos, in.*_imd*, From e5c870fcd25e2945f67033bd550b33c35e9d4b1e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 6 Sep 2024 23:15:32 -0400 Subject: [PATCH 242/355] switch full regression back to serial execution --- .github/workflows/full-regression.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index 161c480793..5eb8e4f28b 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -79,12 +79,12 @@ jobs: source linuxenv/bin/activate python3 tools/regression-tests/run_tests.py \ --lmp-bin=build/lmp \ - --config-file=tools/regression-tests/config.yaml \ + --config-file=tools/regression-tests/config_serial.yaml \ --examples-top-level=examples --analyze --num-workers=4 python3 tools/regression-tests/run_tests.py \ --lmp-bin=build/lmp \ - --config-file=tools/regression-tests/config.yaml \ + --config-file=tools/regression-tests/config_serial.yaml \ --list-input=input-list-${{ matrix.idx }}.txt \ --output-file=output-${{ matrix.idx }}.xml \ --progress-file=progress-${{ matrix.idx }}.yaml \ From 5aea0a061ffea8f55c36137ade98f7b4aa8dd368 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 7 Sep 2024 00:22:56 -0400 Subject: [PATCH 243/355] provide updated reference and update command line --- .github/workflows/full-regression.yml | 6 ++--- .github/workflows/quick-regression.yml | 4 +++- tools/regression-tests/run_tests.py | 31 +++++++++++++++++++++++++- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index 5eb8e4f28b..106bda9d2e 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -3,10 +3,8 @@ name: "Full Regression Test" on: push: -# branches: -# - develop - pull_request: - - develop + branches: + - develop workflow_dispatch: diff --git a/.github/workflows/quick-regression.yml b/.github/workflows/quick-regression.yml index 3cea960987..985177b2c1 100644 --- a/.github/workflows/quick-regression.yml +++ b/.github/workflows/quick-regression.yml @@ -78,7 +78,9 @@ jobs: python3 tools/regression-tests/run_tests.py \ --lmp-bin=build/lmp \ --config-file=tools/regression-tests/config_quick.yaml \ - --examples-top-level=examples --quick --quick-branch=origin/develop --quick-max=100 --num-workers=4 + --examples-top-level=examples \ + --quick-reference=tools/regression-tests/reference.yaml \ + --quick --quick-branch=origin/develop --quick-max=100 --num-workers=4 if [ -f input-list-${{ matrix.idx }}.txt ] then \ diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index 068f960752..ea3d43e254 100644 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -1036,6 +1036,7 @@ if __name__ == "__main__": quick = False quick_branch = "origin/develop" quick_max = 50 + quick_reference = os.path.join(LAMMPS_DIR, 'tools', 'regression-tests', 'reference.yaml') # distribute the total number of input scripts over the workers num_workers = 1 @@ -1062,6 +1063,8 @@ if __name__ == "__main__": help="Branch to which compare the current head to for changed styles") parser.add_argument("--quick-max", dest="quick_max", default=50, help="Maximum number of inputs to randomly select") + parser.add_argument("--quick-reference", dest="quick_reference", default=quick_reference, + help="Reference YAML file with progress data from full regression test run") parser.add_argument("--skip-numerical-check",dest="skip_numerical_check", action='store_true', default=False, help="Skip numerical checks") parser.add_argument("--gen-ref",dest="genref", action='store_true', default=False, @@ -1098,6 +1101,7 @@ if __name__ == "__main__": quick = args.quick quick_branch = args.quick_branch quick_max = int(args.quick_max) + quick_reference = args.quick_reference skip_numerical_check = args.skip_numerical_check resume = args.resume progress_file = args.progress_file @@ -1132,13 +1136,38 @@ if __name__ == "__main__": msg = f"\nThere are {len(input_list)} input scripts with changed styles relative to branch {quick_branch}." msg += "\nChanged styles: " + str(styles) + # read in refrence data from a previous test run + with open(quick_reference, 'r') as f: + reference = yaml.load(f, Loader=Loader) + f.close() + + # trim previously failing run and runs that would take too long + new_list = [] + keys = reference.keys() + msg += "\nTrimming inputs using reference data from " + str(len(keys)) + " previous runs: " + for infile in input_list: + input = os.path.split(infile)[1] + if input in keys: + if (reference[input]['walltime'] < 0.0): + # print("Skipping ", input, " for previous failure") + pass + elif (reference[input]['walltime'] > 29.0): + # print("Skipping ", input, " for wall time limit") + pass + else: + new_list.append(infile) + else: + new_list.append(infile) + input_list = new_list + msg += "trimmed list has " + str(len(input_list)) + " entries" + if len(input_list) > quick_max: input_list = random.sample(input_list, quick_max) msg += "\nTesting " + str(quick_max) + " randomly selected inputs" print(msg) logger.info(msg) - + quit() # divide the list of input scripts into num_workers chunks sublists = divide_into_N(input_list, num_workers) From fa5a3446c0c0475a8b1e38a5a149ebc483ac7041 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 7 Sep 2024 00:28:59 -0400 Subject: [PATCH 244/355] add forgotten file --- tools/regression-tests/reference.yaml | 809 ++++++++++++++++++++++++++ 1 file changed, 809 insertions(+) create mode 100644 tools/regression-tests/reference.yaml diff --git a/tools/regression-tests/reference.yaml b/tools/regression-tests/reference.yaml new file mode 100644 index 0000000000..2daf17cf13 --- /dev/null +++ b/tools/regression-tests/reference.yaml @@ -0,0 +1,809 @@ +in.granregion.box: { folder: examples/granregion, status: "completed, 8 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } +in.granregion.funnel: { folder: examples/granregion, status: "failed, no Total wall time in the output.", walltime: -1 } +in.granregion.mixer: { folder: examples/granregion, status: "failed, no Total wall time in the output.", walltime: -1 } +in.melt: { folder: examples/melt, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.airebo: { folder: examples/airebo, status: "failed, no Total wall time in the output.", walltime: -1 } +in.airebo-0-0: { folder: examples/airebo, status: "completed, thermo checks passed", walltime: 5.0, walltime_norm: 0.8333333333333334 } +in.airebo-m: { folder: examples/airebo, status: "failed, no Total wall time in the output.", walltime: -1 } +in.rebo2: { folder: examples/airebo, status: "completed, thermo checks passed", walltime: 5.0, walltime_norm: 0.8333333333333334 } +in.hybrid: { folder: examples/template, status: "completed, 3 rel thermo checks failed", walltime: 7.0, walltime_norm: 1.1666666666666667 } +in.mol-data-mix: { folder: examples/template, status: "completed, 3 rel thermo checks failed", walltime: 18.0, walltime_norm: 3.0 } +in.mol-restart-mix: { folder: examples/template, status: "completed, thermo checks passed", walltime: 45.0, walltime_norm: 7.5 } +in.molecular-mix: { folder: examples/template, status: "completed, 5 rel thermo checks failed", walltime: 26.0, walltime_norm: 4.333333333333333 } +in.template-mix: { folder: examples/template, status: "completed, 5 rel thermo checks failed", walltime: 26.0, walltime_norm: 4.333333333333333 } +in.tmpl-data-mix: { folder: examples/template, status: "completed, 3 rel thermo checks failed", walltime: 18.0, walltime_norm: 3.0 } +in.tmpl-restart-mix: { folder: examples/template, status: "completed, thermo checks passed", walltime: 45.0, walltime_norm: 7.5 } +in.first: { folder: examples/rerun, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 16.0, walltime_norm: 2.6666666666666665 } +in.rdf.first: { folder: examples/rerun, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 16.0, walltime_norm: 2.6666666666666665 } +in.rdf.rerun: { folder: examples/rerun, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 5.0, walltime_norm: 0.8333333333333334 } +in.read_dump: { folder: examples/rerun, status: "failed, no Total wall time in the output.", walltime: -1 } +in.rerun: { folder: examples/rerun, status: "failed, no Total wall time in the output.", walltime: -1 } +in.lj.ehex: { folder: examples/HEAT, status: "failed, no Total wall time in the output.", walltime: -1 } +in.lj.hex: { folder: examples/HEAT, status: "failed, no Total wall time in the output.", walltime: -1 } +in.spce.ehex: { folder: examples/HEAT, status: "failed, no Total wall time in the output.", walltime: -1 } +in.spce.hex: { folder: examples/HEAT, status: "failed, no Total wall time in the output.", walltime: -1 } +in.vashishta.inp: { folder: examples/vashishta, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.vashishta.sio2: { folder: examples/vashishta, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.vashishta.table.inp: { folder: examples/vashishta, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.vashishta.table.sio2: { folder: examples/vashishta, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.rigid: { folder: examples/rigid, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.rigid.atomfile: { folder: examples/rigid, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.rigid.atomvar: { folder: examples/rigid, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.rigid.early: { folder: examples/rigid, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.rigid.gravity: { folder: examples/rigid, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.rigid.infile: { folder: examples/rigid, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.rigid.molecule: { folder: examples/rigid, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.rigid.nve: { folder: examples/rigid, status: "completed, 3 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } +in.rigid.nve.early: { folder: examples/rigid, status: "completed, 3 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } +in.rigid.poems: { folder: examples/rigid, status: "completed, 3 rel thermo checks failed", walltime: 3.0, walltime_norm: 0.5 } +in.rigid.poems2: { folder: examples/rigid, status: "completed, 3 rel thermo checks failed", walltime: 3.0, walltime_norm: 0.5 } +in.rigid.poems3: { folder: examples/rigid, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } +in.rigid.poems4: { folder: examples/rigid, status: "completed, 3 rel thermo checks failed", walltime: 3.0, walltime_norm: 0.5 } +in.rigid.poems5: { folder: examples/rigid, status: "completed, 3 rel thermo checks failed", walltime: 3.0, walltime_norm: 0.5 } +in.rigid.property: { folder: examples/rigid, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.rigid.small: { folder: examples/rigid, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.rigid.small.infile: { folder: examples/rigid, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.rigid.tnr: { folder: examples/rigid, status: "completed, 22 rel thermo checks failed", walltime: 20.0, walltime_norm: 3.3333333333333335 } +in.voronoi: { folder: examples/voronoi, status: "completed, but no Step nor Loop in the output.", walltime: 6.0, walltime_norm: 1.0 } +in.voronoi.2d: { folder: examples/voronoi, status: "completed, but no Step nor Loop in the output.", walltime: 0.0, walltime_norm: 0.0 } +in.voronoi.data: { folder: examples/voronoi, status: "completed, but no Step nor Loop in the output.", walltime: 0.0, walltime_norm: 0.0 } +in.ehex: { folder: examples/KAPPA, status: "failed, no Total wall time in the output.", walltime: -1 } +in.heat: { folder: examples/KAPPA, status: "failed, no Total wall time in the output.", walltime: -1 } +in.heatflux: { folder: examples/KAPPA, status: "failed, no Total wall time in the output.", walltime: -1 } +in.langevin: { folder: examples/KAPPA, status: "failed, no Total wall time in the output.", walltime: -1 } +in.mp: { folder: examples/KAPPA, status: "failed, no Total wall time in the output.", walltime: -1 } +in.pour: { folder: examples/pour, status: "completed, thermo checks passed", walltime: 42.0, walltime_norm: 7.0 } +in.pour.2d: { folder: examples/pour, status: "completed, thermo checks passed", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.pour.2d.molecule: { folder: examples/pour, status: "completed, thermo checks passed", walltime: 5.0, walltime_norm: 0.8333333333333334 } +in.deposit.atom: { folder: examples/deposit, status: "completed, 3 rel thermo checks failed", walltime: 7.0, walltime_norm: 1.1666666666666667 } +in.deposit.molecule: { folder: examples/deposit, status: "completed, 3 rel thermo checks failed", walltime: 8.0, walltime_norm: 1.3333333333333333 } +in.deposit.molecule.rigid-nve-small: { folder: examples/deposit, status: "completed, thermo checks passed", walltime: 9.0, walltime_norm: 1.5 } +in.deposit.molecule.rigid-nvt-small: { folder: examples/deposit, status: "completed, thermo checks passed", walltime: 9.0, walltime_norm: 1.5 } +in.deposit.molecule.rigid-small: { folder: examples/deposit, status: "completed, thermo checks passed", walltime: 8.0, walltime_norm: 1.3333333333333333 } +in.deposit.molecule.shake: { folder: examples/deposit, status: "completed, 3 rel thermo checks failed", walltime: 7.0, walltime_norm: 1.1666666666666667 } +in.charmmfsw: { folder: examples/charmmfsw, status: "completed, thermo checks passed", walltime: 27.0, walltime_norm: 4.5 } +in.indent: { folder: examples/indent, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } +in.indent.min: { folder: examples/indent, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.qeq.buck: { folder: examples/qeq, status: "completed, thermo checks passed", walltime: 29.0, walltime_norm: 4.833333333333333 } +in.qeq.reaxff: { folder: examples/qeq, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.dreiding: { folder: examples/dreiding, status: "completed, error parsing log.lammps into YAML", walltime: 0.0, walltime_norm: 0.0 } +in.22DMH.real: { folder: examples/relres, status: "failed, no Total wall time in the output.", walltime: -1 } +in.22DMH.relres: { folder: examples/relres, status: "failed, incomplete runs", walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.22DMH.respa: { folder: examples/relres, status: "completed, thermo checks passed", walltime: 7.0, walltime_norm: 1.1666666666666667 } +in.track: { folder: examples/tracker, status: "failed, ERROR: Illegal pair_style command (src/MISC/pair_tracker.cpp:221).", walltime: -1 } +in.pour.drum: { folder: examples/granular, status: "completed, thermo checks passed", walltime: 15.0, walltime_norm: 2.5 } +in.pour.flatwall: { folder: examples/granular, status: "completed, thermo checks passed", walltime: 14.0, walltime_norm: 2.3333333333333335 } +in.pour.heat: { folder: examples/granular, status: "failed, no Total wall time in the output.", walltime: -1 } +in.restitution: { folder: examples/granular, status: "completed, thermo checks passed", walltime: 7.0, walltime_norm: 1.1666666666666667 } +in.micelle: { folder: examples/micelle, status: "completed, 1 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } +in.micelle-rigid: { folder: examples/micelle, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.replicate.bond.x: { folder: examples/replicate, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.replicate.bond.x.noloop: { folder: examples/replicate, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.replicate.bond.x.y: { folder: examples/replicate, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.replicate.bond.xy: { folder: examples/replicate, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.replicate.cnt: { folder: examples/replicate, status: "completed, thermo checks passed", walltime: 13.0, walltime_norm: 2.1666666666666665 } +in.srd.mixture: { folder: examples/srd, status: "completed, 3 rel thermo checks failed", walltime: 3.0, walltime_norm: 0.5 } +in.srd.pure: { folder: examples/srd, status: "completed, thermo checks passed", walltime: 6.0, walltime_norm: 1.0 } +in.ttm: { folder: examples/ttm, status: "completed, thermo checks passed", walltime: 17.0, walltime_norm: 2.8333333333333335 } +in.ttm.grid: { folder: examples/ttm, status: "completed, thermo checks passed", walltime: 17.0, walltime_norm: 2.8333333333333335 } +in.ttm.mod: { folder: examples/ttm, status: "completed, thermo checks passed", walltime: 6.0, walltime_norm: 1.0 } +in.colloid: { folder: examples/multi, status: "completed, 3 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } +in.granular: { folder: examples/multi, status: "completed, 3 rel thermo checks failed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.powerlaw: { folder: examples/multi, status: "completed, 3 rel thermo checks failed", walltime: 3.0, walltime_norm: 0.5 } +in.msst: { folder: examples/msst, status: "completed, thermo checks passed", walltime: 8.0, walltime_norm: 1.3333333333333333 } +in.gjf.vfull: { folder: examples/gjf, status: "completed, 3 rel thermo checks failed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.gjf.vhalf: { folder: examples/gjf, status: "completed, 3 rel thermo checks failed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.spin.cobalt_fcc: { folder: examples/SPIN/cobalt_fcc, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } +in.spin.nickel: { folder: examples/SPIN/nickel, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } +in.spin.nickel_cubic: { folder: examples/SPIN/nickel, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } +in.spin.cobalt_hcp: { folder: examples/SPIN/cobalt_hcp, status: "completed, 4 rel thermo checks failed", walltime: 3.0, walltime_norm: 0.5 } +in.spin.iron: { folder: examples/SPIN/iron, status: "completed, 4 rel thermo checks failed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.spin.iron_cubic: { folder: examples/SPIN/iron, status: "completed, 2 rel thermo checks failed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.gneb.skyrmion: { folder: examples/SPIN/gneb/skyrmion, status: "failed, ERROR: Did not assign all atoms correctly (src/read_data.cpp:1562).", walltime: -1 } +in.gneb.iron: { folder: examples/SPIN/gneb/iron, status: "failed, ERROR: Cannot use NEBSpin with a single replica (src/SPIN/neb_spin.cpp:133).", walltime: -1 } +in.spin.read_data: { folder: examples/SPIN/read_restart, status: "failed, no Total wall time in the output.", walltime: -1 } +in.spin.restart: { folder: examples/SPIN/read_restart, status: "failed, ERROR: Invalid flag in force field section of restart file (src/read_restart.cpp:948).", walltime: -1 } +in.spin.write_restart: { folder: examples/SPIN/read_restart, status: "completed, 2 rel thermo checks failed", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.spin.bfo_min: { folder: examples/SPIN/spinmin, status: "completed, thermo checks passed", walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.spin.bfo_min_cg: { folder: examples/SPIN/spinmin, status: "completed, thermo checks passed", walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.spin.bfo_min_lbfgs: { folder: examples/SPIN/spinmin, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.spin.iron_min: { folder: examples/SPIN/spinmin, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.spin.setforce: { folder: examples/SPIN/setforce_spin, status: "failed, no Total wall time in the output.", walltime: -1 } +in.spin.bfo: { folder: examples/SPIN/bfo, status: "completed, 2 rel thermo checks failed", walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.spin.iron_dipole_cut: { folder: examples/SPIN/dipole_spin, status: "completed, thermo checks passed", walltime: 9.0, walltime_norm: 1.5 } +in.spin.iron_dipole_ewald: { folder: examples/SPIN/dipole_spin, status: "completed, 1 abs thermo checks failed", walltime: 30.0, walltime_norm: 5.0 } +in.spin.iron_dipole_pppm: { folder: examples/SPIN/dipole_spin, status: "completed, thermo checks passed", walltime: 17.0, walltime_norm: 2.8333333333333335 } +in.spin.iron-nve: { folder: examples/SPIN/test_problems/validation_nve, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 32.0, walltime_norm: 5.333333333333333 } +in.spin.nvt_lattice: { folder: examples/SPIN/test_problems/validation_nvt, status: "failed, no Total wall time in the output.", walltime: -1 } +in.spin.nvt_spin: { folder: examples/SPIN/test_problems/validation_nvt, status: "failed, ERROR: Fix langevin period must be > 0.0 (src/fix_langevin.cpp:80).", walltime: -1 } +in.mliap.ace.compute: { folder: examples/mliap, status: "completed, but no Step nor Loop in the output.", walltime: 0.0, walltime_norm: 0.0 } +in.mliap.nn.Cu: { folder: examples/mliap, status: "completed, thermo checks passed", walltime: 6.0, walltime_norm: 1.0 } +in.mliap.nn.Ta06A: { folder: examples/mliap, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.mliap.pytorch.Ta06A: { folder: examples/mliap, status: "failed, ERROR: Using pair_style mliap model mliappy requires ML-IAP with python support (src/ML-IAP/pair_mliap.cpp:173).", walltime: -1 } +in.mliap.pytorch.ace: { folder: examples/mliap, status: "failed, ERROR: Using pair_style mliap model mliappy requires ML-IAP with python support (src/ML-IAP/pair_mliap.cpp:173).", walltime: -1 } +in.mliap.pytorch.ace.NN: { folder: examples/mliap, status: "failed, ERROR: Using pair_style mliap model mliappy requires ML-IAP with python support (src/ML-IAP/pair_mliap.cpp:173).", walltime: -1 } +in.mliap.pytorch.relu1hidden: { folder: examples/mliap, status: "failed, ERROR: Using pair_style mliap model mliappy requires ML-IAP with python support (src/ML-IAP/pair_mliap.cpp:173).", walltime: -1 } +in.mliap.quadratic.compute: { folder: examples/mliap, status: "failed, no Total wall time in the output.", walltime: -1 } +in.mliap.snap.Ta06A: { folder: examples/mliap, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.mliap.snap.WBe.PRB2019: { folder: examples/mliap, status: "completed, thermo checks passed", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.mliap.snap.chem: { folder: examples/mliap, status: "completed, thermo checks passed", walltime: 26.0, walltime_norm: 4.333333333333333 } +in.mliap.snap.compute: { folder: examples/mliap, status: "failed, no Total wall time in the output.", walltime: -1 } +in.mliap.snap.quadratic: { folder: examples/mliap, status: "completed, thermo checks passed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.mliap.so3.Ni_Mo: { folder: examples/mliap, status: "completed, but no Step nor Loop in the output.", walltime: 0.0, walltime_norm: 0.0 } +in.mliap.so3.nn.Si: { folder: examples/mliap, status: "completed, thermo checks passed", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.mliap.unified.lj.Ar: { folder: examples/mliap, status: "failed, ERROR: Could not process Python string: .", walltime: -1 } +in.run: { folder: examples/mliap/jax, status: "failed, ERROR: Using pair_style mliap unified requires ML-IAP with python support (src/ML-IAP/pair_mliap.cpp:213).", walltime: -1 } +in.eim: { folder: examples/eim, status: "completed, 2 rel thermo checks failed", walltime: 3.0, walltime_norm: 0.5 } +in.shear: { folder: examples/shear, status: "completed, thermo checks passed", walltime: 6.0, walltime_norm: 1.0 } +in.shear.void: { folder: examples/shear, status: "completed, thermo checks passed", walltime: 5.0, walltime_norm: 0.8333333333333334 } +in.aimd.alone: { folder: examples/mdi, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.aimd.driver: { folder: examples/mdi, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.aimd.driver.plugin: { folder: examples/mdi, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.aimd.engine: { folder: examples/mdi, status: "failed, unknown command, package not installed, ERROR: Unknown command: mdi engine (src/input.cpp:314)", walltime: -1 } +in.aimdpy.mm: { folder: examples/mdi, status: "failed, unknown command, package not installed, ERROR: Unknown command: mdi engine (src/input.cpp:314)", walltime: -1 } +in.aimdpy.qm: { folder: examples/mdi, status: "failed, unknown command, package not installed, ERROR: Unknown command: mdi engine (src/input.cpp:314)", walltime: -1 } +in.sequence.python: { folder: examples/mdi, status: "failed, unknown command, package not installed, ERROR: Unknown command: mdi engine (src/input.cpp:314)", walltime: -1 } +in.series.alone: { folder: examples/mdi, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.series.driver: { folder: examples/mdi, status: "failed, unknown command, package not installed, ERROR: Unknown command: mdi connect (src/input.cpp:314)", walltime: -1 } +in.series.driver.plugin: { folder: examples/mdi, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.series.engine: { folder: examples/mdi, status: "failed, unknown command, package not installed, ERROR: Unknown command: mdi engine (src/input.cpp:314)", walltime: -1 } +in.snapshot.alone: { folder: examples/mdi, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.snapshot.driver: { folder: examples/mdi, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.snapshot.driver.plugin: { folder: examples/mdi, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.snapshot.engine: { folder: examples/mdi, status: "failed, unknown command, package not installed, ERROR: Unknown command: mdi engine (src/input.cpp:314)", walltime: -1 } +in.lammps: { folder: examples/PACKAGES/dpd-smooth/2d-diffusion, status: "failed, no Total wall time in the output.", walltime: -1 } +in.lammps: { folder: examples/PACKAGES/dpd-smooth/2d-diffusion-in-shear-flow, status: "failed, no Total wall time in the output.", walltime: -1 } +in.lammps: { folder: examples/PACKAGES/dpd-smooth/equipartition-verification, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 49.0, walltime_norm: 8.166666666666666 } +in.fitpod: { folder: examples/PACKAGES/pod/InP, status: "failed, ERROR: Cannot fit potential without data files. The data paths may not be valid. Please check the data paths in the POD data file. (src/ML-POD/fitpod_command.cpp:718).", walltime: -1 } +in.pod: { folder: examples/PACKAGES/pod/InP, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } +in.fitpod: { folder: examples/PACKAGES/pod/Ta, status: "completed, but no Step nor Loop in the output.", walltime: 0.0, walltime_norm: 0.0 } +in.pod: { folder: examples/PACKAGES/pod/Ta, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.pod.compute: { folder: examples/PACKAGES/pod/Ta, status: "failed, ERROR: Per-atom data too large (src/ML-POD/compute_podd_atom.cpp:62).", walltime: -1 } +in.duplex2: { folder: examples/PACKAGES/cgdna/examples/real_units/oxRNA2/duplex2, status: "completed, 1 rel thermo checks failed", walltime: 19.0, walltime_norm: 3.1666666666666665 } +in.duplex2: { folder: examples/PACKAGES/cgdna/examples/real_units/oxRNA2/potential_file, status: "completed, 1 rel thermo checks failed", walltime: 19.0, walltime_norm: 3.1666666666666665 } +in.duplex2: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA/duplex2, status: "completed, thermo checks passed", walltime: 21.0, walltime_norm: 3.5 } +in.duplex1: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA/duplex1, status: "completed, thermo checks passed", walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.duplex1: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA/potential_file, status: "completed, thermo checks passed", walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.duplex2: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA2/duplex2, status: "completed, thermo checks passed", walltime: 24.0, walltime_norm: 4.0 } +in.duplex1: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA2/duplex1, status: "completed, thermo checks passed", walltime: 12.0, walltime_norm: 2.0 } +in.dsring: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA2/dsring, status: "completed, thermo checks passed", walltime: 46.0, walltime_norm: 7.666666666666667 } +in.duplex1: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA2/potential_file, status: "completed, thermo checks passed", walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.duplex4.4type: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA2/unique_bp, status: "completed, thermo checks passed", walltime: 50.0, walltime_norm: 8.333333333333334 } +in.duplex4.8type: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA2/unique_bp, status: "completed, thermo checks passed", walltime: 50.0, walltime_norm: 8.333333333333334 } +in.duplex3: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA2/duplex3, status: "completed, thermo checks passed", walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.duplex2: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxRNA2/duplex2, status: "completed, thermo checks passed", walltime: 19.0, walltime_norm: 3.1666666666666665 } +in.duplex2: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxRNA2/potential_file, status: "completed, thermo checks passed", walltime: 19.0, walltime_norm: 3.1666666666666665 } +in.duplex2: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA/duplex2, status: "completed, thermo checks passed", walltime: 22.0, walltime_norm: 3.6666666666666665 } +in.duplex1: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA/duplex1, status: "completed, thermo checks passed", walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.duplex1: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA/potential_file, status: "completed, thermo checks passed", walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.duplex2: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA2/duplex2, status: "completed, thermo checks passed", walltime: 24.0, walltime_norm: 4.0 } +in.duplex1: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA2/duplex1, status: "completed, thermo checks passed", walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.dsring: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA2/dsring, status: "completed, thermo checks passed", walltime: 46.0, walltime_norm: 7.666666666666667 } +in.duplex1: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA2/potential_file, status: "completed, thermo checks passed", walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.duplex4.4type: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA2/unique_bp, status: "completed, thermo checks passed", walltime: 50.0, walltime_norm: 8.333333333333334 } +in.duplex4.8type: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA2/unique_bp, status: "completed, thermo checks passed", walltime: 50.0, walltime_norm: 8.333333333333334 } +in.duplex3: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA2/duplex3, status: "completed, thermo checks passed", walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.temper_npt: { folder: examples/PACKAGES/temper_npt, status: "failed, ERROR: World variable count doesn't match # of partitions (src/variable.cpp:255).", walltime: -1 } +in.peptide-plumed: { folder: examples/PACKAGES/plumed, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'plumed' is part of the PLUMED package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.methanol: { folder: examples/PACKAGES/bocs, status: "completed, 4 rel thermo checks failed", walltime: 23.0, walltime_norm: 3.8333333333333335 } +in.pedone.melt: { folder: examples/PACKAGES/pedone, status: "completed, 2 rel thermo checks failed", walltime: 13.0, walltime_norm: 2.1666666666666665 } +in.pedone.relax: { folder: examples/PACKAGES/pedone, status: "completed, 2 rel thermo checks failed", walltime: 16.0, walltime_norm: 2.6666666666666665 } +in.methanol_implicit_water: { folder: examples/PACKAGES/local_density/methanol_implicit_water, status: "failed, no Total wall time in the output.", walltime: -1 } +in.benzene_water: { folder: examples/PACKAGES/local_density/benzene_water, status: "completed, but no Step nor Loop in the output.", walltime: 24.0, walltime_norm: 4.0 } +in.gauss-diel: { folder: examples/PACKAGES/gauss_diel, status: "completed, error parsing log.lammps into YAML", walltime: 8.0, walltime_norm: 1.3333333333333333 } +in.gauss-diel-cg: { folder: examples/PACKAGES/gauss_diel, status: "completed, error parsing log.lammps into YAML", walltime: 3.0, walltime_norm: 0.5 } +in.gauss-diel-split: { folder: examples/PACKAGES/gauss_diel, status: "completed, error parsing log.lammps into YAML", walltime: 3.0, walltime_norm: 0.5 } +in.alloy: { folder: examples/PACKAGES/alchemy, status: "failed, ERROR: World variable count doesn't match # of partitions (src/variable.cpp:255).", walltime: -1 } +in.twowater: { folder: examples/PACKAGES/alchemy, status: "failed, ERROR: World variable count doesn't match # of partitions (src/variable.cpp:255).", walltime: -1 } +in.sds-hybrid: { folder: examples/PACKAGES/cgspica/sds-monolayer, status: "completed, thermo checks passed", walltime: 6.0, walltime_norm: 1.0 } +in.sds-regular: { folder: examples/PACKAGES/cgspica/sds-monolayer, status: "completed, thermo checks passed", walltime: 6.0, walltime_norm: 1.0 } +in.pegc12e8: { folder: examples/PACKAGES/cgspica/peg-verlet, status: "failed, no Total wall time in the output.", walltime: -1 } +in.pegc12e8-angle: { folder: examples/PACKAGES/cgspica/peg-verlet, status: "failed, no Total wall time in the output.", walltime: -1 } +in.hkust1: { folder: examples/PACKAGES/mofff, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.hkust1_long: { folder: examples/PACKAGES/mofff, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.e3b-tip4p2005: { folder: examples/PACKAGES/e3b, status: "completed, thermo checks passed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.uf3.Nb: { folder: examples/PACKAGES/uf3, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.fep01.lmp: { folder: examples/PACKAGES/fep/CC-CO/fep01, status: "failed, no Total wall time in the output.", walltime: -1 } +in.fep10.lmp: { folder: examples/PACKAGES/fep/CC-CO/fep10, status: "failed, no Total wall time in the output.", walltime: -1 } +in.insertion: { folder: examples/PACKAGES/fep/C7inEthanol/fep01, status: "failed, no Total wall time in the output.", walltime: -1 } +in.deletion: { folder: examples/PACKAGES/fep/C7inEthanol/fep10, status: "failed, no Total wall time in the output.", walltime: -1 } +in.fep01.lmp: { folder: examples/PACKAGES/fep/CH4-CF4/fep01, status: "failed, no Total wall time in the output.", walltime: -1 } +in.fep10.lmp: { folder: examples/PACKAGES/fep/CH4-CF4/fep10, status: "failed, no Total wall time in the output.", walltime: -1 } +in.bar10.lmp: { folder: examples/PACKAGES/fep/CH4-CF4/bar10, status: "failed, no Total wall time in the output.", walltime: -1 } +in.bar01.lmp: { folder: examples/PACKAGES/fep/CH4-CF4/bar01, status: "failed, no Total wall time in the output.", walltime: -1 } +in.fep01.lmp: { folder: examples/PACKAGES/fep/CH4hyd/fep01, status: "failed, no Total wall time in the output.", walltime: -1 } +in.fdti01.lmp: { folder: examples/PACKAGES/fep/CH4hyd/fdti01, status: "failed, no Total wall time in the output.", walltime: -1 } +in.fep10.lmp: { folder: examples/PACKAGES/fep/CH4hyd/fep10, status: "failed, no Total wall time in the output.", walltime: -1 } +in.fdti10.lmp: { folder: examples/PACKAGES/fep/CH4hyd/fdti10, status: "failed, no Total wall time in the output.", walltime: -1 } +in.spce.lmp: { folder: examples/PACKAGES/fep/ta, status: "failed, no Total wall time in the output.", walltime: -1 } +in.gap: { folder: examples/PACKAGES/quip, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized pair style 'quip' is part of the ML-QUIP package which is not enabled in this LAMMPS binary. (src/force.cpp:275)", walltime: -1 } +in.molecular: { folder: examples/PACKAGES/quip, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized pair style 'quip' is part of the ML-QUIP package which is not enabled in this LAMMPS binary. (src/force.cpp:275)", walltime: -1 } +in.sw: { folder: examples/PACKAGES/quip, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized pair style 'quip' is part of the ML-QUIP package which is not enabled in this LAMMPS binary. (src/force.cpp:275)", walltime: -1 } +in.srp_react: { folder: examples/PACKAGES/srp_react, status: "failed, ERROR: Invalid bond type 0 for pair style srp (src/MISC/pair_srp.cpp:403).", walltime: -1 } +in.spce: { folder: examples/PACKAGES/manybody_table, status: "completed, 3 rel thermo checks failed", walltime: 9.0, walltime_norm: 1.5 } +in.spce2: { folder: examples/PACKAGES/manybody_table, status: "completed, 3 rel thermo checks failed", walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.spce_sw: { folder: examples/PACKAGES/manybody_table, status: "completed, 1 rel thermo checks failed", walltime: 9.0, walltime_norm: 1.5 } +in.confined: { folder: examples/PACKAGES/dielectric, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.nopbc: { folder: examples/PACKAGES/dielectric, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.methane_qtb: { folder: examples/PACKAGES/qtb/methane_qtb, status: "completed, thermo checks passed", walltime: 19.0, walltime_norm: 3.1666666666666665 } +in.alpha_quartz_qtb: { folder: examples/PACKAGES/qtb/alpha_quartz_qtb, status: "completed, 2 rel thermo checks failed", walltime: 27.0, walltime_norm: 4.5 } +in.alpha_quartz_qbmsst: { folder: examples/PACKAGES/qtb/alpha_quartz_qbmsst, status: "failed, no Total wall time in the output.", walltime: -1 } +in.methane_qbmsst: { folder: examples/PACKAGES/qtb/methane_qbmsst, status: "failed, no Total wall time in the output.", walltime: -1 } +in.tmd: { folder: examples/PACKAGES/tmd, status: "completed, error parsing log.lammps into YAML", walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.meam-spline.Si: { folder: examples/PACKAGES/meam_spline, status: "completed, thermo checks passed", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.meam-spline.TiO2: { folder: examples/PACKAGES/meam_spline, status: "failed, no Total wall time in the output.", walltime: -1 } +in.silicon: { folder: examples/PACKAGES/phonon/dynamical_matrix_command/Silicon, status: "completed, but no Step nor Loop in the output.", walltime: 0.0, walltime_norm: 0.0 } +in.EAM3D: { folder: examples/PACKAGES/phonon/3-3D-FCC-Cu-EAM, status: "failed, no Total wall time in the output.", walltime: -1 } +in.disp: { folder: examples/PACKAGES/phonon/3-3D-FCC-Cu-EAM, status: "failed, unknown command, package not installed, ERROR: Unknown command: 1 (src/input.cpp:314)", walltime: -1 } +in.disp2: { folder: examples/PACKAGES/phonon/3-3D-FCC-Cu-EAM, status: "failed, unknown command, package not installed, ERROR: Unknown command: 1 (src/input.cpp:314)", walltime: -1 } +in.dos: { folder: examples/PACKAGES/phonon/3-3D-FCC-Cu-EAM, status: "skipped", walltime: -2 } +in.Ana: { folder: examples/PACKAGES/phonon/1-1D-mono, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 9.0, walltime_norm: 1.5 } +in.disp: { folder: examples/PACKAGES/phonon/1-1D-mono, status: "failed, unknown command, package not installed, ERROR: Unknown command: 1 (src/input.cpp:314)", walltime: -1 } +in.Ana: { folder: examples/PACKAGES/phonon/2-1D-diatomic, status: "completed, thermo checks passed", walltime: 24.0, walltime_norm: 4.0 } +in.disp: { folder: examples/PACKAGES/phonon/2-1D-diatomic, status: "failed, unknown command, package not installed, ERROR: Unknown command: 10 (src/input.cpp:314)", walltime: -1 } +in.disp: { folder: examples/PACKAGES/phonon/4-Graphene, status: "failed, unknown command, package not installed, ERROR: Unknown command: 100 (src/input.cpp:314)", walltime: -1 } +in.graphene: { folder: examples/PACKAGES/phonon/4-Graphene, status: "failed, no Total wall time in the output.", walltime: -1 } +in.dpde-shardlow: { folder: examples/PACKAGES/dpd-react/dpde-shardlow, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.dpde-vv: { folder: examples/PACKAGES/dpd-react/dpde-vv, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 18.0, walltime_norm: 3.0 } +in.dpd-shardlow: { folder: examples/PACKAGES/dpd-react/dpd-shardlow, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.dpd-vv: { folder: examples/PACKAGES/dpd-react/dpd-vv, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 14.0, walltime_norm: 2.3333333333333335 } +in.dpdp-shardlow: { folder: examples/PACKAGES/dpd-react/dpdp-shardlow, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.multi-lucy: { folder: examples/PACKAGES/dpd-react/multi-lucy, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.dpdh-shardlow: { folder: examples/PACKAGES/dpd-react/dpdh-shardlow, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.dpdrx-shardlow: { folder: examples/PACKAGES/dpd-react/dpdrx-shardlow, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.GD: { folder: examples/PACKAGES/flow_gauss, status: "completed, thermo checks passed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.basal: { folder: examples/PACKAGES/basal, status: "failed, no Total wall time in the output.", walltime: -1 } +in.cascade_AlCu: { folder: examples/PACKAGES/electron_stopping, status: "failed, ERROR: Must set 'extscalar' when setting 'scalar_flag' for fix electron/stopping/fit. Contact the developer. (src/fix.cpp:135).", walltime: -1 } +in.cascade_SiSi: { folder: examples/PACKAGES/electron_stopping, status: "failed, ERROR: Must set 'extscalar' when setting 'scalar_flag' for fix electron/stopping/fit. Contact the developer. (src/fix.cpp:135).", walltime: -1 } +in.elstop: { folder: examples/PACKAGES/electron_stopping, status: "completed, thermo checks passed", walltime: 33.0, walltime_norm: 5.5 } +in.elstop.only: { folder: examples/PACKAGES/electron_stopping, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } +in.chreg-acid: { folder: examples/PACKAGES/charge_regulation, status: "completed, 1 rel thermo checks failed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.chreg-acid-real: { folder: examples/PACKAGES/charge_regulation, status: "completed, 1 rel thermo checks failed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.chreg-polymer: { folder: examples/PACKAGES/charge_regulation, status: "completed, 1 rel thermo checks failed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.adatom: { folder: examples/PACKAGES/agni, status: "completed, thermo checks passed", walltime: 27.0, walltime_norm: 4.5 } +in.vacancy: { folder: examples/PACKAGES/agni, status: "completed, thermo checks passed", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.bucky-plus-cnt: { folder: examples/PACKAGES/imd, status: "skipped", walltime: -2 } +in.bucky-plus-cnt-gpu: { folder: examples/PACKAGES/imd, status: "skipped", walltime: -2 } +in.deca-ala-solv-filter_imd: { folder: examples/PACKAGES/imd, status: "skipped", walltime: -2 } +in.deca-ala-solv_imd: { folder: examples/PACKAGES/imd, status: "skipped", walltime: -2 } +in.deca-ala_imd: { folder: examples/PACKAGES/imd, status: "skipped", walltime: -2 } +in.deca-ala_imd-gpu: { folder: examples/PACKAGES/imd, status: "skipped", walltime: -2 } +in.melt_imd: { folder: examples/PACKAGES/imd, status: "skipped", walltime: -2 } +in.melt_imd-gpu: { folder: examples/PACKAGES/imd, status: "skipped", walltime: -2 } +in.first: { folder: examples/PACKAGES/adios/rerun, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized dump style 'custom/adios' is part of the ADIOS package which is not enabled in this LAMMPS binary. (src/output.cpp:776)", walltime: -1 } +in.read_dump: { folder: examples/PACKAGES/adios/rerun, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized reader style 'adios' is part of the ADIOS package which is not enabled in this LAMMPS binary. (src/read_dump.cpp:236)", walltime: -1 } +in.rerun: { folder: examples/PACKAGES/adios/rerun, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized reader style 'adios' is part of the ADIOS package which is not enabled in this LAMMPS binary. (src/read_dump.cpp:236)", walltime: -1 } +in.adios_balance: { folder: examples/PACKAGES/adios/balance, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized dump style 'custom/adios' is part of the ADIOS package which is not enabled in this LAMMPS binary. (src/output.cpp:776)", walltime: -1 } +in.adios_balance2: { folder: examples/PACKAGES/adios/balance, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized reader style 'adios' is part of the ADIOS package which is not enabled in this LAMMPS binary. (src/read_dump.cpp:236)", walltime: -1 } +in.bcc0: { folder: examples/PACKAGES/mgpt, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized pair style 'mgpt' is part of the MGPT package which is not enabled in this LAMMPS binary. (src/force.cpp:275)", walltime: -1 } +in.vac0-bcc: { folder: examples/PACKAGES/mgpt, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized pair style 'mgpt' is part of the MGPT package which is not enabled in this LAMMPS binary. (src/force.cpp:275)", walltime: -1 } +in.vacmin-bcc: { folder: examples/PACKAGES/mgpt, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized pair style 'mgpt' is part of the MGPT package which is not enabled in this LAMMPS binary. (src/force.cpp:275)", walltime: -1 } +in.vtk: { folder: examples/PACKAGES/vtk, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized dump style 'vtk' is part of the VTK package which is not enabled in this LAMMPS binary. (src/output.cpp:776)", walltime: -1 } +in.vtp: { folder: examples/PACKAGES/vtk, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized dump style 'vtk' is part of the VTK package which is not enabled in this LAMMPS binary. (src/output.cpp:776)", walltime: -1 } +in.dpdext: { folder: examples/PACKAGES/dpd-basic/dpdext, status: "completed, 2 rel thermo checks failed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.dpd: { folder: examples/PACKAGES/dpd-basic/dpd, status: "completed, 2 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } +in.dpdext_tstat: { folder: examples/PACKAGES/dpd-basic/dpdext_tstat, status: "completed, thermo checks passed", walltime: 30.0, walltime_norm: 5.0 } +in.dpd_tstat: { folder: examples/PACKAGES/dpd-basic/dpd_tstat, status: "completed, thermo checks passed", walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.dpd_coul_slater_long: { folder: examples/PACKAGES/dpd-basic/dpd_coul_slater_long, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.piston: { folder: examples/PACKAGES/electrode/piston, status: "failed, no Total wall time in the output.", walltime: -1 } +in.cg: { folder: examples/PACKAGES/electrode/madelung, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.eta: { folder: examples/PACKAGES/electrode/madelung, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.eta_cg: { folder: examples/PACKAGES/electrode/madelung, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.eta_mix: { folder: examples/PACKAGES/electrode/madelung, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.ewald-ew2d: { folder: examples/PACKAGES/electrode/madelung, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.ewald-ew3dc: { folder: examples/PACKAGES/electrode/madelung, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.ewald-ffield: { folder: examples/PACKAGES/electrode/madelung, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.pppm-ew3dc: { folder: examples/PACKAGES/electrode/madelung, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.pppm-ffield: { folder: examples/PACKAGES/electrode/madelung, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.ffield: { folder: examples/PACKAGES/electrode/au-aq, status: "failed, no Total wall time in the output.", walltime: -1 } +in.tf: { folder: examples/PACKAGES/electrode/au-aq, status: "failed, no Total wall time in the output.", walltime: -1 } +in.conp: { folder: examples/PACKAGES/electrode/graph-il, status: "failed, no Total wall time in the output.", walltime: -1 } +in.conq: { folder: examples/PACKAGES/electrode/graph-il, status: "failed, no Total wall time in the output.", walltime: -1 } +in.conq2: { folder: examples/PACKAGES/electrode/graph-il, status: "failed, no Total wall time in the output.", walltime: -1 } +in.etypes: { folder: examples/PACKAGES/electrode/graph-il, status: "failed, no Total wall time in the output.", walltime: -1 } +in.ffield: { folder: examples/PACKAGES/electrode/graph-il, status: "failed, no Total wall time in the output.", walltime: -1 } +in.ramp: { folder: examples/PACKAGES/electrode/graph-il, status: "failed, no Total wall time in the output.", walltime: -1 } +in.thermo: { folder: examples/PACKAGES/electrode/graph-il, status: "failed, no Total wall time in the output.", walltime: -1 } +in.planar-ewald-ew2d: { folder: examples/PACKAGES/electrode/planar, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.planar-ewald-ew3dc: { folder: examples/PACKAGES/electrode/planar, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.planar-ewald-ffield: { folder: examples/PACKAGES/electrode/planar, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.planar-pppm-ew3dc: { folder: examples/PACKAGES/electrode/planar, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } +in.planar-pppm-ffield: { folder: examples/PACKAGES/electrode/planar, status: "completed, thermo checks passed", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.convective_pulse: { folder: examples/PACKAGES/atc/drift_diffusion, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.ddm_schrodinger: { folder: examples/PACKAGES/atc/drift_diffusion, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.finite_well: { folder: examples/PACKAGES/atc/drift_diffusion, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.no_atoms_ddm: { folder: examples/PACKAGES/atc/drift_diffusion, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.null_material_ddm: { folder: examples/PACKAGES/atc/drift_diffusion, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.poisson1d_noatoms: { folder: examples/PACKAGES/atc/drift_diffusion, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.poisson2d_noatoms: { folder: examples/PACKAGES/atc/drift_diffusion, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.schrodinger-poisson2d_Jconstraint: { folder: examples/PACKAGES/atc/drift_diffusion, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.schrodinger-poisson2d_convective: { folder: examples/PACKAGES/atc/drift_diffusion, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.schrodinger-poisson2d_noatoms: { folder: examples/PACKAGES/atc/drift_diffusion, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.cb_biaxial: { folder: examples/PACKAGES/atc/cauchy_born, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.cb_shear: { folder: examples/PACKAGES/atc/cauchy_born, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.cb_unistrain: { folder: examples/PACKAGES/atc/cauchy_born, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.cb_unistrain_eam: { folder: examples/PACKAGES/atc/cauchy_born, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.cb_unistrain_eam_linear: { folder: examples/PACKAGES/atc/cauchy_born, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.cb_unistrain_linear: { folder: examples/PACKAGES/atc/cauchy_born, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.cb_volumetric: { folder: examples/PACKAGES/atc/cauchy_born, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.flying_cube: { folder: examples/PACKAGES/atc/cauchy_born, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.ftcb_constV: { folder: examples/PACKAGES/atc/cauchy_born, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.read_xref: { folder: examples/PACKAGES/atc/cauchy_born, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.consistency: { folder: examples/PACKAGES/atc/hardy, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.eam_kernel_convergence: { folder: examples/PACKAGES/atc/hardy, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.eam_unistrain_cell: { folder: examples/PACKAGES/atc/hardy, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.eam_unistrain_mesh: { folder: examples/PACKAGES/atc/hardy, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.eam_unistrain_qcylinder: { folder: examples/PACKAGES/atc/hardy, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.eam_unistrain_qsphere: { folder: examples/PACKAGES/atc/hardy, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.eam_unistrain_step: { folder: examples/PACKAGES/atc/hardy, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.eam_volume_stretch: { folder: examples/PACKAGES/atc/hardy, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.eshelby_static: { folder: examples/PACKAGES/atc/hardy, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.nvt: { folder: examples/PACKAGES/atc/hardy, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.bar1d_fluids: { folder: examples/PACKAGES/atc/fluids, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.concentration: { folder: examples/PACKAGES/atc/fluids, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.conducting_interface: { folder: examples/PACKAGES/atc/fluids, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.dielectric_interface: { folder: examples/PACKAGES/atc/fluids, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.double_layer: { folder: examples/PACKAGES/atc/fluids, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.liquid_electrostatic: { folder: examples/PACKAGES/atc/fluids, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.opp_force: { folder: examples/PACKAGES/atc/fluids, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.poisson: { folder: examples/PACKAGES/atc/fluids, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.shear_flow: { folder: examples/PACKAGES/atc/fluids, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.shear_no_atoms: { folder: examples/PACKAGES/atc/fluids, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.harmonic_bonds: { folder: examples/PACKAGES/atc/molecule, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.polarize: { folder: examples/PACKAGES/atc/molecule, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.quartic_bonds: { folder: examples/PACKAGES/atc/molecule, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.water: { folder: examples/PACKAGES/atc/molecule, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.bar1d: { folder: examples/PACKAGES/atc/thermal, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.bar1d_all_atoms: { folder: examples/PACKAGES/atc/thermal, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.bar1d_combined: { folder: examples/PACKAGES/atc/thermal, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.bar1d_flux: { folder: examples/PACKAGES/atc/thermal, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.bar1d_frac_step: { folder: examples/PACKAGES/atc/thermal, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.bar1d_hoover: { folder: examples/PACKAGES/atc/thermal, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.bar1d_interpolate: { folder: examples/PACKAGES/atc/thermal, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.bar1d_lumped: { folder: examples/PACKAGES/atc/thermal, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.no_atoms: { folder: examples/PACKAGES/atc/thermal, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.gaussianIC1d_hex: { folder: examples/PACKAGES/atc/mesh, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.gaussianIC2d_hex: { folder: examples/PACKAGES/atc/mesh, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.gaussianIC2d_hex20_uniform: { folder: examples/PACKAGES/atc/mesh, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.gaussianIC2d_hex27_uniform: { folder: examples/PACKAGES/atc/mesh, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.gaussianIC2d_tet: { folder: examples/PACKAGES/atc/mesh, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.kernel2d_hex: { folder: examples/PACKAGES/atc/mesh, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.kernel2d_tet: { folder: examples/PACKAGES/atc/mesh, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.mesh2d_tet: { folder: examples/PACKAGES/atc/mesh, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.semicircle: { folder: examples/PACKAGES/atc/mesh, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.bar1d: { folder: examples/PACKAGES/atc/elastic, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.bar1d_damped: { folder: examples/PACKAGES/atc/elastic, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.bar1d_flux: { folder: examples/PACKAGES/atc/elastic, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.bar1d_frac_step: { folder: examples/PACKAGES/atc/elastic, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.bar1d_ghost_flux: { folder: examples/PACKAGES/atc/elastic, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.bar1d_thermo_elastic: { folder: examples/PACKAGES/atc/elastic, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.eam_energy: { folder: examples/PACKAGES/atc/elastic, status: "failed, no Total wall time in the output.", walltime: -1 } +in.electron_density: { folder: examples/PACKAGES/atc/elastic, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.no_atoms: { folder: examples/PACKAGES/atc/elastic, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.no_atoms_cb: { folder: examples/PACKAGES/atc/elastic, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.no_atoms_cb_linear: { folder: examples/PACKAGES/atc/elastic, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.bar1d_ttm: { folder: examples/PACKAGES/atc/two_temperature, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.cutout: { folder: examples/PACKAGES/atc/two_temperature, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.gaussianIC_ttm: { folder: examples/PACKAGES/atc/two_temperature, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.no_atoms: { folder: examples/PACKAGES/atc/two_temperature, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.restart: { folder: examples/PACKAGES/atc/two_temperature, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.uniform_exchange: { folder: examples/PACKAGES/atc/two_temperature, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.uniform_heating: { folder: examples/PACKAGES/atc/two_temperature, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.pits: { folder: examples/PACKAGES/latboltz/pit_geometry, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'lb/fluid' is part of the LATBOLTZ package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.polymer: { folder: examples/PACKAGES/latboltz/polymer, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'lb/fluid' is part of the LATBOLTZ package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.confined_colloids: { folder: examples/PACKAGES/latboltz/confined_colloid, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'lb/fluid' is part of the LATBOLTZ package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.trapnewsphere: { folder: examples/PACKAGES/latboltz/diffusingsphere, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'lb/fluid' is part of the LATBOLTZ package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.translocation: { folder: examples/PACKAGES/latboltz/translocation, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'lb/fluid' is part of the LATBOLTZ package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.toycar: { folder: examples/PACKAGES/latboltz/toycar, status: "failed, no Total wall time in the output.", walltime: -1 } +in.microrheology: { folder: examples/PACKAGES/latboltz/microrheology, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'lb/fluid' is part of the LATBOLTZ package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.dragtest: { folder: examples/PACKAGES/latboltz/dragforce, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'lb/fluid' is part of the LATBOLTZ package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.planewall: { folder: examples/PACKAGES/latboltz/planewall, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'lb/fluid' is part of the LATBOLTZ package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.compute: { folder: examples/PACKAGES/pace/compute, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.pace.product: { folder: examples/PACKAGES/pace, status: "completed, thermo checks passed", walltime: 16.0, walltime_norm: 2.6666666666666665 } +in.pace.recursive: { folder: examples/PACKAGES/pace, status: "completed, thermo checks passed", walltime: 12.0, walltime_norm: 2.0 } +in.addtorque: { folder: examples/PACKAGES/addtorque, status: "failed, no Total wall time in the output.", walltime: -1 } +in.cnp: { folder: examples/PACKAGES/cnp, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 29.0, walltime_norm: 4.833333333333333 } +in.CH4fc.ang: { folder: examples/PACKAGES/eff/fixed-core/CH4, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 5.0, walltime_norm: 0.8333333333333334 } +in.CH4fc.bohr: { folder: examples/PACKAGES/eff/fixed-core/CH4, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.CH4fc.spe.ang: { folder: examples/PACKAGES/eff/fixed-core/CH4, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.CH4fc.spe.bohr: { folder: examples/PACKAGES/eff/fixed-core/CH4, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.C2H6fc.ang: { folder: examples/PACKAGES/eff/fixed-core/C2H6, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.C2H6fc.bohr: { folder: examples/PACKAGES/eff/fixed-core/C2H6, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.ch4.dynamics: { folder: examples/PACKAGES/eff/CH4, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.ch4.min: { folder: examples/PACKAGES/eff/CH4, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.ch4_ionized.dynamics: { folder: examples/PACKAGES/eff/CH4, status: "completed, thermo checks passed", walltime: 16.0, walltime_norm: 2.6666666666666665 } +in.Be-solid.spe: { folder: examples/PACKAGES/eff/Be-solid, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 14.0, walltime_norm: 2.3333333333333335 } +in.adamantane_ionized.nve: { folder: examples/PACKAGES/eff/Auger-Adamantane, status: "failed, ERROR: Lost atoms: original 101 current 100 (src/thermo.cpp:494).", walltime: -1 } +in.SiH4: { folder: examples/PACKAGES/eff/ECP/SiH4, status: "completed, error parsing log.lammps into YAML", walltime: 0.0, walltime_norm: 0.0 } +in.SiH4.ang: { folder: examples/PACKAGES/eff/ECP/SiH4, status: "completed, error parsing log.lammps into YAML", walltime: 0.0, walltime_norm: 0.0 } +in.Si2H6: { folder: examples/PACKAGES/eff/ECP/Si2H6, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.Si2H6.ang: { folder: examples/PACKAGES/eff/ECP/Si2H6, status: "completed, thermo checks passed", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.SiC: { folder: examples/PACKAGES/eff/ECP/SiC/bulk, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 31.0, walltime_norm: 5.166666666666667 } +in.h2bulk.npt: { folder: examples/PACKAGES/eff/H_plasma, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 47.0, walltime_norm: 7.833333333333333 } +in.h2bulk.nve: { folder: examples/PACKAGES/eff/H_plasma, status: "failed, no Total wall time in the output.", walltime: -1 } +in.h2bulk.nve.ang: { folder: examples/PACKAGES/eff/H_plasma, status: "failed, no Total wall time in the output.", walltime: -1 } +in.Li-dendritic.min: { folder: examples/PACKAGES/eff/Li-dendritic, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 35.0, walltime_norm: 5.833333333333333 } +in.Li-dendritic.nvt: { folder: examples/PACKAGES/eff/Li-dendritic, status: "failed, no Total wall time in the output.", walltime: -1 } +in.Li.ang: { folder: examples/PACKAGES/eff/Li-solid, status: "failed, no Total wall time in the output.", walltime: -1 } +in.Li.bohr: { folder: examples/PACKAGES/eff/Li-solid, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 53.0, walltime_norm: 8.833333333333334 } +in.h2: { folder: examples/PACKAGES/eff/H2, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.h_atom.spe.ang: { folder: examples/PACKAGES/eff/H, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.h_atom.spe.bohr: { folder: examples/PACKAGES/eff/H, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.npt_biaxial: { folder: examples/PACKAGES/uef/npt_biaxial, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } +in.nvt_uniaxial: { folder: examples/PACKAGES/uef/nvt_uniaxial, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } +in.crystal: { folder: examples/PACKAGES/rhok, status: "completed, 2 rel thermo checks failed", walltime: 24.0, walltime_norm: 4.0 } +in.pinning: { folder: examples/PACKAGES/rhok, status: "failed, ERROR: Cannot open file data.halfhalf: No such file or directory (src/read_data.cpp:367).", walltime: -1 } +in.setup: { folder: examples/PACKAGES/rhok, status: "completed, 2 rel thermo checks failed", walltime: 17.0, walltime_norm: 2.8333333333333335 } +in.peptide-colvars: { folder: examples/PACKAGES/colvars, status: "completed, error parsing log.lammps into YAML", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.peptide-colvars2: { folder: examples/PACKAGES/colvars, status: "completed, error parsing log.lammps into YAML", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.peptide-spring: { folder: examples/PACKAGES/colvars, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.peptide-spring2: { folder: examples/PACKAGES/colvars, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.hdnnp: { folder: examples/PACKAGES/hdnnp, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized pair style 'hdnnp' is part of the ML-HDNNP package which is not enabled in this LAMMPS binary. (src/force.cpp:275)", walltime: -1 } +in.hybrid: { folder: examples/PACKAGES/hdnnp, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized pair style 'hdnnp' is part of the ML-HDNNP package which is not enabled in this LAMMPS binary. (src/force.cpp:275)", walltime: -1 } +in.edip-Si: { folder: examples/PACKAGES/edip, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.edip-Si-multi: { folder: examples/PACKAGES/edip, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.edip-SiC: { folder: examples/PACKAGES/edip, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.large_nylon_melt: { folder: examples/PACKAGES/reaction/nylon,6-6_melt, status: "failed, no Total wall time in the output.", walltime: -1 } +in.tiny_polystyrene.stabilized: { folder: examples/PACKAGES/reaction/tiny_polystyrene, status: "completed, 2 rel thermo checks failed", walltime: 18.0, walltime_norm: 3.0 } +in.tiny_epoxy.stabilized: { folder: examples/PACKAGES/reaction/tiny_epoxy, status: "completed, 1 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } +in.grow_styrene: { folder: examples/PACKAGES/reaction/create_atoms_polystyrene, status: "completed, 2 rel thermo checks failed", walltime: 7.0, walltime_norm: 1.1666666666666667 } +in.tiny_nylon.stabilized: { folder: examples/PACKAGES/reaction/tiny_nylon, status: "failed, unknown command, package not installed, ERROR: Unknown command: react rxn2 all 1 0.0 5.0 mol3 mol4 rxn1_stp2_map rescale_charges yes (src/input.cpp:314)", walltime: -1 } +in.tiny_nylon.stabilized_variable_probability: { folder: examples/PACKAGES/reaction/tiny_nylon, status: "completed, 2 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } +in.tiny_nylon.unstabilized: { folder: examples/PACKAGES/reaction/tiny_nylon, status: "completed, 2 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } +in.BulkNi: { folder: examples/PACKAGES/diffraction, status: "failed, no Total wall time in the output.", walltime: -1 } +in.tdpd: { folder: examples/PACKAGES/dpd-meso/tdpd, status: "failed, no Total wall time in the output.", walltime: -1 } +in.tdpd-region: { folder: examples/PACKAGES/dpd-meso/tdpd, status: "failed, no Total wall time in the output.", walltime: -1 } +in.mdpd: { folder: examples/PACKAGES/dpd-meso/mdpd, status: "failed, no Total wall time in the output.", walltime: -1 } +in.edpd: { folder: examples/PACKAGES/dpd-meso/edpd, status: "failed, no Total wall time in the output.", walltime: -1 } +in.edpd-region: { folder: examples/PACKAGES/dpd-meso/edpd, status: "failed, no Total wall time in the output.", walltime: -1 } +in.cylinder: { folder: examples/PACKAGES/stressprofile, status: "completed, thermo checks passed", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.flat: { folder: examples/PACKAGES/stressprofile, status: "failed, ERROR: Illegal compute stress/cartesian command: missing argument(s) (src/EXTRA-COMPUTE/compute_stress_cartesian.cpp:65).", walltime: -1 } +in.sphere: { folder: examples/PACKAGES/stressprofile, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.srp: { folder: examples/PACKAGES/srp, status: "completed, thermo checks passed", walltime: 6.0, walltime_norm: 1.0 } +in.scafacos: { folder: examples/PACKAGES/scafacos, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized kspace style 'scafacos' is part of the SCAFACOS package which is not enabled in this LAMMPS binary. (src/force.cpp:660)", walltime: -1 } +in.scafacos.cw.ewald: { folder: examples/PACKAGES/scafacos, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized kspace style 'scafacos' is part of the SCAFACOS package which is not enabled in this LAMMPS binary. (src/force.cpp:660)", walltime: -1 } +in.scafacos.cw.fmm: { folder: examples/PACKAGES/scafacos, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized kspace style 'scafacos' is part of the SCAFACOS package which is not enabled in this LAMMPS binary. (src/force.cpp:660)", walltime: -1 } +in.scafacos.cw.p2nfft: { folder: examples/PACKAGES/scafacos, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized kspace style 'scafacos' is part of the SCAFACOS package which is not enabled in this LAMMPS binary. (src/force.cpp:660)", walltime: -1 } +in.scafacos.cw.p3m: { folder: examples/PACKAGES/scafacos, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized kspace style 'scafacos' is part of the SCAFACOS package which is not enabled in this LAMMPS binary. (src/force.cpp:660)", walltime: -1 } +in.scafacos.ewald: { folder: examples/PACKAGES/scafacos, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized kspace style 'scafacos' is part of the SCAFACOS package which is not enabled in this LAMMPS binary. (src/force.cpp:660)", walltime: -1 } +in.scafacos.fmm: { folder: examples/PACKAGES/scafacos, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized kspace style 'scafacos' is part of the SCAFACOS package which is not enabled in this LAMMPS binary. (src/force.cpp:660)", walltime: -1 } +in.scafacos.hsph.direct: { folder: examples/PACKAGES/scafacos, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized kspace style 'scafacos' is part of the SCAFACOS package which is not enabled in this LAMMPS binary. (src/force.cpp:660)", walltime: -1 } +in.scafacos.hsph.fmm: { folder: examples/PACKAGES/scafacos, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized kspace style 'scafacos' is part of the SCAFACOS package which is not enabled in this LAMMPS binary. (src/force.cpp:660)", walltime: -1 } +in.scafacos.hsph.p2nfft: { folder: examples/PACKAGES/scafacos, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized kspace style 'scafacos' is part of the SCAFACOS package which is not enabled in this LAMMPS binary. (src/force.cpp:660)", walltime: -1 } +in.scafacos.p2nfft: { folder: examples/PACKAGES/scafacos, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized kspace style 'scafacos' is part of the SCAFACOS package which is not enabled in this LAMMPS binary. (src/force.cpp:660)", walltime: -1 } +in.scafacos.p3m: { folder: examples/PACKAGES/scafacos, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized kspace style 'scafacos' is part of the SCAFACOS package which is not enabled in this LAMMPS binary. (src/force.cpp:660)", walltime: -1 } +in.h_atom: { folder: examples/PACKAGES/awpmd, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized atom style 'wavepacket' is part of the AWPMD package which is not enabled in this LAMMPS binary. (src/atom.cpp:745)", walltime: -1 } +in.h_molecule: { folder: examples/PACKAGES/awpmd, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized atom style 'wavepacket' is part of the AWPMD package which is not enabled in this LAMMPS binary. (src/atom.cpp:745)", walltime: -1 } +in.gold_gr: { folder: examples/PACKAGES/interlayer/saip_metal, status: "completed, thermo checks passed", walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.atom-diffusion: { folder: examples/PACKAGES/interlayer/kolmogorov_crespi_z, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.bilayer-graphene: { folder: examples/PACKAGES/interlayer/kolmogorov_crespi_z, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.gr_water: { folder: examples/PACKAGES/interlayer/aip_water_2dm, status: "completed, 2 rel thermo checks failed", walltime: 19.0, walltime_norm: 3.1666666666666665 } +in.gr_water.opt: { folder: examples/PACKAGES/interlayer/aip_water_2dm, status: "completed, 2 rel thermo checks failed", walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.bilayer-graphene: { folder: examples/PACKAGES/interlayer/kolmogorov_crespi_full, status: "failed, no Total wall time in the output.", walltime: -1 } +in.CH_drip: { folder: examples/PACKAGES/interlayer/drip, status: "completed, 1 rel thermo checks failed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.C_drip: { folder: examples/PACKAGES/interlayer/drip, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.mos2: { folder: examples/PACKAGES/interlayer/ilp_tmds, status: "completed, 1 rel thermo checks failed", walltime: 56.0, walltime_norm: 9.333333333333334 } +in.bilayer-graphene: { folder: examples/PACKAGES/interlayer/ilp_graphene_hbn, status: "failed, no Total wall time in the output.", walltime: -1 } +in.bilayer-hBN: { folder: examples/PACKAGES/interlayer/ilp_graphene_hbn, status: "failed, no Total wall time in the output.", walltime: -1 } +in.grhBN: { folder: examples/PACKAGES/interlayer/ilp_graphene_hbn, status: "failed, no Total wall time in the output.", walltime: -1 } +in.ilp_graphene_hbn: { folder: examples/PACKAGES/interlayer/ilp_graphene_hbn, status: "failed, no Total wall time in the output.", walltime: -1 } +in.smatbAgCuPancake: { folder: examples/PACKAGES/smtbq, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.smatbBulkFCC: { folder: examples/PACKAGES/smtbq, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.smtbq.Al: { folder: examples/PACKAGES/smtbq, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 3.0, walltime_norm: 0.5 } +in.smtbq.Al2O3: { folder: examples/PACKAGES/smtbq, status: "failed, no Total wall time in the output.", walltime: -1 } +in.smtbq.TiO2: { folder: examples/PACKAGES/smtbq, status: "failed, no Total wall time in the output.", walltime: -1 } +in.slater: { folder: examples/PACKAGES/slater, status: "failed, no Total wall time in the output.", walltime: -1 } +in.slcsa: { folder: examples/PACKAGES/sna_nnn_slcsa, status: "completed, error parsing log.lammps into YAML", walltime: 42.0, walltime_norm: 7.0 } +in.orient_eco: { folder: examples/PACKAGES/orient_eco, status: "failed, no Total wall time in the output.", walltime: -1 } +in.entropy: { folder: examples/PACKAGES/entropy, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 13.0, walltime_norm: 2.1666666666666665 } +in.bpti: { folder: examples/PACKAGES/filter_corotate, status: "completed, error parsing log.lammps into YAML", walltime: 24.0, walltime_norm: 4.0 } +in.peptide: { folder: examples/PACKAGES/filter_corotate, status: "failed, no Total wall time in the output.", walltime: -1 } +in.graphene: { folder: examples/PACKAGES/ipi, status: "failed, no Total wall time in the output.", walltime: -1 } +in.gREM-npt: { folder: examples/PACKAGES/grem/lj-single, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.gREM-nvt: { folder: examples/PACKAGES/grem/lj-single, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.gREM: { folder: examples/PACKAGES/grem/lj-6rep, status: "failed, ERROR: Cannot open file restart_file: No such file or directory (src/read_data.cpp:367).", walltime: -1 } +in.gREM-temper: { folder: examples/PACKAGES/grem/lj-temper, status: "failed, ERROR: World variable count doesn't match # of partitions (src/variable.cpp:255).", walltime: -1 } +in.compute_stress_mop: { folder: examples/PACKAGES/mop, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.fix_wall: { folder: examples/PACKAGES/ees, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.fix_wall_region: { folder: examples/PACKAGES/ees, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.ti_spring: { folder: examples/PACKAGES/ti, status: "failed, no Total wall time in the output.", walltime: -1 } +in.extep-bn: { folder: examples/PACKAGES/extep, status: "completed, 3 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } +in.toluene.lang: { folder: examples/PACKAGES/drude/toluene, status: "failed, no Total wall time in the output.", walltime: -1 } +in.toluene.nh: { folder: examples/PACKAGES/drude/toluene, status: "failed, no Total wall time in the output.", walltime: -1 } +in.butane.lang: { folder: examples/PACKAGES/drude/butane, status: "completed, 3 rel thermo checks failed", walltime: 50.0, walltime_norm: 8.333333333333334 } +in.butane.nh: { folder: examples/PACKAGES/drude/butane, status: "completed, 5 rel thermo checks failed", walltime: 49.0, walltime_norm: 8.166666666666666 } +in.butane.tgnh: { folder: examples/PACKAGES/drude/butane, status: "completed, 4 rel thermo checks failed", walltime: 49.0, walltime_norm: 8.166666666666666 } +in.swm4-ndp.lang: { folder: examples/PACKAGES/drude/swm4-ndp, status: "failed, no Total wall time in the output.", walltime: -1 } +in.swm4-ndp.nh: { folder: examples/PACKAGES/drude/swm4-ndp, status: "completed, 5 rel thermo checks failed", walltime: 55.0, walltime_norm: 9.166666666666666 } +in.ethylene_glycol: { folder: examples/PACKAGES/drude/ethylene_glycol, status: "completed, 4 rel thermo checks failed", walltime: 25.0, walltime_norm: 4.166666666666667 } +in.ethanol.lang: { folder: examples/PACKAGES/drude/ethanol, status: "completed, 5 rel thermo checks failed", walltime: 43.0, walltime_norm: 7.166666666666667 } +in.ethanol.nh: { folder: examples/PACKAGES/drude/ethanol, status: "completed, 5 rel thermo checks failed", walltime: 42.0, walltime_norm: 7.0 } +in.ethanol.tgnh: { folder: examples/PACKAGES/drude/ethanol, status: "completed, 5 rel thermo checks failed", walltime: 44.0, walltime_norm: 7.333333333333333 } +in.force: { folder: examples/PACKAGES/tally, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.pe: { folder: examples/PACKAGES/tally, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.stress: { folder: examples/PACKAGES/tally, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.system: { folder: examples/PACKAGES/momb, status: "completed, thermo checks passed", walltime: 13.0, walltime_norm: 2.1666666666666665 } +in.momentum: { folder: examples/PACKAGES/momentum, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 41.0, walltime_norm: 6.833333333333333 } +in.alpha: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.alpha_relaxation: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.beta: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.energy_conservation.meam.sw: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "failed, no Total wall time in the output.", walltime: -1 } +in.fcc: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.hexagonal: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.omega: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.single_atom: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.bcc: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.bcc_relax: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.dc: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.dc_relax: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.energy_conservation.meam.sw: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed, thermo checks passed", walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.fcc: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.fcc_relax: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.hcp_relax: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.sc: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.sc_relax: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.single_atom: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.film_mesocnt: { folder: examples/PACKAGES/mesont, status: "failed, no Total wall time in the output.", walltime: -1 } +in.cauchystat: { folder: examples/PACKAGES/cauchy, status: "completed, 1 rel thermo checks failed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.rubber_strip_pull: { folder: examples/PACKAGES/machdyn/rubber_strip_pull, status: "completed, thermo checks passed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.aluminum_strip_pull: { folder: examples/PACKAGES/machdyn/aluminum_strip_pull, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.funnel_flow: { folder: examples/PACKAGES/machdyn/funnel_flow, status: "completed, thermo checks passed", walltime: 29.0, walltime_norm: 4.833333333333333 } +in.fluid_structure_interaction: { folder: examples/PACKAGES/machdyn/fluid_structure_interaction, status: "completed, thermo checks passed", walltime: 32.0, walltime_norm: 5.333333333333333 } +in.rubber_rings_3d: { folder: examples/PACKAGES/machdyn/rubber_rings_3d, status: "completed, thermo checks passed", walltime: 21.0, walltime_norm: 3.5 } +in.h2o-quantum: { folder: examples/PACKAGES/gle, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 13.0, walltime_norm: 2.1666666666666665 } +in.h2o-smart: { folder: examples/PACKAGES/gle, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 13.0, walltime_norm: 2.1666666666666665 } +in.pafi: { folder: examples/PACKAGES/pafi, status: "failed, no Total wall time in the output.", walltime: -1 } +in.scp: { folder: examples/PACKAGES/pimd/prot-hairpin, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 15.0, walltime_norm: 2.5 } +in.scp: { folder: examples/PACKAGES/pimd/para-h2, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 6.0, walltime_norm: 1.0 } +in.lmp: { folder: examples/PACKAGES/pimd/langevin_reduced_units, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.langevin.metal: { folder: examples/PACKAGES/pimd/langevin_metal_units, status: "completed, 2 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } +in.pimd-langevin.metal: { folder: examples/PACKAGES/pimd/langevin_metal_units, status: "completed, 2 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } +in.rann: { folder: examples/PACKAGES/rann, status: "failed, no Total wall time in the output.", walltime: -1 } +in.msd.2d: { folder: examples/DIFFUSE, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 36.0, walltime_norm: 6.0 } +in.vacf.2d: { folder: examples/DIFFUSE, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 38.0, walltime_norm: 6.333333333333333 } +in.numdiff: { folder: examples/numdiff, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.bpm.pour: { folder: examples/bpm/pour, status: "failed, no Total wall time in the output.", walltime: -1 } +in.bpm.impact.rotational: { folder: examples/bpm/impact, status: "failed, no Total wall time in the output.", walltime: -1 } +in.bpm.impact.spring: { folder: examples/bpm/impact, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 12.0, walltime_norm: 2.0 } +in.rheo.balloon: { folder: examples/rheo/balloon, status: "failed, no Total wall time in the output.", walltime: -1 } +in.rheo.oxidation: { folder: examples/rheo/oxidation, status: "failed, no Total wall time in the output.", walltime: -1 } +in.rheo.taylor.green: { folder: examples/rheo/taylor-green, status: "failed, no Total wall time in the output.", walltime: -1 } +in.rheo.ice.cubes: { folder: examples/rheo/ice-cubes, status: "failed, no Total wall time in the output.", walltime: -1 } +in.rheo.poiseuille: { folder: examples/rheo/poiseuille, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 29.0, walltime_norm: 4.833333333333333 } +in.rheo.dam.break: { folder: examples/rheo/dam-break, status: "failed, no Total wall time in the output.", walltime: -1 } +in.peptide: { folder: examples/peptide, status: "completed, error parsing log.lammps into YAML", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.coreshell: { folder: examples/coreshell, status: "completed, 9 rel thermo checks failed", walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.coreshell.dsf: { folder: examples/coreshell, status: "completed, 8 rel thermo checks failed", walltime: 20.0, walltime_norm: 3.3333333333333335 } +in.coreshell.thermostats: { folder: examples/coreshell, status: "completed, 14 rel thermo checks failed", walltime: 14.0, walltime_norm: 2.3333333333333335 } +in.coreshell.wolf: { folder: examples/coreshell, status: "completed, 5 rel thermo checks failed", walltime: 22.0, walltime_norm: 3.6666666666666665 } +in.marble_race: { folder: examples/mesh, status: "failed, no Total wall time in the output.", walltime: -1 } +in.mesh_box: { folder: examples/mesh, status: "completed, thermo checks passed", walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.abcfire: { folder: examples/fire, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.abcfire_mod: { folder: examples/fire, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.cg: { folder: examples/fire, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.fire: { folder: examples/fire, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.fire_mod: { folder: examples/fire, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.meam.abcfire: { folder: examples/fire, status: "completed, thermo checks passed", walltime: 44.0, walltime_norm: 7.333333333333333 } +in.meam.fire: { folder: examples/fire, status: "failed, no Total wall time in the output.", walltime: -1 } +in.neb.sivac.abcfire: { folder: examples/fire, status: "failed, ERROR: Cannot use NEB with a single replica (src/REPLICA/neb.cpp:141).", walltime: -1 } +in.neb.sivac.abcfire_mod: { folder: examples/fire, status: "failed, ERROR: Cannot use NEB with a single replica (src/REPLICA/neb.cpp:141).", walltime: -1 } +in.neb.sivac.fire: { folder: examples/fire, status: "failed, ERROR: Cannot use NEB with a single replica (src/REPLICA/neb.cpp:141).", walltime: -1 } +in.neb.sivac.fire_mod: { folder: examples/fire, status: "failed, ERROR: Cannot use NEB with a single replica (src/REPLICA/neb.cpp:141).", walltime: -1 } +in.neb.sivac.qm: { folder: examples/fire, status: "failed, ERROR: Cannot use NEB with a single replica (src/REPLICA/neb.cpp:141).", walltime: -1 } +in.bcc.orthog: { folder: examples/triclinic, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.bcc.primitive: { folder: examples/triclinic, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.data.general: { folder: examples/triclinic, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.fcc.orthog: { folder: examples/triclinic, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.fcc.primitive: { folder: examples/triclinic, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.hex.orthog: { folder: examples/triclinic, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.hex.primitive: { folder: examples/triclinic, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.sq2.orthog: { folder: examples/triclinic, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.sq2.primitive: { folder: examples/triclinic, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.tri.srd: { folder: examples/ASPHERE/tri, status: "failed, no Total wall time in the output.", walltime: -1 } +in.star: { folder: examples/ASPHERE/star, status: "completed, 3 rel thermo checks failed", walltime: 12.0, walltime_norm: 2.0 } +in.star.mp: { folder: examples/ASPHERE/star, status: "completed, 3 rel thermo checks failed", walltime: 12.0, walltime_norm: 2.0 } +in.box: { folder: examples/ASPHERE/box, status: "completed, 3 rel thermo checks failed", walltime: 18.0, walltime_norm: 3.0 } +in.box.mp: { folder: examples/ASPHERE/box, status: "completed, 3 rel thermo checks failed", walltime: 18.0, walltime_norm: 3.0 } +in.dimer: { folder: examples/ASPHERE/dimer, status: "completed, 3 rel thermo checks failed", walltime: 6.0, walltime_norm: 1.0 } +in.dimer.mp: { folder: examples/ASPHERE/dimer, status: "completed, 3 rel thermo checks failed", walltime: 16.0, walltime_norm: 2.6666666666666665 } +in.vesicle: { folder: examples/ASPHERE/vesicle, status: "completed, thermo checks passed", walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.line: { folder: examples/ASPHERE/line, status: "failed, no Total wall time in the output.", walltime: -1 } +in.line.srd: { folder: examples/ASPHERE/line, status: "failed, no Total wall time in the output.", walltime: -1 } +in.poly: { folder: examples/ASPHERE/poly, status: "completed, thermo checks passed", walltime: 8.0, walltime_norm: 1.3333333333333333 } +in.poly.mp: { folder: examples/ASPHERE/poly, status: "completed, thermo checks passed", walltime: 9.0, walltime_norm: 1.5 } +in.flat_membrane: { folder: examples/ASPHERE/flat_membrane, status: "completed, 2 rel thermo checks failed", walltime: 7.0, walltime_norm: 1.1666666666666667 } +in.ellipsoid: { folder: examples/ASPHERE/ellipsoid, status: "completed, thermo checks passed", walltime: 9.0, walltime_norm: 1.5 } +in.ellipsoid.mp: { folder: examples/ASPHERE/ellipsoid, status: "completed, thermo checks passed", walltime: 9.0, walltime_norm: 1.5 } +in.ubiquitin: { folder: examples/amoeba, status: "completed, thermo checks passed", walltime: 13.0, walltime_norm: 2.1666666666666665 } +in.water_box.amoeba: { folder: examples/amoeba, status: "completed, thermo checks passed", walltime: 8.0, walltime_norm: 1.3333333333333333 } +in.water_box.hippo: { folder: examples/amoeba, status: "completed, thermo checks passed", walltime: 6.0, walltime_norm: 1.0 } +in.water_dimer.amoeba: { folder: examples/amoeba, status: "completed, 1 abs thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } +in.water_dimer.hippo: { folder: examples/amoeba, status: "completed, 1 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } +in.water_hexamer.amoeba: { folder: examples/amoeba, status: "completed, 1 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } +in.water_hexamer.hippo: { folder: examples/amoeba, status: "completed, 1 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } +in.nb3b: { folder: examples/nb3b, status: "completed, thermo checks passed", walltime: 5.0, walltime_norm: 0.8333333333333334 } +in.nb3b.screened: { folder: examples/nb3b, status: "completed, thermo checks passed", walltime: 30.0, walltime_norm: 5.0 } +in.min: { folder: examples/min, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.min.box: { folder: examples/min, status: "completed, thermo checks passed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.balance: { folder: examples/balance, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.balance.bond.fast: { folder: examples/balance, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.balance.bond.slow: { folder: examples/balance, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.balance.clock.dynamic: { folder: examples/balance, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 17.0, walltime_norm: 2.8333333333333335 } +in.balance.clock.static: { folder: examples/balance, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 6.0, walltime_norm: 1.0 } +in.balance.group.dynamic: { folder: examples/balance, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.balance.group.static: { folder: examples/balance, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.balance.kspace: { folder: examples/balance, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 6.0, walltime_norm: 1.0 } +in.balance.neigh.dynamic: { folder: examples/balance, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 16.0, walltime_norm: 2.6666666666666665 } +in.balance.neigh.rcb: { folder: examples/balance, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.balance.neigh.static: { folder: examples/balance, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.balance.var.dynamic: { folder: examples/balance, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 16.0, walltime_norm: 2.6666666666666665 } +in.ch4: { folder: examples/QUANTUM/LATTE, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.ch4.plugin: { folder: examples/QUANTUM/LATTE, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.graphene: { folder: examples/QUANTUM/LATTE, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.graphene.plugin: { folder: examples/QUANTUM/LATTE, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.series: { folder: examples/QUANTUM/LATTE, status: "failed, unknown command, package not installed, ERROR: Unknown command: mdi connect (src/input.cpp:314)", walltime: -1 } +in.series.plugin: { folder: examples/QUANTUM/LATTE, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.sucrose: { folder: examples/QUANTUM/LATTE, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.sucrose.plugin: { folder: examples/QUANTUM/LATTE, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.uo2: { folder: examples/QUANTUM/LATTE, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.uo2.plugin: { folder: examples/QUANTUM/LATTE, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.water: { folder: examples/QUANTUM/LATTE, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.water.min: { folder: examples/QUANTUM/LATTE, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.water.min.plugin: { folder: examples/QUANTUM/LATTE, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.water.plugin: { folder: examples/QUANTUM/LATTE, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.mixture.mm: { folder: examples/QUANTUM/PySCF, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.mixture.qmmm: { folder: examples/QUANTUM/PySCF, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qmmm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.mixture.qmmm.plugin: { folder: examples/QUANTUM/PySCF, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qmmm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.water.qmmm: { folder: examples/QUANTUM/PySCF, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qmmm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.water.qmmm.plugin: { folder: examples/QUANTUM/PySCF, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qmmm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.series: { folder: examples/QUANTUM/NWChem, status: "failed, unknown command, package not installed, ERROR: Unknown command: mdi connect (src/input.cpp:314)", walltime: -1 } +in.series.plugin: { folder: examples/QUANTUM/NWChem, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.water.mm: { folder: examples/QUANTUM/NWChem, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.water.qmmm: { folder: examples/QUANTUM/NWChem, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qmmm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.water.qmmm.plugin: { folder: examples/QUANTUM/NWChem, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qmmm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.zeolite.mm: { folder: examples/QUANTUM/NWChem, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.zeolite.qmmm: { folder: examples/QUANTUM/NWChem, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qmmm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.zeolite.qmmm.plugin: { folder: examples/QUANTUM/NWChem, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qmmm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } +in.wall.ccl: { folder: examples/wall, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.wall.diffusive: { folder: examples/wall, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.wall.flow: { folder: examples/wall, status: "completed, thermo checks passed", walltime: 6.0, walltime_norm: 1.0 } +in.wall.lepton: { folder: examples/wall, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.wall.maxwell: { folder: examples/wall, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.wall.specular: { folder: examples/wall, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.wall.table: { folder: examples/wall, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.meam: { folder: examples/meam, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.meam.shear: { folder: examples/meam, status: "completed, 3 rel thermo checks failed", walltime: 31.0, walltime_norm: 5.166666666666667 } +in.msmeam: { folder: examples/meam/msmeam, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.hugoniostat: { folder: examples/hugoniostat, status: "completed, thermo checks passed", walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.comb.Cu: { folder: examples/comb, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.comb.Cu2O.elastic: { folder: examples/comb, status: "completed, thermo checks passed", walltime: 21.0, walltime_norm: 3.5 } +in.comb.HfO2: { folder: examples/comb, status: "completed, thermo checks passed", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.comb.Si: { folder: examples/comb, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.comb.Si.elastic: { folder: examples/comb, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.comb3: { folder: examples/comb, status: "completed, thermo checks passed", walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.tad: { folder: examples/tad, status: "failed, ERROR: Cannot use TAD with a single replica for NEB (src/REPLICA/tad.cpp:79).", walltime: -1 } +in.controller.temp: { folder: examples/controller, status: "completed, thermo checks passed", walltime: 6.0, walltime_norm: 1.0 } +in.controller.wall: { folder: examples/controller, status: "completed, thermo checks passed", walltime: 19.0, walltime_norm: 3.1666666666666665 } +in.reaxff.rdx: { folder: examples/reaxff, status: "completed, 3 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } +in.reaxff.rdx-shielded: { folder: examples/reaxff, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.reaxff.tatb: { folder: examples/reaxff, status: "completed, 2 rel thermo checks failed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.reaxff.tatb-shielded: { folder: examples/reaxff, status: "completed, 2 rel thermo checks failed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.ci-reax.CH: { folder: examples/reaxff/ci-reaxFF, status: "completed, thermo checks passed", walltime: 13.0, walltime_norm: 2.1666666666666665 } +in.reaxff.hns: { folder: examples/reaxff/HNS, status: "completed, thermo checks passed", walltime: 20.0, walltime_norm: 3.3333333333333335 } +in.VOH: { folder: examples/reaxff/VOH, status: "completed, 2 rel thermo checks failed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.water.acks2: { folder: examples/reaxff/water, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 13.0, walltime_norm: 2.1666666666666665 } +in.water.acks2.field: { folder: examples/reaxff/water, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 17.0, walltime_norm: 2.8333333333333335 } +in.water.qeq: { folder: examples/reaxff/water, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 6.0, walltime_norm: 1.0 } +in.water.qeq.field: { folder: examples/reaxff/water, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 5.0, walltime_norm: 0.8333333333333334 } +in.ZnOH2: { folder: examples/reaxff/ZnOH2, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.FC: { folder: examples/reaxff/FC, status: "completed, thermo checks passed", walltime: 15.0, walltime_norm: 2.5 } +in.RDX: { folder: examples/reaxff/RDX, status: "completed, 3 rel thermo checks failed", walltime: 6.0, walltime_norm: 1.0 } +in.AuO: { folder: examples/reaxff/AuO, status: "completed, thermo checks passed", walltime: 6.0, walltime_norm: 1.0 } +in.CHO: { folder: examples/reaxff/CHO, status: "completed, 2 rel thermo checks failed", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.FeOH3: { folder: examples/reaxff/FeOH3, status: "completed, thermo checks passed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.AB: { folder: examples/reaxff/AB, status: "completed, 3 rel thermo checks failed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.grid.2d: { folder: examples/grid, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.grid.3d: { folder: examples/grid, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.sph: { folder: examples/grid, status: "completed, thermo checks passed", walltime: 36.0, walltime_norm: 6.0 } +in.yaml: { folder: examples/yaml, status: "completed, error parsing log.lammps into YAML", walltime: 3.0, walltime_norm: 0.5 } +in.hBN_shift: { folder: examples/tersoff, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.tersoff: { folder: examples/tersoff, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.friction: { folder: examples/friction, status: "completed, thermo checks passed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.cmap: { folder: examples/cmap, status: "completed, 1 rel thermo checks failed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.dipole: { folder: examples/dipole, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.colloid: { folder: examples/colloid, status: "completed, 3 rel thermo checks failed", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.streitz.ewald: { folder: examples/streitz, status: "completed, 2 rel thermo checks failed", walltime: 54.0, walltime_norm: 9.0 } +in.streitz.wolf: { folder: examples/streitz, status: "failed, mismatched columns in the log files", walltime: 57.0, walltime_norm: 9.5 } +in.neb.hop1: { folder: examples/neb, status: "failed, ERROR: Cannot use NEB with a single replica (src/REPLICA/neb.cpp:141).", walltime: -1 } +in.neb.hop1.end: { folder: examples/neb, status: "failed, ERROR: Cannot use NEB with a single replica (src/REPLICA/neb.cpp:141).", walltime: -1 } +in.neb.hop2: { folder: examples/neb, status: "failed, ERROR: Cannot use NEB with a single replica (src/REPLICA/neb.cpp:141).", walltime: -1 } +in.neb.sivac: { folder: examples/neb, status: "failed, ERROR: Cannot use NEB with a single replica (src/REPLICA/neb.cpp:141).", walltime: -1 } +in.fix_python_invoke: { folder: examples/python, status: "failed, ERROR: Could not process Python string: .", walltime: -1 } +in.fix_python_invoke_neighlist: { folder: examples/python, status: "failed, ERROR: Could not process Python string: .", walltime: -1 } +in.fix_python_move_nve_melt: { folder: examples/python, status: "failed, ERROR: Loading python integrator module failure (src/PYTHON/fix_python_move.cpp:64).", walltime: -1 } +in.fix_python_move_nve_melt_opt: { folder: examples/python, status: "failed, ERROR: Loading python integrator module failure (src/PYTHON/fix_python_move.cpp:64).", walltime: -1 } +in.pair_python_coulomb: { folder: examples/python, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } +in.pair_python_harmonic: { folder: examples/python, status: "completed, thermo checks passed", walltime: 45.0, walltime_norm: 7.5 } +in.pair_python_hybrid: { folder: examples/python, status: "completed, 3 rel thermo checks failed", walltime: 13.0, walltime_norm: 2.1666666666666665 } +in.pair_python_long: { folder: examples/python, status: "completed, thermo checks passed", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.pair_python_melt: { folder: examples/python, status: "completed, 3 rel thermo checks failed", walltime: 28.0, walltime_norm: 4.666666666666667 } +in.pair_python_spce: { folder: examples/python, status: "completed, thermo checks passed", walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.pair_python_table: { folder: examples/python, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.python: { folder: examples/python, status: "failed, ERROR on proc 0: Python evaluation of function loop failed (src/PYTHON/python_impl.cpp:384).", walltime: -1 } +in.bcc: { folder: examples/steinhardt, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.fcc: { folder: examples/steinhardt, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.icos: { folder: examples/steinhardt, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.kim-ex.melt: { folder: examples/kim, status: "failed, unknown command, package not installed, ERROR: Unknown command: kim init LennardJones_Ar real (src/input.cpp:314)", walltime: -1 } +in.kim-pm-property: { folder: examples/kim, status: "failed, unknown command, package not installed, ERROR: Unknown command: kim init LJ_Shifted_Bernardes_1958MedCutoff_Ar__MO_126566794224_004 metal (src/input.cpp:314)", walltime: -1 } +in.kim-pm-query.melt: { folder: examples/kim, status: "failed, unknown command, package not installed, ERROR: Unknown command: kim init SW_StillingerWeber_1985_Si__MO_405512056662_005 real (src/input.cpp:314)", walltime: -1 } +in.kim-pm.melt: { folder: examples/kim, status: "failed, unknown command, package not installed, ERROR: Unknown command: kim init SW_StillingerWeber_1985_Si__MO_405512056662_005 real (src/input.cpp:314)", walltime: -1 } +in.kim-query: { folder: examples/kim, status: "failed, unknown command, package not installed, ERROR: Unknown command: kim init EAM_Dynamo_ErcolessiAdams_1994_Al__MO_123629422045_005 metal (src/input.cpp:314)", walltime: -1 } +in.kim-sm.melt: { folder: examples/kim, status: "failed, unknown command, package not installed, ERROR: Unknown command: kim init Sim_LAMMPS_ReaxFF_StrachanVanDuinChakraborty_2003_CHNO__SM_107643900657_000 real (src/input.cpp:314)", walltime: -1 } +in.lammps.melt: { folder: examples/kim, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.ellipse.gayberne: { folder: examples/ellipse, status: "completed, 2 rel thermo checks failed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.ellipse.resquared: { folder: examples/ellipse, status: "completed, 2 rel thermo checks failed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.cos.1000SPCE: { folder: examples/VISCOSITY, status: "completed, 3 rel thermo checks failed", walltime: 36.0, walltime_norm: 6.0 } +in.einstein.2d: { folder: examples/VISCOSITY, status: "completed, thermo checks passed", walltime: 38.0, walltime_norm: 6.333333333333333 } +in.gk.2d: { folder: examples/VISCOSITY, status: "completed, thermo checks passed", walltime: 38.0, walltime_norm: 6.333333333333333 } +in.mp.2d: { folder: examples/VISCOSITY, status: "completed, thermo checks passed", walltime: 5.0, walltime_norm: 0.8333333333333334 } +in.nemd.2d: { folder: examples/VISCOSITY, status: "completed, 8 rel thermo checks failed", walltime: 17.0, walltime_norm: 2.8333333333333335 } +in.wall.2d: { folder: examples/VISCOSITY, status: "completed, thermo checks passed", walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.gcmc.co2: { folder: examples/mc, status: "completed, thermo checks passed", walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.gcmc.h2o: { folder: examples/mc, status: "failed, no Total wall time in the output.", walltime: -1 } +in.gcmc.lj: { folder: examples/mc, status: "completed, thermo checks passed", walltime: 18.0, walltime_norm: 3.0 } +in.mixed: { folder: examples/mc, status: "failed, no Total wall time in the output.", walltime: -1 } +in.pure: { folder: examples/mc, status: "failed, no Total wall time in the output.", walltime: -1 } +in.sgcmc.eam: { folder: examples/mc, status: "completed, thermo checks passed", walltime: 46.0, walltime_norm: 7.666666666666667 } +in.widom.lj: { folder: examples/mc, status: "completed, thermo checks passed", walltime: 8.0, walltime_norm: 1.3333333333333333 } +in.widom.spce: { folder: examples/mc, status: "completed, thermo checks passed", walltime: 45.0, walltime_norm: 7.5 } +in.mc: { folder: examples/MC-LOOP, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.flow.couette: { folder: examples/flow, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.flow.pois: { folder: examples/flow, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.prd: { folder: examples/prd, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 3.0, walltime_norm: 0.5 } +in.C_SNAP: { folder: examples/snap, status: "completed, thermo checks passed", walltime: 34.0, walltime_norm: 5.666666666666667 } +in.grid.snap: { folder: examples/snap, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.grid.tri: { folder: examples/snap, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.snap.InP.JCPA2020: { folder: examples/snap, status: "completed, thermo checks passed", walltime: 15.0, walltime_norm: 2.5 } +in.snap.Mo_Chen: { folder: examples/snap, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.snap.Ta06A: { folder: examples/snap, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.snap.W.2940: { folder: examples/snap, status: "completed, thermo checks passed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.snap.WBe.PRB2019: { folder: examples/snap, status: "completed, thermo checks passed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.snap.compute: { folder: examples/snap, status: "completed, but no Step nor Loop in the output.", walltime: 0.0, walltime_norm: 0.0 } +in.snap.compute.quadratic: { folder: examples/snap, status: "completed, but no Step nor Loop in the output.", walltime: 0.0, walltime_norm: 0.0 } +in.snap.hybrid.WSNAP.HePair: { folder: examples/snap, status: "completed, thermo checks passed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.snap.scale.Ni_Zuo_JCPA2020: { folder: examples/snap, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 3.0, walltime_norm: 0.5 } +in.lammps: { folder: examples/COUPLE/lammps_spparks, status: "failed, ERROR: Cannot open file data.lammps: No such file or directory (src/read_data.cpp:367).", walltime: -1 } +in.spparks: { folder: examples/COUPLE/lammps_spparks, status: "failed, unknown command, package not installed, ERROR: Unknown command: seed 56789 (src/input.cpp:314)", walltime: -1 } +in.lj: { folder: examples/COUPLE/plugin, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.fix_external: { folder: examples/COUPLE/python, status: "completed, but no Step nor Loop in the output.", walltime: 0.0, walltime_norm: 0.0 } +in.chain: { folder: examples/COUPLE/multiple, status: "failed, no Total wall time in the output.", walltime: -1 } +in.lj: { folder: examples/COUPLE/simple, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.body: { folder: examples/body, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.cubes: { folder: examples/body, status: "completed, 2 rel thermo checks failed", walltime: 36.0, walltime_norm: 6.0 } +in.pour3d: { folder: examples/body, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.squares: { folder: examples/body, status: "completed, 3 rel thermo checks failed", walltime: 3.0, walltime_norm: 0.5 } +in.wall2d: { folder: examples/body, status: "completed, 3 rel thermo checks failed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.atm: { folder: examples/atm, status: "completed, thermo checks passed", walltime: 13.0, walltime_norm: 2.1666666666666665 } +in.ar.lj: { folder: examples/UNITS, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.ar.metal: { folder: examples/UNITS, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.ar.real: { folder: examples/UNITS, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.mos2-bulk: { folder: examples/threebody, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.mos2.rebomos: { folder: examples/threebody, status: "completed, thermo checks passed", walltime: 21.0, walltime_norm: 3.5 } +in.mos2.sw.mod: { folder: examples/threebody, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.threebody: { folder: examples/threebody, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.nemd: { folder: examples/nemd, status: "completed, 3 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } +in.obstacle: { folder: examples/obstacle, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.crack: { folder: examples/crack, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } +in.elastic: { folder: examples/ELASTIC, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } +in.peri-pmb: { folder: examples/peri, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 9.0, walltime_norm: 1.5 } +in.peri.eps: { folder: examples/peri, status: "completed, 3 rel thermo checks failed", walltime: 21.0, walltime_norm: 3.5 } +in.peri.lps: { folder: examples/peri, status: "completed, 3 rel thermo checks failed", walltime: 17.0, walltime_norm: 2.8333333333333335 } +in.peri.pmb: { folder: examples/peri, status: "completed, thermo checks passed", walltime: 9.0, walltime_norm: 1.5 } +in.peri.ves: { folder: examples/peri, status: "completed, 3 rel thermo checks failed", walltime: 21.0, walltime_norm: 3.5 } +in.hyper.global: { folder: examples/hyper, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 53.0, walltime_norm: 8.833333333333334 } +in.hyper.local: { folder: examples/hyper, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 14.0, walltime_norm: 2.3333333333333335 } +in.spce: { folder: examples/rdf-adf, status: "failed, no Total wall time in the output.", walltime: -1 } +in.elastic: { folder: examples/ELASTIC_T/BORN_MATRIX/Silicon, status: "failed, no Total wall time in the output.", walltime: -1 } +in.ljcov: { folder: examples/ELASTIC_T/BORN_MATRIX/Argon/Analytical, status: "failed, no Total wall time in the output.", walltime: -1 } +in.ljcov: { folder: examples/ELASTIC_T/BORN_MATRIX/Argon/Numdiff, status: "failed, no Total wall time in the output.", walltime: -1 } +in.elastic: { folder: examples/ELASTIC_T/DEFORMATION/Silicon, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 9.0, walltime_norm: 1.5 } From 57353566d666265c8ce507f62df08e556c3ab423 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 7 Sep 2024 00:35:57 -0400 Subject: [PATCH 245/355] one more fix (it is getting late...) --- tools/regression-tests/run_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index ea3d43e254..ee56a75682 100644 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -1167,7 +1167,7 @@ if __name__ == "__main__": print(msg) logger.info(msg) - quit() + # divide the list of input scripts into num_workers chunks sublists = divide_into_N(input_list, num_workers) From 6b68656a743035dbf333583e2df3bdb599162468 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Sat, 7 Sep 2024 09:06:22 -0500 Subject: [PATCH 246/355] fix a typo --- doc/src/Tools.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/Tools.rst b/doc/src/Tools.rst index 9f9f63f46a..ba7cb2035a 100644 --- a/doc/src/Tools.rst +++ b/doc/src/Tools.rst @@ -1022,7 +1022,7 @@ regression tests with a given LAMMPS binary. The tool launches the LAMMPS binary with any given input script under one of the `examples` subdirectories, and compares the thermo output in the generated log file with those in the provided log file with the same number of processors -ub the same subdirectory. If the differences between the actual and +in the same subdirectory. If the differences between the actual and reference values are within specified tolerances, the test is considered passed. For each test batch, that is, a set of example input scripts, the mpirun command, the LAMMPS command line arguments, and the From 397ca4bd25f300bb12e84a4dc65261d5e2b100fd Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Sat, 7 Sep 2024 09:10:25 -0500 Subject: [PATCH 247/355] correct the file names in examples phonon that are actually not LAMMPS input --- tools/regression-tests/config.yaml | 4 ++-- tools/regression-tests/config_quick.yaml | 4 ++-- tools/regression-tests/config_serial.yaml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/regression-tests/config.yaml b/tools/regression-tests/config.yaml index 6b793d0ce8..dd7ffe0b03 100644 --- a/tools/regression-tests/config.yaml +++ b/tools/regression-tests/config.yaml @@ -30,8 +30,8 @@ rel: 1e-4 skip: [ - in.displ, - in.displ2, + in.disp, + in.disp2, in.dos, in.*_imd*, in.bucky-plus-cnt*, diff --git a/tools/regression-tests/config_quick.yaml b/tools/regression-tests/config_quick.yaml index 3c8cc4e51b..bc6e19b730 100644 --- a/tools/regression-tests/config_quick.yaml +++ b/tools/regression-tests/config_quick.yaml @@ -30,8 +30,8 @@ rel: 1e-4 skip: [ - in.displ, - in.displ2, + in.disp, + in.disp2, in.dos, in.*_imd*, in.bucky-plus-cnt*, diff --git a/tools/regression-tests/config_serial.yaml b/tools/regression-tests/config_serial.yaml index b55cc1547d..705fb7ec9b 100644 --- a/tools/regression-tests/config_serial.yaml +++ b/tools/regression-tests/config_serial.yaml @@ -30,8 +30,8 @@ rel: 1e-4 skip: [ - in.displ, - in.displ2, + in.disp, + in.disp2, in.dos, in.*_imd*, in.bucky-plus-cnt*, From 77bf224b3f439eef9e685bff4669fa8e0fbf9d71 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Sun, 8 Sep 2024 00:24:33 -0500 Subject: [PATCH 248/355] report if a run is timeout to progress.yaml --- tools/regression-tests/run_tests.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index ee56a75682..cb7c64308f 100644 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -410,7 +410,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file logger.info(f"\n Output:\n{output}") logger.info(f"\n Error:\n{error}") - msg = f"{input}: {{ folder: {input_folder}, status: \"failed, no Total wall time in the output.\", walltime: {walltime} }}\n" + msg = f"{input}: {{ folder: {input_folder}, status: \"failed, no Total wall time in the output, {error}\", walltime: {walltime} }}\n" progress.write(msg) progress.close() failure.write(msg) @@ -851,7 +851,8 @@ def execute(lmp_binary, config, input_file_name, generate_ref_yaml=False): logger.info(msg) print(msg) - return cmd_str, "", "", -1 + error_str = f"timeout ({timeout}s expired)" + return cmd_str, "", error_str, -1 ''' get the reference walltime by running the lmp_binary with config with an input script in the bench/ folder From 4d04d8492d4485bebcedd6eaf8c3016e3ea77533 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Sun, 8 Sep 2024 17:09:05 -0500 Subject: [PATCH 249/355] report the number of abs and rel diff checks failed --- tools/regression-tests/run_tests.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index cb7c64308f..68a81ac260 100644 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -639,35 +639,39 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file msg = f" mismatched log files after the first run. Check both log files for more details." print(msg) logger.info(msg) - result.status = "thermo checks failed" + result.status = "thermo checks failed due to mismatched log files after the first run" + result.status = "" if num_abs_failed > 0: - msg = f" {num_abs_failed} abs diff thermo checks failed." + msg = f" {num_abs_failed} abs diff checks failed." print(msg) logger.info(msg) - result.status = f"{num_abs_failed} abs thermo checks failed" + #result.status = f"abs_diff_failed: {num_abs_failed}, " if verbose == True: for out in failed_abs_output: print(f" - {out}") + if num_rel_failed > 0: - msg = f" {num_rel_failed} rel diff thermo checks failed." + msg = f" {num_rel_failed} rel diff checks failed." print(msg) logger.info(msg) - result.status = f"{num_rel_failed} rel thermo checks failed" + #result.status += f"rel_diff_failed: {num_rel_failed}" if verbose == True: for out in failed_rel_output: print(f" - {out}") + if num_abs_failed == 0 and num_rel_failed == 0: - msg = f" all {num_checks} thermo checks passed." + msg = f" all {num_checks} checks passed." print(msg) logger.info(msg) - result.status = "thermo checks passed" + #result.status = f"all {num_checks} checks passed." num_passed = num_passed + 1 + result.status = f"abs_diff_failed: {num_abs_failed}, rel_diff_failed: {num_rel_failed}" results.append(result) # check if memleak detects from valgrind run (need to replace "mpirun" -> valgrind --leak-check=yes mpirun") - msg = "completed, " + result.status + msg = "completed" if use_valgrind == True: if "All heap blocks were freed" in error: msg += ", no memory leak" @@ -675,12 +679,12 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file msg += ", memory leaks detected" num_memleak = num_memleak + 1 - progress.write(f"{input}: {{ folder: {input_folder}, status: \"{msg}\", walltime: {walltime}, walltime_norm: {walltime_norm} }}\n") + progress.write(f"{input}: {{ folder: {input_folder}, status: \"{msg}\", failed_checks: {{ \"{result.status}\" }}, walltime: {walltime}, walltime_norm: {walltime_norm} }}\n") progress.close() # write to failure if there is any numerical failed check if num_abs_failed > 0 or num_rel_failed > 0: - failure.write(f"{input}: {{ folder: {input_folder}, status: \"{msg}\", walltime: {walltime}, walltime_norm: {walltime_norm} }}\n") + failure.write(f"{input}: {{ folder: {input_folder}, status: \"{msg}\", failed_checks: {{ \"{result.status}\" }}, walltime: {walltime}, walltime_norm: {walltime_norm} }}\n") # count the number of completed runs num_completed = num_completed + 1 From 274112834b3c5c492e7aaf31efcb554af2feaeb5 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Sun, 8 Sep 2024 17:11:02 -0500 Subject: [PATCH 250/355] remove double quotes --- tools/regression-tests/run_tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index 68a81ac260..5f88f03f64 100644 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -679,12 +679,12 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file msg += ", memory leaks detected" num_memleak = num_memleak + 1 - progress.write(f"{input}: {{ folder: {input_folder}, status: \"{msg}\", failed_checks: {{ \"{result.status}\" }}, walltime: {walltime}, walltime_norm: {walltime_norm} }}\n") + progress.write(f"{input}: {{ folder: {input_folder}, status: \"{msg}\", failed_checks: {{ {result.status} }}, walltime: {walltime}, walltime_norm: {walltime_norm} }}\n") progress.close() # write to failure if there is any numerical failed check if num_abs_failed > 0 or num_rel_failed > 0: - failure.write(f"{input}: {{ folder: {input_folder}, status: \"{msg}\", failed_checks: {{ \"{result.status}\" }}, walltime: {walltime}, walltime_norm: {walltime_norm} }}\n") + failure.write(f"{input}: {{ folder: {input_folder}, status: \"{msg}\", failed_checks: {{ {result.status} }}, walltime: {walltime}, walltime_norm: {walltime_norm} }}\n") # count the number of completed runs num_completed = num_completed + 1 From c05390209e9008b8166a8a0d27b58fce076845b1 Mon Sep 17 00:00:00 2001 From: Evan Weinberg Date: Mon, 9 Sep 2024 12:10:13 -0700 Subject: [PATCH 251/355] Removed d_bo_first, d_hb_first, replacing it with the strided offsets to the bonds for atom i --- src/KOKKOS/pair_reaxff_kokkos.cpp | 70 ++++++++++++------------------- src/KOKKOS/pair_reaxff_kokkos.h | 2 +- 2 files changed, 28 insertions(+), 44 deletions(-) diff --git a/src/KOKKOS/pair_reaxff_kokkos.cpp b/src/KOKKOS/pair_reaxff_kokkos.cpp index 6ff955e6d8..a2844c2ff8 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.cpp +++ b/src/KOKKOS/pair_reaxff_kokkos.cpp @@ -1502,7 +1502,6 @@ void PairReaxFFKokkos::allocate_array() } if (cut_hbsq > 0.0) { - MemKK::realloc_kokkos(d_hb_first,"reaxff/kk:hb_first",nmax); MemKK::realloc_kokkos(d_hb_num,"reaxff/kk:hb_num",nmax); if (((bigint) nmax*maxhb) > MAXSMALLINT) @@ -1510,7 +1509,6 @@ void PairReaxFFKokkos::allocate_array() MemKK::realloc_kokkos(d_hb_list,"reaxff/kk:hb_list",nmax*maxhb); } - MemKK::realloc_kokkos(d_bo_first,"reaxff/kk:bo_first",nmax); MemKK::realloc_kokkos(d_bo_num,"reaxff/kk:bo_num",nmax); if (((bigint) nmax*maxbo) > MAXSMALLINT) @@ -1606,8 +1604,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfBlocking< F_FLOAT dDeltap_self_i[3] = {0.0,0.0,0.0}; F_FLOAT total_bo_i = 0.0; - d_bo_first[i] = i*maxbo; - const int bo_first_i = d_bo_first[i]; + const int bo_first_i = i * maxbo; int ihb = -1; @@ -1615,8 +1612,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfBlocking< if (cut_hbsq > 0.0) { ihb = paramssing(itype).p_hbond; if (ihb == 1) { - d_hb_first[i] = i*maxhb; - hb_first_i = d_hb_first[i]; + hb_first_i = i * maxhb; } } @@ -1632,9 +1628,6 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfBlocking< int j = d_neighbors(i,jj); j &= NEIGHMASK; - d_bo_first[j] = j*maxbo; - d_hb_first[j] = j*maxhb; - delij[0] = x(j,0) - xtmp; delij[1] = x(j,1) - ytmp; delij[2] = x(j,2) - ztmp; @@ -1750,8 +1743,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfBlockingP F_FLOAT C12, C34, C56, BO_s, BO_pi, BO_pi2, BO, delij[3]; - d_bo_first[i] = i*maxbo; - const int bo_first_i = d_bo_first[i]; + const int bo_first_i = i * maxbo; int ihb = -1; @@ -1759,8 +1751,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfBlockingP if (cut_hbsq > 0.0) { ihb = paramssing(itype).p_hbond; if (ihb == 1) { - d_hb_first[i] = i*maxhb; - hb_first_i = d_hb_first[i]; + hb_first_i = i * maxhb; } } @@ -1780,9 +1771,6 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfBlockingP int j = d_neighbors(i,jj); j &= NEIGHMASK; - d_bo_first[j] = j*maxbo; - d_hb_first[j] = j*maxhb; - delij[0] = x(j,0) - xtmp; delij[1] = x(j,1) - ytmp; delij[2] = x(j,2) - ztmp; @@ -1848,8 +1836,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfPreview::operator()(TagPairReaxBuildListsHalfPreview 0.0) { ihb = paramssing(itype).p_hbond; if (ihb == 1) { - d_hb_first[i] = i*maxhb; - hb_first_i = d_hb_first[i]; + hb_first_i = i * maxhb; } } @@ -1866,8 +1852,6 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfPreview::build_hb_list(F_FLOAT rsq, int i, int hb_firs d_hb_list[j_index] = j; } else if (j < nlocal && ihb == 2 && jhb == 1) { if (NEIGHFLAG == HALF) { - i_index = d_hb_first[j] + d_hb_num[j]; + i_index = j * maxhb + d_hb_num[j]; d_hb_num[j]++; } else - i_index = d_hb_first[j] + Kokkos::atomic_fetch_add(&d_hb_num[j],1); + i_index = j * maxhb + Kokkos::atomic_fetch_add(&d_hb_num[j],1); - const int ii_index = i_index - d_hb_first[j]; + const int ii_index = i_index - j * maxhb; if (ii_index >= maxhb) d_resize_hb() = MAX(d_resize_hb(),ii_index+1); @@ -1952,16 +1936,16 @@ bool PairReaxFFKokkos::build_bo_list(int bo_first_i, int i, int j, i if (NEIGHFLAG == HALF) { j_index = bo_first_i + d_bo_num[i]; - i_index = d_bo_first[j] + d_bo_num[j]; + i_index = j * maxbo + d_bo_num[j]; d_bo_num[i]++; d_bo_num[j]++; } else { j_index = bo_first_i + Kokkos::atomic_fetch_add(&d_bo_num[i],1); - i_index = d_bo_first[j] + Kokkos::atomic_fetch_add(&d_bo_num[j],1); + i_index = j * maxbo + Kokkos::atomic_fetch_add(&d_bo_num[j],1); } jj_index = j_index - bo_first_i; - ii_index = i_index - d_bo_first[j]; + ii_index = i_index - j * maxbo; bool set_dB_flag = true; @@ -1995,7 +1979,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsFull, const i F_FLOAT dDeltap_self_i[3] = {0.0,0.0,0.0}; F_FLOAT total_bo_i = 0.0; - const int j_start = d_bo_first[i]; + const int j_start = i * maxbo; const int j_end = j_start + d_bo_num[i]; for (int jj = j_start; jj < j_end; jj++) { int j = d_bo_list[jj]; @@ -2110,7 +2094,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxBondOrder2, const int & const int i = d_ilist[ii]; const int itype = type(i); - const int j_start = d_bo_first[i]; + const int j_start = i * maxbo; const int j_end = j_start + d_bo_num[i]; const F_FLOAT val_i = paramssing(itype).valency; @@ -2279,7 +2263,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeMulti1, const in if (imass > 21.0) dfvl = 0.0; else dfvl = 1.0; - const int j_start = d_bo_first[i]; + const int j_start = i * maxbo; const int j_end = j_start + d_bo_num[i]; F_FLOAT sum_ovun1 = 0.0; @@ -2402,7 +2386,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeMulti2 0 || enobondsflag) a_CdDelta[i] += CEunder3; - const int j_start = d_bo_first[i]; + const int j_start = i * maxbo; const int j_end = j_start + d_bo_num[i]; F_FLOAT CdDelta_i = 0.0; @@ -2461,7 +2445,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxCountAngularTorsion::preprocess_torsion(int i, int /*itype*/, tagin const F_FLOAT bo_ij = d_BO(i,j_index); if (bo_ij < thb_cut) continue; - const int l_start = d_bo_first[j]; + const int l_start = j * maxbo; const int l_end = l_start + d_bo_num[j]; for (int kk = j_start; kk < j_end; kk++) { @@ -3340,9 +3324,9 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeHydrogen::operator()(TagPairReaxUpdateBond, const X_FLOAT ytmp = x(i,1); const X_FLOAT ztmp = x(i,2); const tagint itag = tag(i); - const int j_start = d_bo_first[i]; + const int j_start = i * maxbo; const int j_end = j_start + d_bo_num[i]; for (int jj = j_start; jj < j_end; jj++) { @@ -3506,7 +3490,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxUpdateBond, const F_FLOAT Cdbopi_i = d_Cdbopi(i,j_index); const F_FLOAT Cdbopi2_i = d_Cdbopi2(i,j_index); - const int k_start = d_bo_first[j]; + const int k_start = j * maxbo; const int k_end = k_start + d_bo_num[j]; for (int kk = k_start; kk < k_end; kk++) { @@ -3541,7 +3525,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeBond1::operator()(TagPairReaxComputeBond2::operator()(TagPairReaxComputeBond2::calculate_find_bond_item(int ii, int &numbond int nj = 0; if (mask[i] & groupbit) { - const int j_start = d_bo_first[i]; + const int j_start = i * maxbo; const int j_end = j_start + d_bo_num[i]; for (int jj = j_start; jj < j_end; jj++) { int j = d_bo_list[jj]; @@ -4409,7 +4393,7 @@ KOKKOS_INLINE_FUNCTION void PairReaxFFKokkos::operator()(TagPairReaxFindBondSpecies, const int &i) const { int nj = 0; - const int j_start = d_bo_first[i]; + const int j_start = i * maxbo; const int j_end = j_start + d_bo_num[i]; for (int jj = j_start; jj < j_end; jj++) { int j = d_bo_list[jj]; diff --git a/src/KOKKOS/pair_reaxff_kokkos.h b/src/KOKKOS/pair_reaxff_kokkos.h index 5f228ebd19..3902260068 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.h +++ b/src/KOKKOS/pair_reaxff_kokkos.h @@ -470,7 +470,7 @@ class PairReaxFFKokkos : public PairReaxFF { typename AT::t_int_1d_randomread d_ilist; typename AT::t_int_1d_randomread d_numneigh; - typename AT::t_int_1d d_bo_first, d_bo_num, d_bo_list, d_hb_first, d_hb_num, d_hb_list; + typename AT::t_int_1d d_bo_num, d_bo_list, d_hb_num, d_hb_list; DAT::tdual_int_scalar k_resize_bo, k_resize_hb; typename AT::t_int_scalar d_resize_bo, d_resize_hb; From 8e56f37d3da8109fc2c2412305d6ab943f31af40 Mon Sep 17 00:00:00 2001 From: Evan Weinberg Date: Mon, 9 Sep 2024 12:14:44 -0700 Subject: [PATCH 252/355] Removed the variables bo_first_i, hb_first_i from build_bo/hb_list, replacing them with the strided offset calculation --- src/KOKKOS/pair_reaxff_kokkos.cpp | 55 ++++++++++--------------------- src/KOKKOS/pair_reaxff_kokkos.h | 4 +-- 2 files changed, 19 insertions(+), 40 deletions(-) diff --git a/src/KOKKOS/pair_reaxff_kokkos.cpp b/src/KOKKOS/pair_reaxff_kokkos.cpp index a2844c2ff8..acfa858780 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.cpp +++ b/src/KOKKOS/pair_reaxff_kokkos.cpp @@ -1604,17 +1604,10 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfBlocking< F_FLOAT dDeltap_self_i[3] = {0.0,0.0,0.0}; F_FLOAT total_bo_i = 0.0; - const int bo_first_i = i * maxbo; - int ihb = -1; - int hb_first_i; - if (cut_hbsq > 0.0) { + if (cut_hbsq > 0.0) ihb = paramssing(itype).p_hbond; - if (ihb == 1) { - hb_first_i = i * maxhb; - } - } int nnz; blocking_t selected_jj[blocksize]; @@ -1656,7 +1649,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfBlocking< const F_FLOAT rsq = delij[0]*delij[0] + delij[1]*delij[1] + delij[2]*delij[2]; // hbond list - build_hb_list(rsq, i, hb_first_i, ihb, j, jtype); + build_hb_list(rsq, i, ihb, j, jtype); if (rsq > cut_bosq) continue; @@ -1675,7 +1668,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfBlocking< int ii_index = -1; int jj_index = -1; - if (build_bo_list(bo_first_i, i, j, ii_index, jj_index)) { + if (build_bo_list(i, j, ii_index, jj_index)) { // from BondOrder1 @@ -1743,17 +1736,10 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfBlockingP F_FLOAT C12, C34, C56, BO_s, BO_pi, BO_pi2, BO, delij[3]; - const int bo_first_i = i * maxbo; - int ihb = -1; - int hb_first_i; - if (cut_hbsq > 0.0) { + if (cut_hbsq > 0.0) ihb = paramssing(itype).p_hbond; - if (ihb == 1) { - hb_first_i = i * maxhb; - } - } int nnz; blocking_t selected_jj[blocksize]; @@ -1796,7 +1782,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfBlockingP const F_FLOAT rsq = delij[0]*delij[0] + delij[1]*delij[1] + delij[2]*delij[2]; // hbond list - build_hb_list(rsq, i, hb_first_i, ihb, j, jtype); + build_hb_list(rsq, i, ihb, j, jtype); if (rsq > cut_bosq) continue; @@ -1815,7 +1801,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfBlockingP int ii_index = -1; int jj_index = -1; - build_bo_list(bo_first_i, i, j, ii_index, jj_index); + build_bo_list(i, j, ii_index, jj_index); } } } @@ -1836,17 +1822,10 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfPreview 0.0) { + if (cut_hbsq > 0.0) ihb = paramssing(itype).p_hbond; - if (ihb == 1) { - hb_first_i = i * maxhb; - } - } for (int jj = 0; jj < jnum; jj++) { int j = d_neighbors(i,jj); @@ -1860,7 +1839,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfPreview(rsq, i, hb_first_i, ihb, j, jtype); + build_hb_list(rsq, i, ihb, j, jtype); if (rsq > cut_bosq) continue; @@ -1880,7 +1859,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfPreview(bo_first_i, i, j, ii_index, jj_index); + build_bo_list(i, j, ii_index, jj_index); } } @@ -1889,7 +1868,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfPreview template KOKKOS_INLINE_FUNCTION -void PairReaxFFKokkos::build_hb_list(F_FLOAT rsq, int i, int hb_first_i, int ihb, int j, int jtype) const { +void PairReaxFFKokkos::build_hb_list(F_FLOAT rsq, int i, int ihb, int j, int jtype) const { int i_index, j_index; int jhb = -1; @@ -1897,12 +1876,12 @@ void PairReaxFFKokkos::build_hb_list(F_FLOAT rsq, int i, int hb_firs jhb = paramssing(jtype).p_hbond; if (ihb == 1 && jhb == 2) { if (NEIGHFLAG == HALF) { - j_index = hb_first_i + d_hb_num[i]; + j_index = i * maxhb + d_hb_num[i]; d_hb_num[i]++; } else - j_index = hb_first_i + Kokkos::atomic_fetch_add(&d_hb_num[i],1); + j_index = i * maxhb + Kokkos::atomic_fetch_add(&d_hb_num[i],1); - const int jj_index = j_index - hb_first_i; + const int jj_index = j_index - i * maxhb; if (jj_index >= maxhb) d_resize_hb() = MAX(d_resize_hb(),jj_index+1); @@ -1931,20 +1910,20 @@ void PairReaxFFKokkos::build_hb_list(F_FLOAT rsq, int i, int hb_firs template template KOKKOS_INLINE_FUNCTION -bool PairReaxFFKokkos::build_bo_list(int bo_first_i, int i, int j, int& ii_index, int& jj_index) const { +bool PairReaxFFKokkos::build_bo_list(int i, int j, int& ii_index, int& jj_index) const { int i_index, j_index; if (NEIGHFLAG == HALF) { - j_index = bo_first_i + d_bo_num[i]; + j_index = i * maxbo + d_bo_num[i]; i_index = j * maxbo + d_bo_num[j]; d_bo_num[i]++; d_bo_num[j]++; } else { - j_index = bo_first_i + Kokkos::atomic_fetch_add(&d_bo_num[i],1); + j_index = i * maxbo + Kokkos::atomic_fetch_add(&d_bo_num[i],1); i_index = j * maxbo + Kokkos::atomic_fetch_add(&d_bo_num[j],1); } - jj_index = j_index - bo_first_i; + jj_index = j_index - i * maxbo; ii_index = i_index - j * maxbo; bool set_dB_flag = true; diff --git a/src/KOKKOS/pair_reaxff_kokkos.h b/src/KOKKOS/pair_reaxff_kokkos.h index 3902260068..450d9c57a0 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.h +++ b/src/KOKKOS/pair_reaxff_kokkos.h @@ -178,14 +178,14 @@ class PairReaxFFKokkos : public PairReaxFF { // TagPairReaxBuildListsHalfBlocking, HalfBlockingPreview, HalfPreview template KOKKOS_INLINE_FUNCTION - void build_hb_list(F_FLOAT, int, int, int, int, int) const; + void build_hb_list(F_FLOAT, int, int, int, int) const; // Isolated function that builds the bond order list, reused across // TagPairReaxBuildListsHalfBlocking, HalfBlockingPreview, HalfPreview // Returns if we need to populate d_d* functions or not template KOKKOS_INLINE_FUNCTION - bool build_bo_list(int, int, int, int&, int&) const; + bool build_bo_list(int, int, int&, int&) const; KOKKOS_INLINE_FUNCTION void operator()(TagPairReaxBuildListsFull, const int&) const; From fced73ffd779c83f1e340aa14a49672fac123206 Mon Sep 17 00:00:00 2001 From: Evan Weinberg Date: Mon, 9 Sep 2024 12:18:10 -0700 Subject: [PATCH 253/355] Converted d_bo_list and d_hb_list to 2-d Views, removed integer overflow checks --- src/KOKKOS/pair_reaxff_kokkos.cpp | 297 ++++++++++++------------------ src/KOKKOS/pair_reaxff_kokkos.h | 9 +- 2 files changed, 127 insertions(+), 179 deletions(-) diff --git a/src/KOKKOS/pair_reaxff_kokkos.cpp b/src/KOKKOS/pair_reaxff_kokkos.cpp index acfa858780..44e72c53c1 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.cpp +++ b/src/KOKKOS/pair_reaxff_kokkos.cpp @@ -1503,18 +1503,10 @@ void PairReaxFFKokkos::allocate_array() if (cut_hbsq > 0.0) { MemKK::realloc_kokkos(d_hb_num,"reaxff/kk:hb_num",nmax); - - if (((bigint) nmax*maxhb) > MAXSMALLINT) - error->one(FLERR,"Too many hydrogen bonds in pair reaxff"); - - MemKK::realloc_kokkos(d_hb_list,"reaxff/kk:hb_list",nmax*maxhb); + MemKK::realloc_kokkos(d_hb_list,"reaxff/kk:hb_list", nmax, maxhb); } MemKK::realloc_kokkos(d_bo_num,"reaxff/kk:bo_num",nmax); - - if (((bigint) nmax*maxbo) > MAXSMALLINT) - error->one(FLERR,"Too many bonds in pair reaxff"); - - MemKK::realloc_kokkos(d_bo_list,"reaxff/kk:bo_list",nmax*maxbo); + MemKK::realloc_kokkos(d_bo_list,"reaxff/kk:bo_list", nmax, maxbo); MemKK::realloc_kokkos(d_BO,"reaxff/kk:BO",nmax,maxbo); MemKK::realloc_kokkos(d_BO_s,"reaxff/kk:BO",nmax,maxbo); @@ -1666,23 +1658,23 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfBlocking< BO = BO_s + BO_pi + BO_pi2; if (BO < bo_cut) continue; - int ii_index = -1; - int jj_index = -1; - if (build_bo_list(i, j, ii_index, jj_index)) { + int i_index = -1; + int j_index = -1; + if (build_bo_list(i, j, i_index, j_index)) { // from BondOrder1 - d_BO(i,jj_index) = BO; - d_BO_s(i,jj_index) = BO_s; + d_BO(i,j_index) = BO; + d_BO_s(i,j_index) = BO_s; - d_BO(j,ii_index) = BO; - d_BO_s(j,ii_index) = BO_s; + d_BO(j,i_index) = BO; + d_BO_s(j,i_index) = BO_s; - d_BO_pi(j,ii_index) = BO_pi; - d_BO_pi2(j,ii_index) = BO_pi2; + d_BO_pi(j,i_index) = BO_pi; + d_BO_pi2(j,i_index) = BO_pi2; - d_BO_pi(i,jj_index) = BO_pi; - d_BO_pi2(i,jj_index) = BO_pi2; + d_BO_pi(i,j_index) = BO_pi; + d_BO_pi2(i,j_index) = BO_pi2; F_FLOAT Cln_BOp_s = p_bo2 * C12 / rij / rij; F_FLOAT Cln_BOp_pi = p_bo4 * C34 / rij / rij; @@ -1695,18 +1687,18 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfBlocking< for (int d = 0; d < 3; d++) dDeltap_self_i[d] += dBOp_i[d]; for (int d = 0; d < 3; d++) a_dDeltap_self(j,d) += -dBOp_i[d]; - d_dln_BOp_pi(i,jj_index) = -(BO_pi*Cln_BOp_pi); - d_dln_BOp_pi(j,ii_index) = -(BO_pi*Cln_BOp_pi); + d_dln_BOp_pi(i,j_index) = -(BO_pi*Cln_BOp_pi); + d_dln_BOp_pi(j,i_index) = -(BO_pi*Cln_BOp_pi); - d_dln_BOp_pi2(i,jj_index) = -(BO_pi2*Cln_BOp_pi2); - d_dln_BOp_pi2(j,ii_index) = -(BO_pi2*Cln_BOp_pi2); + d_dln_BOp_pi2(i,j_index) = -(BO_pi2*Cln_BOp_pi2); + d_dln_BOp_pi2(j,i_index) = -(BO_pi2*Cln_BOp_pi2); - d_dBOp(i,jj_index) = -(BO_s*Cln_BOp_s+BO_pi*Cln_BOp_pi+BO_pi2*Cln_BOp_pi2); - d_dBOp(j,ii_index) = -(BO_s*Cln_BOp_s+BO_pi*Cln_BOp_pi+BO_pi2*Cln_BOp_pi2); - d_BO(i,jj_index) = BO - bo_cut; - d_BO(j,ii_index) = BO - bo_cut; - d_BO_s(i,jj_index) = BO_s - bo_cut; - d_BO_s(j,ii_index) = BO_s - bo_cut; + d_dBOp(i,j_index) = -(BO_s*Cln_BOp_s+BO_pi*Cln_BOp_pi+BO_pi2*Cln_BOp_pi2); + d_dBOp(j,i_index) = -(BO_s*Cln_BOp_s+BO_pi*Cln_BOp_pi+BO_pi2*Cln_BOp_pi2); + d_BO(i,j_index) = BO - bo_cut; + d_BO(j,i_index) = BO - bo_cut; + d_BO_s(i,j_index) = BO_s - bo_cut; + d_BO_s(j,i_index) = BO_s - bo_cut; total_bo_i += (BO - bo_cut); a_total_bo[j] += (BO - bo_cut); } @@ -1799,9 +1791,9 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfBlockingP BO = BO_s + BO_pi + BO_pi2; if (BO < bo_cut) continue; - int ii_index = -1; - int jj_index = -1; - build_bo_list(i, j, ii_index, jj_index); + int i_index = -1; + int j_index = -1; + build_bo_list(i, j, i_index, j_index); } } } @@ -1856,10 +1848,10 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsHalfPreview(i, j, ii_index, jj_index); + build_bo_list(i, j, i_index, j_index); } } @@ -1876,30 +1868,26 @@ void PairReaxFFKokkos::build_hb_list(F_FLOAT rsq, int i, int ihb, in jhb = paramssing(jtype).p_hbond; if (ihb == 1 && jhb == 2) { if (NEIGHFLAG == HALF) { - j_index = i * maxhb + d_hb_num[i]; + j_index = d_hb_num[i]; d_hb_num[i]++; } else - j_index = i * maxhb + Kokkos::atomic_fetch_add(&d_hb_num[i],1); + j_index = Kokkos::atomic_fetch_add(&d_hb_num[i],1); - const int jj_index = j_index - i * maxhb; - - if (jj_index >= maxhb) - d_resize_hb() = MAX(d_resize_hb(),jj_index+1); + if (j_index >= maxhb) + d_resize_hb() = MAX(d_resize_hb(), j_index+1); else - d_hb_list[j_index] = j; + d_hb_list(i, j_index) = j; } else if (j < nlocal && ihb == 2 && jhb == 1) { if (NEIGHFLAG == HALF) { - i_index = j * maxhb + d_hb_num[j]; + i_index = d_hb_num[j]; d_hb_num[j]++; } else - i_index = j * maxhb + Kokkos::atomic_fetch_add(&d_hb_num[j],1); + i_index = Kokkos::atomic_fetch_add(&d_hb_num[j],1); - const int ii_index = i_index - j * maxhb; - - if (ii_index >= maxhb) - d_resize_hb() = MAX(d_resize_hb(),ii_index+1); + if (i_index >= maxhb) + d_resize_hb() = MAX(d_resize_hb(), i_index+1); else - d_hb_list[i_index] = i; + d_hb_list(j, i_index) = i; } } @@ -1910,31 +1898,27 @@ void PairReaxFFKokkos::build_hb_list(F_FLOAT rsq, int i, int ihb, in template template KOKKOS_INLINE_FUNCTION -bool PairReaxFFKokkos::build_bo_list(int i, int j, int& ii_index, int& jj_index) const { - int i_index, j_index; +bool PairReaxFFKokkos::build_bo_list(int i, int j, int& i_index, int& j_index) const { if (NEIGHFLAG == HALF) { - j_index = i * maxbo + d_bo_num[i]; - i_index = j * maxbo + d_bo_num[j]; + j_index = d_bo_num[i]; + i_index = d_bo_num[j]; d_bo_num[i]++; d_bo_num[j]++; } else { - j_index = i * maxbo + Kokkos::atomic_fetch_add(&d_bo_num[i],1); - i_index = j * maxbo + Kokkos::atomic_fetch_add(&d_bo_num[j],1); + j_index = Kokkos::atomic_fetch_add(&d_bo_num[i],1); + i_index = Kokkos::atomic_fetch_add(&d_bo_num[j],1); } - jj_index = j_index - i * maxbo; - ii_index = i_index - j * maxbo; - bool set_dB_flag = true; - if (jj_index >= maxbo || ii_index >= maxbo) { - const int max_val = MAX(ii_index+1,jj_index+1); + if (j_index >= maxbo || i_index >= maxbo) { + const int max_val = MAX(i_index + 1, j_index + 1); d_resize_bo() = MAX(d_resize_bo(),max_val); set_dB_flag = false; } else { - d_bo_list[j_index] = j; - d_bo_list[i_index] = i; + d_bo_list(i, j_index) = j; + d_bo_list(j, i_index) = i; set_dB_flag = true; } @@ -1958,13 +1942,11 @@ void PairReaxFFKokkos::operator()(TagPairReaxBuildListsFull, const i F_FLOAT dDeltap_self_i[3] = {0.0,0.0,0.0}; F_FLOAT total_bo_i = 0.0; - const int j_start = i * maxbo; - const int j_end = j_start + d_bo_num[i]; - for (int jj = j_start; jj < j_end; jj++) { - int j = d_bo_list[jj]; + const int jnum = d_bo_num[i]; + for (int j_index = 0; j_index < jnum; j_index++) { + int j = d_bo_list(i, j_index); j &= NEIGHMASK; const int jtype = type(j); - const int j_index = jj - j_start; delij[0] = x(j,0) - xtmp; delij[1] = x(j,1) - ytmp; delij[2] = x(j,2) - ztmp; @@ -2073,20 +2055,18 @@ void PairReaxFFKokkos::operator()(TagPairReaxBondOrder2, const int & const int i = d_ilist[ii]; const int itype = type(i); - const int j_start = i * maxbo; - const int j_end = j_start + d_bo_num[i]; + const int jnum = d_bo_num[i]; const F_FLOAT val_i = paramssing(itype).valency; d_total_bo[i] = 0.0; F_FLOAT total_bo = 0.0; - for (int jj = j_start; jj < j_end; jj++) { - int j = d_bo_list[jj]; + for (int j_index = 0; j_index < jnum; j_index++) { + int j = d_bo_list(i, j_index); j &= NEIGHMASK; const int jtype = type(j); - const int j_index = jj - j_start; - const int i_index = maxbo+j_index; + const int i_index = maxbo + j_index; // this line seems confusing... // calculate corrected BO and total bond order @@ -2242,20 +2222,18 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeMulti1, const in if (imass > 21.0) dfvl = 0.0; else dfvl = 1.0; - const int j_start = i * maxbo; - const int j_end = j_start + d_bo_num[i]; + const int jnum = d_bo_num[i]; F_FLOAT sum_ovun1 = 0.0; F_FLOAT sum_ovun2 = 0.0; - for (int jj = j_start; jj < j_end; jj++) { - int j = d_bo_list[jj]; + for (int j_index = 0; j_index < jnum; j_index++) { + int j = d_bo_list(i, j_index); j &= NEIGHMASK; const int jtype = type(j); - const int j_index = jj - j_start; sum_ovun1 += paramstwbp(itype,jtype).p_ovun1 * paramstwbp(itype,jtype).De_s * d_BO(i,j_index); - sum_ovun2 += (d_Delta[j] - dfvl * d_Delta_lp_temp[j]) * (d_BO_pi(i,j_index) + d_BO_pi2(i,j_index)); + sum_ovun2 += (d_Delta[j] - dfvl * d_Delta_lp_temp[j]) * (d_BO_pi(i, j_index) + d_BO_pi2(i,j_index)); } d_sum_ovun(i,1) += sum_ovun1; d_sum_ovun(i,2) += sum_ovun2; @@ -2365,16 +2343,14 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeMulti2 0 || enobondsflag) a_CdDelta[i] += CEunder3; - const int j_start = i * maxbo; - const int j_end = j_start + d_bo_num[i]; + const int jnum = d_bo_num[i]; F_FLOAT CdDelta_i = 0.0; - for (int jj = j_start; jj < j_end; jj++) { - int j = d_bo_list[jj]; + for (int j_index = 0; j_index < jnum; j_index++) { + int j = d_bo_list(i, j_index); j &= NEIGHMASK; const int jtype = type(j); const F_FLOAT jmass = paramssing(jtype).mass; - const int j_index = jj - j_start; const F_FLOAT De_s = paramstwbp(itype,jtype).De_s; // multibody lone pair: correction for C2 @@ -2424,24 +2400,23 @@ void PairReaxFFKokkos::operator()(TagPairReaxCountAngularTorsion(i, itype, j_start, j_end, location_angular); + int count_angular = preprocess_angular(i, itype, jnum, location_angular); location_angular = Kokkos::atomic_fetch_add(&d_count_angular_torsion(0), count_angular); if (POPULATE) { // Fill buffer for `i` - preprocess_angular(i, itype, j_start, j_end, location_angular); + preprocess_angular(i, itype, jnum, location_angular); } // Torsion @@ -2453,12 +2428,12 @@ void PairReaxFFKokkos::operator()(TagPairReaxCountAngularTorsion(i, itype, itag, xtmp, ytmp, ztmp, j_start, j_end, location_torsion); + int count_torsion = preprocess_torsion(i, itype, itag, xtmp, ytmp, ztmp, jnum, location_torsion); location_torsion = Kokkos::atomic_fetch_add(&d_count_angular_torsion(1), count_torsion); if (POPULATE) { // Fill buffer for `i` - preprocess_torsion(i, itype, itag, xtmp, ytmp, ztmp, j_start, j_end, location_torsion); + preprocess_torsion(i, itype, itag, xtmp, ytmp, ztmp, jnum, location_torsion); } } @@ -2467,7 +2442,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxCountAngularTorsion KOKKOS_INLINE_FUNCTION -void PairReaxFFKokkos::compute_angular_sbo(int i, int itype, int j_start, int j_end) const { +void PairReaxFFKokkos::compute_angular_sbo(int i, int itype, int jnum) const { F_FLOAT SBO2, CSBO2, dSBO1, dSBO2; @@ -2477,8 +2452,7 @@ void PairReaxFFKokkos::compute_angular_sbo(int i, int itype, int j_s F_FLOAT SBOp = 0.0; F_FLOAT prod_SBO = 1.0; - for (int jj = j_start; jj < j_end; jj++) { - const int j_index = jj - j_start; + for (int j_index = 0; j_index < jnum; j_index++) { const F_FLOAT bo_ij = d_BO(i,j_index); SBOp += (d_BO_pi(i,j_index) + d_BO_pi2(i,j_index)); @@ -2531,14 +2505,13 @@ void PairReaxFFKokkos::compute_angular_sbo(int i, int itype, int j_s template template KOKKOS_INLINE_FUNCTION -int PairReaxFFKokkos::preprocess_angular(int i, int itype, int j_start, int j_end, int location_angular) const { +int PairReaxFFKokkos::preprocess_angular(int i, int itype, int jnum, int location_angular) const { int count_angular = 0; - for (int jj = j_start; jj < j_end; jj++) { - int j = d_bo_list[jj]; + for (int j_index = 0; j_index < jnum; j_index++) { + int j = d_bo_list(i, j_index); j &= NEIGHMASK; - const int j_index = jj - j_start; const F_FLOAT bo_ij = d_BO(i,j_index); if (bo_ij <= thb_cut) continue; @@ -2547,13 +2520,12 @@ int PairReaxFFKokkos::preprocess_angular(int i, int itype, int j_sta const int i_index = maxbo + j_index; const int jtype = type(j); - for (int kk = jj+1; kk < j_end; kk++) { + for (int k_index = j_index + 1; k_index < jnum; k_index++) { //for (int kk = j_start; kk < j_end; kk++) { - int k = d_bo_list[kk]; + int k = d_bo_list(i, k_index); k &= NEIGHMASK; if (k == j) continue; - const int k_index = kk - j_start; const F_FLOAT bo_ik = d_BO(i,k_index); if (bo_ij <= thb_cut || bo_ik <= thb_cut || bo_ij * bo_ik <= thb_cutsq) continue; @@ -2571,14 +2543,14 @@ int PairReaxFFKokkos::preprocess_angular(int i, int itype, int j_sta pack.i0 = i; pack.i1 = j; pack.i2 = k; - pack.i3 = j_start; + pack.i3 = jnum; d_angular_pack(location_angular, 0) = pack; // Second pack stores i_index, j_index, k_index, and j_end pack.i0 = i_index; pack.i1 = j_index; pack.i2 = k_index; - pack.i3 = j_end; + // i3 is unused d_angular_pack(location_angular, 1) = pack; location_angular++; @@ -2597,17 +2569,16 @@ template template KOKKOS_INLINE_FUNCTION int PairReaxFFKokkos::preprocess_torsion(int i, int /*itype*/, tagint itag, - F_FLOAT xtmp, F_FLOAT ytmp, F_FLOAT ztmp, int j_start, int j_end, int location_torsion) const { + F_FLOAT xtmp, F_FLOAT ytmp, F_FLOAT ztmp, int jknum, int location_torsion) const { // in reaxff_torsion_angles: j = i, k = j, i = k; int count_torsion = 0; - for (int jj = j_start; jj < j_end; jj++) { - int j = d_bo_list[jj]; + for (int j_index = 0; j_index < jknum; j_index++) { + int j = d_bo_list(i, j_index); j &= NEIGHMASK; const tagint jtag = tag(j); - const int j_index = jj - j_start; // skip half of the interactions if (itag > jtag) { @@ -2623,23 +2594,20 @@ int PairReaxFFKokkos::preprocess_torsion(int i, int /*itype*/, tagin const F_FLOAT bo_ij = d_BO(i,j_index); if (bo_ij < thb_cut) continue; - const int l_start = j * maxbo; - const int l_end = l_start + d_bo_num[j]; + const int lnum = d_bo_num[j]; - for (int kk = j_start; kk < j_end; kk++) { - int k = d_bo_list[kk]; + for (int k_index = 0; k_index < jknum; k_index++) { + int k = d_bo_list(i, k_index); k &= NEIGHMASK; if (k == j) continue; - const int k_index = kk - j_start; const F_FLOAT bo_ik = d_BO(i,k_index); if (bo_ik < thb_cut) continue; - for (int ll = l_start; ll < l_end; ll++) { - int l = d_bo_list[ll]; + for (int l_index = 0; l_index < lnum; l_index++) { + int l = d_bo_list(j, l_index); l &= NEIGHMASK; if (l == i) continue; - const int l_index = ll - l_start; const F_FLOAT bo_jl = d_BO(j,l_index); if (l == k || bo_jl < thb_cut || bo_ij*bo_ik*bo_jl < thb_cut) continue; @@ -2721,13 +2689,13 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeAngularPreproces const int i = pack.i0; const int j = pack.i1; const int k = pack.i2; - const int j_start = pack.i3; + const int jnum = pack.i3; pack = d_angular_pack(apack, 1); const int i_index = pack.i0; const int j_index = pack.i1; const int k_index = pack.i2; - const int j_end = pack.i3; + // i3 is unused const int itype = type(i); const X_FLOAT xtmp = x(i,0); @@ -2885,9 +2853,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeAngularPreproces CdDelta_j += CEcoa4; a_CdDelta[k] += CEcoa5; - for (int ll = j_start; ll < j_end; ll++) { - const int l_index = ll - j_start; - + for (int l_index = 0; l_index < jnum; l_index++) { temp_bo_jt = d_BO(i,l_index); temp = temp_bo_jt * temp_bo_jt * temp_bo_jt; pBOjt7 = temp * temp * temp_bo_jt; @@ -3303,21 +3269,18 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeHydrogen= HB_THRESHOLD) { - hblist[top] = jj; + hblist[top] = j_index; top ++; } } @@ -3325,8 +3288,8 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeHydrogen::operator()(TagPairReaxComputeHydrogen::operator()(TagPairReaxUpdateBond, const X_FLOAT ytmp = x(i,1); const X_FLOAT ztmp = x(i,2); const tagint itag = tag(i); - const int j_start = i * maxbo; - const int j_end = j_start + d_bo_num[i]; + const int jnum = d_bo_num[i]; - for (int jj = j_start; jj < j_end; jj++) { - int j = d_bo_list[jj]; + for (int j_index = 0; j_index < jnum; j_index++) { + int j = d_bo_list(i, j_index); j &= NEIGHMASK; const tagint jtag = tag(j); @@ -3464,19 +3425,16 @@ void PairReaxFFKokkos::operator()(TagPairReaxUpdateBond, if (!flag) continue; - const int j_index = jj - j_start; const F_FLOAT Cdbo_i = d_Cdbo(i,j_index); const F_FLOAT Cdbopi_i = d_Cdbopi(i,j_index); const F_FLOAT Cdbopi2_i = d_Cdbopi2(i,j_index); - const int k_start = j * maxbo; - const int k_end = k_start + d_bo_num[j]; + const int knum = d_bo_num[j]; - for (int kk = k_start; kk < k_end; kk++) { - int k = d_bo_list[kk]; + for (int k_index = 0; k_index < knum; k_index++) { + int k = d_bo_list(j, k_index); k &= NEIGHMASK; if (k != i) continue; - const int k_index = kk - k_start; a_Cdbo(j,k_index) += Cdbo_i; a_Cdbopi(j,k_index) += Cdbopi_i; @@ -3504,13 +3462,12 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeBond1::operator()(TagPairReaxComputeBond1::operator()(TagPairReaxComputeBond2::operator()(TagPairReaxComputeBond2::operator()(TagPairReaxComputeBond2::operator()(TagPairReaxComputeBond2::calculate_find_bond_item(int ii, int &numbond int nj = 0; if (mask[i] & groupbit) { - const int j_start = i * maxbo; - const int j_end = j_start + d_bo_num[i]; - for (int jj = j_start; jj < j_end; jj++) { - int j = d_bo_list[jj]; + const int jnum = d_bo_num[i]; + for (int j_index = 0; j_index < jnum; j_index++) { + int j = d_bo_list(i, j_index); j &= NEIGHMASK; if (mask[j] & groupbit) { const tagint jtag = tag[j]; - const int j_index = jj - j_start; - double bo_tmp = d_BO(i,j_index); + double bo_tmp = d_BO(i, j_index); if (bo_tmp > bo_cut_bond) { d_neighid(i,nj) = jtag; @@ -4372,15 +4321,13 @@ KOKKOS_INLINE_FUNCTION void PairReaxFFKokkos::operator()(TagPairReaxFindBondSpecies, const int &i) const { int nj = 0; - const int j_start = i * maxbo; - const int j_end = j_start + d_bo_num[i]; - for (int jj = j_start; jj < j_end; jj++) { - int j = d_bo_list[jj]; + const int jnum = d_bo_num[i]; + for (int j_index = 0; j_index < jnum; j_index++) { + int j = d_bo_list(i, j_index); j &= NEIGHMASK; if (j < i) continue; - const int j_index = jj - j_start; - double bo_tmp = d_BO(i,j_index); + double bo_tmp = d_BO(i, j_index); if (bo_tmp >= 0.10) { // Why is this a hardcoded value? k_tmpid.view()(i,nj) = j; diff --git a/src/KOKKOS/pair_reaxff_kokkos.h b/src/KOKKOS/pair_reaxff_kokkos.h index 450d9c57a0..8ffabefafc 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.h +++ b/src/KOKKOS/pair_reaxff_kokkos.h @@ -245,17 +245,17 @@ class PairReaxFFKokkos : public PairReaxFF { // Abstraction for computing SBSO2, CSBO2, dSBO1, dsBO2 KOKKOS_INLINE_FUNCTION - void compute_angular_sbo(int, int, int, int) const; + void compute_angular_sbo(int, int, int) const; // Abstraction for counting and populating angular intermediates template KOKKOS_INLINE_FUNCTION - int preprocess_angular(int, int, int, int, int) const; + int preprocess_angular(int, int, int, int) const; // Abstraction for counting and populating torsion intermediated template KOKKOS_INLINE_FUNCTION - int preprocess_torsion(int, int, tagint, F_FLOAT, F_FLOAT, F_FLOAT, int, int, int) const; + int preprocess_torsion(int, int, tagint, F_FLOAT, F_FLOAT, F_FLOAT, int, int) const; template KOKKOS_INLINE_FUNCTION @@ -470,7 +470,8 @@ class PairReaxFFKokkos : public PairReaxFF { typename AT::t_int_1d_randomread d_ilist; typename AT::t_int_1d_randomread d_numneigh; - typename AT::t_int_1d d_bo_num, d_bo_list, d_hb_num, d_hb_list; + typename AT::t_int_1d d_bo_num, d_hb_num; + typename AT::t_int_2d d_bo_list, d_hb_list; DAT::tdual_int_scalar k_resize_bo, k_resize_hb; typename AT::t_int_scalar d_resize_bo, d_resize_hb; From 8b9e2544f03a9c05f7dc82228df7124c95c07bcc Mon Sep 17 00:00:00 2001 From: Evan Weinberg Date: Mon, 9 Sep 2024 12:23:33 -0700 Subject: [PATCH 254/355] Verified that some unused data gets written to extra space in Cdbo, Cdbopi, Cdbopi2 via moving them to 3-d Views --- src/KOKKOS/kokkos_type.h | 17 +++++ src/KOKKOS/pair_reaxff_kokkos.cpp | 101 ++++++++++++++++-------------- src/KOKKOS/pair_reaxff_kokkos.h | 3 +- 3 files changed, 72 insertions(+), 49 deletions(-) diff --git a/src/KOKKOS/kokkos_type.h b/src/KOKKOS/kokkos_type.h index e9061dd7a3..b510134a77 100644 --- a/src/KOKKOS/kokkos_type.h +++ b/src/KOKKOS/kokkos_type.h @@ -863,6 +863,15 @@ typedef tdual_ffloat_2d_dl::t_dev_um t_ffloat_2d_um_dl; typedef tdual_ffloat_2d_dl::t_dev_const_um t_ffloat_2d_const_um_dl; typedef tdual_ffloat_2d_dl::t_dev_const_randomread t_ffloat_2d_randomread_dl; +// 3d F_FLOAT array n*m + +typedef Kokkos::DualView tdual_ffloat_3d; +typedef tdual_ffloat_3d::t_dev t_ffloat_3d; +typedef tdual_ffloat_3d::t_dev_const t_ffloat_3d_const; +typedef tdual_ffloat_3d::t_dev_um t_ffloat_3d_um; +typedef tdual_ffloat_3d::t_dev_const_um t_ffloat_3d_const_um; +typedef tdual_ffloat_3d::t_dev_const_randomread t_ffloat_3d_randomread; + //2d F_FLOAT array n*3 typedef Kokkos::DualView tdual_f_array; @@ -1169,6 +1178,14 @@ typedef tdual_ffloat_2d_dl::t_host_um t_ffloat_2d_um_dl; typedef tdual_ffloat_2d_dl::t_host_const_um t_ffloat_2d_const_um_dl; typedef tdual_ffloat_2d_dl::t_host_const_randomread t_ffloat_2d_randomread_dl; +// 3d F_FLOAT array n*m +typedef Kokkos::DualView tdual_ffloat_3d; +typedef tdual_ffloat_3d::t_host t_ffloat_3d; +typedef tdual_ffloat_3d::t_host_const t_ffloat_3d_const; +typedef tdual_ffloat_3d::t_host_um t_ffloat_3d_um; +typedef tdual_ffloat_3d::t_host_const_um t_ffloat_3d_const_um; +typedef tdual_ffloat_3d::t_host_const_randomread t_ffloat_3d_randomread; + //2d F_FLOAT array n*3 typedef Kokkos::DualView tdual_f_array; //typedef Kokkos::DualView tdual_f_array; diff --git a/src/KOKKOS/pair_reaxff_kokkos.cpp b/src/KOKKOS/pair_reaxff_kokkos.cpp index 44e72c53c1..a448505ef6 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.cpp +++ b/src/KOKKOS/pair_reaxff_kokkos.cpp @@ -1537,9 +1537,9 @@ void PairReaxFFKokkos::allocate_array() MemKK::realloc_kokkos(d_Deltap,"reaxff/kk:Deltap",nmax); MemKK::realloc_kokkos(d_total_bo,"reaxff/kk:total_bo",nmax); - MemKK::realloc_kokkos(d_Cdbo,"reaxff/kk:Cdbo",nmax,3*maxbo); - MemKK::realloc_kokkos(d_Cdbopi,"reaxff/kk:Cdbopi",nmax,3*maxbo); - MemKK::realloc_kokkos(d_Cdbopi2,"reaxff/kk:Cdbopi2",nmax,3*maxbo); + MemKK::realloc_kokkos(d_Cdbo,"reaxff/kk:Cdbo",nmax,maxbo,3); + MemKK::realloc_kokkos(d_Cdbopi,"reaxff/kk:Cdbopi",nmax,maxbo,3); + MemKK::realloc_kokkos(d_Cdbopi2,"reaxff/kk:Cdbopi2",nmax,maxbo,3); MemKK::realloc_kokkos(d_Delta,"reaxff/kk:Delta",nmax); MemKK::realloc_kokkos(d_Delta_boc,"reaxff/kk:Delta_boc",nmax); @@ -2066,7 +2066,9 @@ void PairReaxFFKokkos::operator()(TagPairReaxBondOrder2, const int & int j = d_bo_list(i, j_index); j &= NEIGHMASK; const int jtype = type(j); - const int i_index = maxbo + j_index; // this line seems confusing... + //const int i_index = maxbo + j_index; // this line seems confusing... + const int i_index = j_index; // ?? + // calculate corrected BO and total bond order @@ -2161,12 +2163,13 @@ void PairReaxFFKokkos::operator()(TagPairReaxBondOrder2, const int & total_bo += d_BO(i,j_index); - d_Cdbo(i,j_index) = 0.0; - d_Cdbopi(i,j_index) = 0.0; - d_Cdbopi2(i,j_index) = 0.0; - d_Cdbo(j,i_index) = 0.0; - d_Cdbopi(j,i_index) = 0.0; - d_Cdbopi2(j,i_index) = 0.0; + // debugging whether or not the values that go into (*,*,1) are relevant + d_Cdbo(i,j_index,0) = 0.0; + d_Cdbopi(i,j_index,0) = 0.0; + d_Cdbopi2(i,j_index,0) = 0.0; + d_Cdbo(j,i_index,1) = 0.0; + d_Cdbopi(j,i_index,1) = 0.0; + d_Cdbopi2(j,i_index,1) = 0.0; d_CdDelta[j] = 0.0; } @@ -2361,7 +2364,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeMulti2::operator()(TagPairReaxComputeMulti2::preprocess_angular(int i, int itype, int jnum, if (bo_ij <= thb_cut) continue; if (i >= nlocal && j >= nlocal) continue; - const int i_index = maxbo + j_index; + // const int i_index = maxbo + j_index; ?? + const int i_index = j_index; // plus a shift? const int jtype = type(j); for (int k_index = j_index + 1; k_index < jnum; k_index++) { @@ -2647,9 +2651,9 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeAngularPreproces auto v_f = ScatterViewHelper,decltype(dup_f),decltype(ndup_f)>::get(dup_f,ndup_f); auto a_f = v_f.template access>(); - Kokkos::View::value,Kokkos::MemoryTraits::value>> a_Cdbo = d_Cdbo; - Kokkos::View::value,Kokkos::MemoryTraits::value>> a_Cdbopi = d_Cdbopi; - Kokkos::View::value,Kokkos::MemoryTraits::value>> a_Cdbopi2 = d_Cdbopi2; + Kokkos::View::value>> a_Cdbo = d_Cdbo; + Kokkos::View::value>> a_Cdbopi = d_Cdbopi; + Kokkos::View::value>> a_Cdbopi2 = d_Cdbopi2; auto v_CdDelta = ScatterViewHelper,decltype(dup_CdDelta),decltype(ndup_CdDelta)>::get(dup_CdDelta,ndup_CdDelta); auto a_CdDelta = v_CdDelta.template access>(); @@ -2844,10 +2848,11 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeAngularPreproces // Forces - a_Cdbo(i,j_index) += (CEval1 + CEpen2 + (CEcoa1 - CEcoa4)); - a_Cdbo(j,i_index) += (CEval1 + CEpen2 + (CEcoa1 - CEcoa4)); - a_Cdbo(i,k_index) += (CEval2 + CEpen3 + (CEcoa2 - CEcoa5)); - a_Cdbo(k,i_index) += (CEval2 + CEpen3 + (CEcoa2 - CEcoa5)); + // debugging whether or not the values at "1" are needed, they never seem to be read from? + a_Cdbo(i,j_index,0) += (CEval1 + CEpen2 + (CEcoa1 - CEcoa4)); + a_Cdbo(j,i_index,1) += (CEval1 + CEpen2 + (CEcoa1 - CEcoa4)); + a_Cdbo(i,k_index,0) += (CEval2 + CEpen3 + (CEcoa2 - CEcoa5)); + a_Cdbo(k,i_index,1) += (CEval2 + CEpen3 + (CEcoa2 - CEcoa5)); CdDelta_i += ((CEval3 + CEval7) + CEpen1 + CEcoa3); CdDelta_j += CEcoa4; @@ -2858,9 +2863,9 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeAngularPreproces temp = temp_bo_jt * temp_bo_jt * temp_bo_jt; pBOjt7 = temp * temp * temp_bo_jt; - a_Cdbo(i,l_index) += (CEval6 * pBOjt7); - a_Cdbopi(i,l_index) += CEval5; - a_Cdbopi2(i,l_index) += CEval5; + a_Cdbo(i,l_index,0) += (CEval6 * pBOjt7); + a_Cdbopi(i,l_index,0) += CEval5; + a_Cdbopi2(i,l_index,0) += CEval5; } for (int d = 0; d < 3; d++) fi_tmp[d] = CEval8 * dcos_theta_di[d]; @@ -2907,8 +2912,8 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeTorsionPreproces auto v_CdDelta = ScatterViewHelper,decltype(dup_CdDelta),decltype(ndup_CdDelta)>::get(dup_CdDelta,ndup_CdDelta); auto a_CdDelta = v_CdDelta.template access>(); - Kokkos::View::value,Kokkos::MemoryTraits::value>> a_Cdbo = d_Cdbo; - Kokkos::View::value,Kokkos::MemoryTraits::value>> a_Cdbopi = d_Cdbopi; + Kokkos::View::value>> a_Cdbo = d_Cdbo; + Kokkos::View::value>> a_Cdbopi = d_Cdbopi; //auto a_Cdbo = dup_Cdbo.template access>(); // in reaxff_torsion_angles: j = i, k = j, i = k; @@ -3151,14 +3156,14 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeTorsionPreproces // contribution to bond order - a_Cdbopi(i,j_index) += CEtors2; + a_Cdbopi(i,j_index,0) += CEtors2; a_CdDelta[j] += CEtors3; a_CdDelta[i] += CEtors3; - a_Cdbo(i,k_index) += CEtors4 + CEconj1; - a_Cdbo(i,j_index) += CEtors5 + CEconj2; - a_Cdbo(j,l_index) += CEtors6 + CEconj3; + a_Cdbo(i,k_index,0) += CEtors4 + CEconj1; + a_Cdbo(i,j_index,0) += CEtors5 + CEconj2; + a_Cdbo(j,l_index,0) += CEtors6 + CEconj3; const F_FLOAT coeff74 = CEtors7 + CEconj4; const F_FLOAT coeff85 = CEtors8 + CEconj5; @@ -3352,7 +3357,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeHydrogen KOKKOS_INLINE_FUNCTION void PairReaxFFKokkos::operator()(TagPairReaxUpdateBond, const int &ii) const { - Kokkos::View::value>> a_Cdbo = d_Cdbo; - Kokkos::View::value>> a_Cdbopi = d_Cdbopi; - Kokkos::View::value>> a_Cdbopi2 = d_Cdbopi2; + Kokkos::View::value>> a_Cdbo = d_Cdbo; + Kokkos::View::value>> a_Cdbopi = d_Cdbopi; + Kokkos::View::value>> a_Cdbopi2 = d_Cdbopi2; const int i = d_ilist[ii]; const X_FLOAT xtmp = x(i,0); @@ -3425,9 +3430,9 @@ void PairReaxFFKokkos::operator()(TagPairReaxUpdateBond, if (!flag) continue; - const F_FLOAT Cdbo_i = d_Cdbo(i,j_index); - const F_FLOAT Cdbopi_i = d_Cdbopi(i,j_index); - const F_FLOAT Cdbopi2_i = d_Cdbopi2(i,j_index); + const F_FLOAT Cdbo_i = d_Cdbo(i,j_index,0); + const F_FLOAT Cdbopi_i = d_Cdbopi(i,j_index,0); + const F_FLOAT Cdbopi2_i = d_Cdbopi2(i,j_index,0); const int knum = d_bo_num[j]; @@ -3436,9 +3441,9 @@ void PairReaxFFKokkos::operator()(TagPairReaxUpdateBond, k &= NEIGHMASK; if (k != i) continue; - a_Cdbo(j,k_index) += Cdbo_i; - a_Cdbopi(j,k_index) += Cdbopi_i; - a_Cdbopi2(j,k_index) += Cdbopi2_i; + a_Cdbo(j,k_index,0) += Cdbo_i; + a_Cdbopi(j,k_index,0) += Cdbopi_i; + a_Cdbopi2(j,k_index,0) += Cdbopi2_i; } } } @@ -3509,9 +3514,9 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeBond1template e_tally(ev,i,j,ebond); // calculate derivatives of Bond Orders - d_Cdbo(i,j_index) += CEbo; - d_Cdbopi(i,j_index) -= (CEbo + De_p); - d_Cdbopi2(i,j_index) -= (CEbo + De_pp); + d_Cdbo(i,j_index,0) += CEbo; + d_Cdbopi(i,j_index,0) -= (CEbo + De_p); + d_Cdbopi2(i,j_index,0) -= (CEbo + De_pp); // Stabilisation terminal triple bond F_FLOAT estriph = 0.0; @@ -3537,7 +3542,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeBond1::operator()(TagPairReaxComputeBond2 Date: Mon, 9 Sep 2024 12:29:04 -0700 Subject: [PATCH 255/355] Cleaned up the spurious calculations in Cdbo, Cdbopi, Cdbopi2 and removed the overallocations --- src/KOKKOS/kokkos_type.h | 17 ----- src/KOKKOS/pair_reaxff_kokkos.cpp | 102 +++++++++++++----------------- src/KOKKOS/pair_reaxff_kokkos.h | 3 +- 3 files changed, 45 insertions(+), 77 deletions(-) diff --git a/src/KOKKOS/kokkos_type.h b/src/KOKKOS/kokkos_type.h index b510134a77..e9061dd7a3 100644 --- a/src/KOKKOS/kokkos_type.h +++ b/src/KOKKOS/kokkos_type.h @@ -863,15 +863,6 @@ typedef tdual_ffloat_2d_dl::t_dev_um t_ffloat_2d_um_dl; typedef tdual_ffloat_2d_dl::t_dev_const_um t_ffloat_2d_const_um_dl; typedef tdual_ffloat_2d_dl::t_dev_const_randomread t_ffloat_2d_randomread_dl; -// 3d F_FLOAT array n*m - -typedef Kokkos::DualView tdual_ffloat_3d; -typedef tdual_ffloat_3d::t_dev t_ffloat_3d; -typedef tdual_ffloat_3d::t_dev_const t_ffloat_3d_const; -typedef tdual_ffloat_3d::t_dev_um t_ffloat_3d_um; -typedef tdual_ffloat_3d::t_dev_const_um t_ffloat_3d_const_um; -typedef tdual_ffloat_3d::t_dev_const_randomread t_ffloat_3d_randomread; - //2d F_FLOAT array n*3 typedef Kokkos::DualView tdual_f_array; @@ -1178,14 +1169,6 @@ typedef tdual_ffloat_2d_dl::t_host_um t_ffloat_2d_um_dl; typedef tdual_ffloat_2d_dl::t_host_const_um t_ffloat_2d_const_um_dl; typedef tdual_ffloat_2d_dl::t_host_const_randomread t_ffloat_2d_randomread_dl; -// 3d F_FLOAT array n*m -typedef Kokkos::DualView tdual_ffloat_3d; -typedef tdual_ffloat_3d::t_host t_ffloat_3d; -typedef tdual_ffloat_3d::t_host_const t_ffloat_3d_const; -typedef tdual_ffloat_3d::t_host_um t_ffloat_3d_um; -typedef tdual_ffloat_3d::t_host_const_um t_ffloat_3d_const_um; -typedef tdual_ffloat_3d::t_host_const_randomread t_ffloat_3d_randomread; - //2d F_FLOAT array n*3 typedef Kokkos::DualView tdual_f_array; //typedef Kokkos::DualView tdual_f_array; diff --git a/src/KOKKOS/pair_reaxff_kokkos.cpp b/src/KOKKOS/pair_reaxff_kokkos.cpp index a448505ef6..7af5889e62 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.cpp +++ b/src/KOKKOS/pair_reaxff_kokkos.cpp @@ -1537,9 +1537,9 @@ void PairReaxFFKokkos::allocate_array() MemKK::realloc_kokkos(d_Deltap,"reaxff/kk:Deltap",nmax); MemKK::realloc_kokkos(d_total_bo,"reaxff/kk:total_bo",nmax); - MemKK::realloc_kokkos(d_Cdbo,"reaxff/kk:Cdbo",nmax,maxbo,3); - MemKK::realloc_kokkos(d_Cdbopi,"reaxff/kk:Cdbopi",nmax,maxbo,3); - MemKK::realloc_kokkos(d_Cdbopi2,"reaxff/kk:Cdbopi2",nmax,maxbo,3); + MemKK::realloc_kokkos(d_Cdbo,"reaxff/kk:Cdbo",nmax,maxbo); + MemKK::realloc_kokkos(d_Cdbopi,"reaxff/kk:Cdbopi",nmax,maxbo); + MemKK::realloc_kokkos(d_Cdbopi2,"reaxff/kk:Cdbopi2",nmax,maxbo); MemKK::realloc_kokkos(d_Delta,"reaxff/kk:Delta",nmax); MemKK::realloc_kokkos(d_Delta_boc,"reaxff/kk:Delta_boc",nmax); @@ -2066,12 +2066,8 @@ void PairReaxFFKokkos::operator()(TagPairReaxBondOrder2, const int & int j = d_bo_list(i, j_index); j &= NEIGHMASK; const int jtype = type(j); - //const int i_index = maxbo + j_index; // this line seems confusing... - const int i_index = j_index; // ?? - // calculate corrected BO and total bond order - const F_FLOAT val_j = paramssing(jtype).valency; const F_FLOAT ovc = paramstwbp(itype,jtype).ovc; const F_FLOAT v13cor = paramstwbp(itype,jtype).v13cor; @@ -2163,14 +2159,9 @@ void PairReaxFFKokkos::operator()(TagPairReaxBondOrder2, const int & total_bo += d_BO(i,j_index); - // debugging whether or not the values that go into (*,*,1) are relevant - d_Cdbo(i,j_index,0) = 0.0; - d_Cdbopi(i,j_index,0) = 0.0; - d_Cdbopi2(i,j_index,0) = 0.0; - d_Cdbo(j,i_index,1) = 0.0; - d_Cdbopi(j,i_index,1) = 0.0; - d_Cdbopi2(j,i_index,1) = 0.0; - + d_Cdbo(i,j_index) = 0.0; + d_Cdbopi(i,j_index) = 0.0; + d_Cdbopi2(i,j_index) = 0.0; d_CdDelta[j] = 0.0; } d_CdDelta[i] = 0.0; @@ -2364,7 +2355,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeMulti2::operator()(TagPairReaxComputeMulti2::preprocess_angular(int i, int itype, int jnum, if (bo_ij <= thb_cut) continue; if (i >= nlocal && j >= nlocal) continue; - // const int i_index = maxbo + j_index; ?? - const int i_index = j_index; // plus a shift? const int jtype = type(j); for (int k_index = j_index + 1; k_index < jnum; k_index++) { @@ -2550,8 +2539,8 @@ int PairReaxFFKokkos::preprocess_angular(int i, int itype, int jnum, pack.i3 = jnum; d_angular_pack(location_angular, 0) = pack; - // Second pack stores i_index, j_index, k_index, and j_end - pack.i0 = i_index; + // Second pack stores j_index and k_index + // i0 is unused because there's no i_index pack.i1 = j_index; pack.i2 = k_index; // i3 is unused @@ -2651,9 +2640,9 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeAngularPreproces auto v_f = ScatterViewHelper,decltype(dup_f),decltype(ndup_f)>::get(dup_f,ndup_f); auto a_f = v_f.template access>(); - Kokkos::View::value>> a_Cdbo = d_Cdbo; - Kokkos::View::value>> a_Cdbopi = d_Cdbopi; - Kokkos::View::value>> a_Cdbopi2 = d_Cdbopi2; + Kokkos::View::value>> a_Cdbo = d_Cdbo; + Kokkos::View::value>> a_Cdbopi = d_Cdbopi; + Kokkos::View::value>> a_Cdbopi2 = d_Cdbopi2; auto v_CdDelta = ScatterViewHelper,decltype(dup_CdDelta),decltype(ndup_CdDelta)>::get(dup_CdDelta,ndup_CdDelta); auto a_CdDelta = v_CdDelta.template access>(); @@ -2696,7 +2685,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeAngularPreproces const int jnum = pack.i3; pack = d_angular_pack(apack, 1); - const int i_index = pack.i0; + // i0 is unused const int j_index = pack.i1; const int k_index = pack.i2; // i3 is unused @@ -2848,11 +2837,8 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeAngularPreproces // Forces - // debugging whether or not the values at "1" are needed, they never seem to be read from? - a_Cdbo(i,j_index,0) += (CEval1 + CEpen2 + (CEcoa1 - CEcoa4)); - a_Cdbo(j,i_index,1) += (CEval1 + CEpen2 + (CEcoa1 - CEcoa4)); - a_Cdbo(i,k_index,0) += (CEval2 + CEpen3 + (CEcoa2 - CEcoa5)); - a_Cdbo(k,i_index,1) += (CEval2 + CEpen3 + (CEcoa2 - CEcoa5)); + a_Cdbo(i,j_index) += (CEval1 + CEpen2 + (CEcoa1 - CEcoa4)); + a_Cdbo(i,k_index) += (CEval2 + CEpen3 + (CEcoa2 - CEcoa5)); CdDelta_i += ((CEval3 + CEval7) + CEpen1 + CEcoa3); CdDelta_j += CEcoa4; @@ -2863,9 +2849,9 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeAngularPreproces temp = temp_bo_jt * temp_bo_jt * temp_bo_jt; pBOjt7 = temp * temp * temp_bo_jt; - a_Cdbo(i,l_index,0) += (CEval6 * pBOjt7); - a_Cdbopi(i,l_index,0) += CEval5; - a_Cdbopi2(i,l_index,0) += CEval5; + a_Cdbo(i,l_index) += (CEval6 * pBOjt7); + a_Cdbopi(i,l_index) += CEval5; + a_Cdbopi2(i,l_index) += CEval5; } for (int d = 0; d < 3; d++) fi_tmp[d] = CEval8 * dcos_theta_di[d]; @@ -2912,8 +2898,8 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeTorsionPreproces auto v_CdDelta = ScatterViewHelper,decltype(dup_CdDelta),decltype(ndup_CdDelta)>::get(dup_CdDelta,ndup_CdDelta); auto a_CdDelta = v_CdDelta.template access>(); - Kokkos::View::value>> a_Cdbo = d_Cdbo; - Kokkos::View::value>> a_Cdbopi = d_Cdbopi; + Kokkos::View::value>> a_Cdbo = d_Cdbo; + Kokkos::View::value>> a_Cdbopi = d_Cdbopi; //auto a_Cdbo = dup_Cdbo.template access>(); // in reaxff_torsion_angles: j = i, k = j, i = k; @@ -3156,14 +3142,14 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeTorsionPreproces // contribution to bond order - a_Cdbopi(i,j_index,0) += CEtors2; + a_Cdbopi(i,j_index) += CEtors2; a_CdDelta[j] += CEtors3; a_CdDelta[i] += CEtors3; - a_Cdbo(i,k_index,0) += CEtors4 + CEconj1; - a_Cdbo(i,j_index,0) += CEtors5 + CEconj2; - a_Cdbo(j,l_index,0) += CEtors6 + CEconj3; + a_Cdbo(i,k_index) += CEtors4 + CEconj1; + a_Cdbo(i,j_index) += CEtors5 + CEconj2; + a_Cdbo(j,l_index) += CEtors6 + CEconj3; const F_FLOAT coeff74 = CEtors7 + CEconj4; const F_FLOAT coeff85 = CEtors8 + CEconj5; @@ -3357,7 +3343,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeHydrogen KOKKOS_INLINE_FUNCTION void PairReaxFFKokkos::operator()(TagPairReaxUpdateBond, const int &ii) const { - Kokkos::View::value>> a_Cdbo = d_Cdbo; - Kokkos::View::value>> a_Cdbopi = d_Cdbopi; - Kokkos::View::value>> a_Cdbopi2 = d_Cdbopi2; + Kokkos::View::value>> a_Cdbo = d_Cdbo; + Kokkos::View::value>> a_Cdbopi = d_Cdbopi; + Kokkos::View::value>> a_Cdbopi2 = d_Cdbopi2; const int i = d_ilist[ii]; const X_FLOAT xtmp = x(i,0); @@ -3430,9 +3416,9 @@ void PairReaxFFKokkos::operator()(TagPairReaxUpdateBond, if (!flag) continue; - const F_FLOAT Cdbo_i = d_Cdbo(i,j_index,0); - const F_FLOAT Cdbopi_i = d_Cdbopi(i,j_index,0); - const F_FLOAT Cdbopi2_i = d_Cdbopi2(i,j_index,0); + const F_FLOAT Cdbo_i = d_Cdbo(i,j_index); + const F_FLOAT Cdbopi_i = d_Cdbopi(i,j_index); + const F_FLOAT Cdbopi2_i = d_Cdbopi2(i,j_index); const int knum = d_bo_num[j]; @@ -3441,9 +3427,9 @@ void PairReaxFFKokkos::operator()(TagPairReaxUpdateBond, k &= NEIGHMASK; if (k != i) continue; - a_Cdbo(j,k_index,0) += Cdbo_i; - a_Cdbopi(j,k_index,0) += Cdbopi_i; - a_Cdbopi2(j,k_index,0) += Cdbopi2_i; + a_Cdbo(j,k_index) += Cdbo_i; + a_Cdbopi(j,k_index) += Cdbopi_i; + a_Cdbopi2(j,k_index) += Cdbopi2_i; } } } @@ -3514,9 +3500,9 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeBond1template e_tally(ev,i,j,ebond); // calculate derivatives of Bond Orders - d_Cdbo(i,j_index,0) += CEbo; - d_Cdbopi(i,j_index,0) -= (CEbo + De_p); - d_Cdbopi2(i,j_index,0) -= (CEbo + De_pp); + d_Cdbo(i,j_index) += CEbo; + d_Cdbopi(i,j_index) -= (CEbo + De_p); + d_Cdbopi2(i,j_index) -= (CEbo + De_pp); // Stabilisation terminal triple bond F_FLOAT estriph = 0.0; @@ -3542,7 +3528,7 @@ void PairReaxFFKokkos::operator()(TagPairReaxComputeBond1::operator()(TagPairReaxComputeBond2 Date: Mon, 9 Sep 2024 17:51:19 -0600 Subject: [PATCH 256/355] Tweak error message to use correct style --- src/QEQ/fix_qeq.cpp | 2 +- src/REAXFF/fix_qeq_reaxff.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/QEQ/fix_qeq.cpp b/src/QEQ/fix_qeq.cpp index 52ab20c9e1..e09921d11b 100644 --- a/src/QEQ/fix_qeq.cpp +++ b/src/QEQ/fix_qeq.cpp @@ -264,7 +264,7 @@ void FixQEq::allocate_matrix() } bigint m_cap_big = (bigint)MAX(m * safezone, mincap * MIN_NBRS); if (m_cap_big > MAXSMALLINT) - error->one(FLERR,"Too many neighbors in fix qeq"); + error->one(FLERR,"Too many neighbors in fix {}",style); m_cap = m_cap_big; H.n = n_cap; diff --git a/src/REAXFF/fix_qeq_reaxff.cpp b/src/REAXFF/fix_qeq_reaxff.cpp index adaf5be031..921f6e0261 100644 --- a/src/REAXFF/fix_qeq_reaxff.cpp +++ b/src/REAXFF/fix_qeq_reaxff.cpp @@ -363,7 +363,7 @@ void FixQEqReaxFF::allocate_matrix() } bigint m_cap_big = (bigint)MAX(m * safezone, mincap * REAX_MIN_NBRS); if (m_cap_big > MAXSMALLINT) - error->one(FLERR,"Too many neighbors in fix qeq/reaxff"); + error->one(FLERR,"Too many neighbors in fix {}",style); m_cap = m_cap_big; H.n = n_cap; From c3162b4488a4779764dd54e0cbae138c4ce2125a Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 9 Sep 2024 20:29:23 -0400 Subject: [PATCH 257/355] increase timeout for full regression to 180 seconds. use 8 runners. --- .github/workflows/full-regression.yml | 8 ++++++-- tools/regression-tests/config_serial.yaml | 1 + tools/regression-tests/run_tests.py | 0 3 files changed, 7 insertions(+), 2 deletions(-) mode change 100644 => 100755 tools/regression-tests/run_tests.py diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index 106bda9d2e..814631154e 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -2,6 +2,10 @@ name: "Full Regression Test" on: + pull_request: + branches: + - develop + push: branches: - develop @@ -17,9 +21,9 @@ jobs: env: CCACHE_DIR: ${{ github.workspace }}/.ccache strategy: - max-parallel: 4 + max-parallel: 8 matrix: - idx: [ 0, 1, 2, 3 ] + idx: [ 0, 1, 2, 3, 4, 5, 6, 7 ] steps: - name: Checkout repository diff --git a/tools/regression-tests/config_serial.yaml b/tools/regression-tests/config_serial.yaml index 705fb7ec9b..c685815ff0 100644 --- a/tools/regression-tests/config_serial.yaml +++ b/tools/regression-tests/config_serial.yaml @@ -37,6 +37,7 @@ in.bucky-plus-cnt*, ] + timeout: 180 nugget: 1.0 epsilon: 1e-16 diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py old mode 100644 new mode 100755 From a4a8f994719630b43055228a04e0c997101d5c9b Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 9 Sep 2024 20:50:16 -0400 Subject: [PATCH 258/355] forgot to update the --analyze step to 8 runners --- .github/workflows/full-regression.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index 814631154e..4d173173d0 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -82,7 +82,7 @@ jobs: python3 tools/regression-tests/run_tests.py \ --lmp-bin=build/lmp \ --config-file=tools/regression-tests/config_serial.yaml \ - --examples-top-level=examples --analyze --num-workers=4 + --examples-top-level=examples --analyze --num-workers=8 python3 tools/regression-tests/run_tests.py \ --lmp-bin=build/lmp \ From a6b9c170108363f90aeb22fa4834b88cc22a7df2 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 9 Sep 2024 22:30:09 -0400 Subject: [PATCH 259/355] update reference.yaml --- .github/workflows/full-regression.yml | 4 - tools/regression-tests/reference.yaml | 1103 ++++++++++++++----------- 2 files changed, 613 insertions(+), 494 deletions(-) diff --git a/.github/workflows/full-regression.yml b/.github/workflows/full-regression.yml index 4d173173d0..73e1803bb6 100644 --- a/.github/workflows/full-regression.yml +++ b/.github/workflows/full-regression.yml @@ -2,10 +2,6 @@ name: "Full Regression Test" on: - pull_request: - branches: - - develop - push: branches: - develop diff --git a/tools/regression-tests/reference.yaml b/tools/regression-tests/reference.yaml index 2daf17cf13..c18883f375 100644 --- a/tools/regression-tests/reference.yaml +++ b/tools/regression-tests/reference.yaml @@ -1,141 +1,192 @@ -in.granregion.box: { folder: examples/granregion, status: "completed, 8 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } -in.granregion.funnel: { folder: examples/granregion, status: "failed, no Total wall time in the output.", walltime: -1 } -in.granregion.mixer: { folder: examples/granregion, status: "failed, no Total wall time in the output.", walltime: -1 } -in.melt: { folder: examples/melt, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.airebo: { folder: examples/airebo, status: "failed, no Total wall time in the output.", walltime: -1 } -in.airebo-0-0: { folder: examples/airebo, status: "completed, thermo checks passed", walltime: 5.0, walltime_norm: 0.8333333333333334 } -in.airebo-m: { folder: examples/airebo, status: "failed, no Total wall time in the output.", walltime: -1 } -in.rebo2: { folder: examples/airebo, status: "completed, thermo checks passed", walltime: 5.0, walltime_norm: 0.8333333333333334 } -in.hybrid: { folder: examples/template, status: "completed, 3 rel thermo checks failed", walltime: 7.0, walltime_norm: 1.1666666666666667 } -in.mol-data-mix: { folder: examples/template, status: "completed, 3 rel thermo checks failed", walltime: 18.0, walltime_norm: 3.0 } -in.mol-restart-mix: { folder: examples/template, status: "completed, thermo checks passed", walltime: 45.0, walltime_norm: 7.5 } -in.molecular-mix: { folder: examples/template, status: "completed, 5 rel thermo checks failed", walltime: 26.0, walltime_norm: 4.333333333333333 } -in.template-mix: { folder: examples/template, status: "completed, 5 rel thermo checks failed", walltime: 26.0, walltime_norm: 4.333333333333333 } -in.tmpl-data-mix: { folder: examples/template, status: "completed, 3 rel thermo checks failed", walltime: 18.0, walltime_norm: 3.0 } -in.tmpl-restart-mix: { folder: examples/template, status: "completed, thermo checks passed", walltime: 45.0, walltime_norm: 7.5 } +in.granregion.box: { folder: examples/granregion, status: "completed", failed_checks: { abs_diff_failed: 5, rel_diff_failed: 8 }, walltime: 0.0, walltime_norm: 0.0 } +in.granregion.funnel: { folder: examples/granregion, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 106.0, walltime_norm: 17.666666666666668 } +in.granregion.mixer: { folder: examples/granregion, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 84.0, walltime_norm: 14.0 } +in.melt: { folder: examples/melt, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.airebo: { folder: examples/airebo, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 60.0, walltime_norm: 10.0 } +in.airebo-0-0: { folder: examples/airebo, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 5.0, walltime_norm: 0.8333333333333334 } +in.airebo-m: { folder: examples/airebo, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 63.0, walltime_norm: 10.5 } +in.rebo2: { folder: examples/airebo, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 5.0, walltime_norm: 0.8333333333333334 } +in.hybrid: { folder: examples/template, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 7.0, walltime_norm: 1.1666666666666667 } +in.mol-data-mix: { folder: examples/template, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 18.0, walltime_norm: 3.0 } +in.mol-restart-mix: { folder: examples/template, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 45.0, walltime_norm: 7.5 } +in.molecular-mix: { folder: examples/template, status: "completed", failed_checks: { abs_diff_failed: 5, rel_diff_failed: 5 }, walltime: 26.0, walltime_norm: 4.333333333333333 } +in.template-mix: { folder: examples/template, status: "completed", failed_checks: { abs_diff_failed: 5, rel_diff_failed: 5 }, walltime: 26.0, walltime_norm: 4.333333333333333 } +in.tmpl-data-mix: { folder: examples/template, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 18.0, walltime_norm: 3.0 } +in.tmpl-restart-mix: { folder: examples/template, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 45.0, walltime_norm: 7.5 } in.first: { folder: examples/rerun, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 16.0, walltime_norm: 2.6666666666666665 } in.rdf.first: { folder: examples/rerun, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 16.0, walltime_norm: 2.6666666666666665 } in.rdf.rerun: { folder: examples/rerun, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 5.0, walltime_norm: 0.8333333333333334 } -in.read_dump: { folder: examples/rerun, status: "failed, no Total wall time in the output.", walltime: -1 } -in.rerun: { folder: examples/rerun, status: "failed, no Total wall time in the output.", walltime: -1 } -in.lj.ehex: { folder: examples/HEAT, status: "failed, no Total wall time in the output.", walltime: -1 } -in.lj.hex: { folder: examples/HEAT, status: "failed, no Total wall time in the output.", walltime: -1 } -in.spce.ehex: { folder: examples/HEAT, status: "failed, no Total wall time in the output.", walltime: -1 } -in.spce.hex: { folder: examples/HEAT, status: "failed, no Total wall time in the output.", walltime: -1 } -in.vashishta.inp: { folder: examples/vashishta, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.vashishta.sio2: { folder: examples/vashishta, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.vashishta.table.inp: { folder: examples/vashishta, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.vashishta.table.sio2: { folder: examples/vashishta, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.rigid: { folder: examples/rigid, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.rigid.atomfile: { folder: examples/rigid, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.rigid.atomvar: { folder: examples/rigid, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.rigid.early: { folder: examples/rigid, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.read_dump: { folder: examples/rerun, status: "failed, no Total wall time in the output, -------------------------------------------------------------------------- +MPI_ABORT was invoked on rank 0 in communicator MPI_COMM_WORLD +with errorcode 1. + +NOTE: invoking MPI_ABORT causes Open MPI to kill all MPI processes. +You may or may not see output from other processes, depending on +exactly when Open MPI kills them. +-------------------------------------------------------------------------- +", walltime: -1 } +in.rerun: { folder: examples/rerun, status: "failed, no Total wall time in the output, -------------------------------------------------------------------------- +MPI_ABORT was invoked on rank 0 in communicator MPI_COMM_WORLD +with errorcode 1. + +NOTE: invoking MPI_ABORT causes Open MPI to kill all MPI processes. +You may or may not see output from other processes, depending on +exactly when Open MPI kills them. +-------------------------------------------------------------------------- +", walltime: -1 } +in.lj.ehex: { folder: examples/HEAT, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.lj.hex: { folder: examples/HEAT, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.spce.ehex: { folder: examples/HEAT, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.spce.hex: { folder: examples/HEAT, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.vashishta.inp: { folder: examples/vashishta, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.vashishta.sio2: { folder: examples/vashishta, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.vashishta.table.inp: { folder: examples/vashishta, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.vashishta.table.sio2: { folder: examples/vashishta, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.rigid: { folder: examples/rigid, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.rigid.atomfile: { folder: examples/rigid, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.rigid.atomvar: { folder: examples/rigid, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.rigid.early: { folder: examples/rigid, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } in.rigid.gravity: { folder: examples/rigid, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } -in.rigid.infile: { folder: examples/rigid, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.rigid.molecule: { folder: examples/rigid, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.rigid.nve: { folder: examples/rigid, status: "completed, 3 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } -in.rigid.nve.early: { folder: examples/rigid, status: "completed, 3 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } -in.rigid.poems: { folder: examples/rigid, status: "completed, 3 rel thermo checks failed", walltime: 3.0, walltime_norm: 0.5 } -in.rigid.poems2: { folder: examples/rigid, status: "completed, 3 rel thermo checks failed", walltime: 3.0, walltime_norm: 0.5 } -in.rigid.poems3: { folder: examples/rigid, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } -in.rigid.poems4: { folder: examples/rigid, status: "completed, 3 rel thermo checks failed", walltime: 3.0, walltime_norm: 0.5 } -in.rigid.poems5: { folder: examples/rigid, status: "completed, 3 rel thermo checks failed", walltime: 3.0, walltime_norm: 0.5 } -in.rigid.property: { folder: examples/rigid, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.rigid.small: { folder: examples/rigid, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.rigid.small.infile: { folder: examples/rigid, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.rigid.tnr: { folder: examples/rigid, status: "completed, 22 rel thermo checks failed", walltime: 20.0, walltime_norm: 3.3333333333333335 } +in.rigid.infile: { folder: examples/rigid, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.rigid.molecule: { folder: examples/rigid, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.rigid.nve: { folder: examples/rigid, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 0.0, walltime_norm: 0.0 } +in.rigid.nve.early: { folder: examples/rigid, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 0.0, walltime_norm: 0.0 } +in.rigid.poems: { folder: examples/rigid, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 3.0, walltime_norm: 0.5 } +in.rigid.poems2: { folder: examples/rigid, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 3.0, walltime_norm: 0.5 } +in.rigid.poems3: { folder: examples/rigid, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.rigid.poems4: { folder: examples/rigid, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 3.0, walltime_norm: 0.5 } +in.rigid.poems5: { folder: examples/rigid, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 3.0, walltime_norm: 0.5 } +in.rigid.property: { folder: examples/rigid, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.rigid.small: { folder: examples/rigid, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.rigid.small.infile: { folder: examples/rigid, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.rigid.tnr: { folder: examples/rigid, status: "completed", failed_checks: { abs_diff_failed: 18, rel_diff_failed: 22 }, walltime: 21.0, walltime_norm: 3.5 } in.voronoi: { folder: examples/voronoi, status: "completed, but no Step nor Loop in the output.", walltime: 6.0, walltime_norm: 1.0 } in.voronoi.2d: { folder: examples/voronoi, status: "completed, but no Step nor Loop in the output.", walltime: 0.0, walltime_norm: 0.0 } in.voronoi.data: { folder: examples/voronoi, status: "completed, but no Step nor Loop in the output.", walltime: 0.0, walltime_norm: 0.0 } -in.ehex: { folder: examples/KAPPA, status: "failed, no Total wall time in the output.", walltime: -1 } -in.heat: { folder: examples/KAPPA, status: "failed, no Total wall time in the output.", walltime: -1 } -in.heatflux: { folder: examples/KAPPA, status: "failed, no Total wall time in the output.", walltime: -1 } -in.langevin: { folder: examples/KAPPA, status: "failed, no Total wall time in the output.", walltime: -1 } -in.mp: { folder: examples/KAPPA, status: "failed, no Total wall time in the output.", walltime: -1 } -in.pour: { folder: examples/pour, status: "completed, thermo checks passed", walltime: 42.0, walltime_norm: 7.0 } -in.pour.2d: { folder: examples/pour, status: "completed, thermo checks passed", walltime: 4.0, walltime_norm: 0.6666666666666666 } -in.pour.2d.molecule: { folder: examples/pour, status: "completed, thermo checks passed", walltime: 5.0, walltime_norm: 0.8333333333333334 } -in.deposit.atom: { folder: examples/deposit, status: "completed, 3 rel thermo checks failed", walltime: 7.0, walltime_norm: 1.1666666666666667 } -in.deposit.molecule: { folder: examples/deposit, status: "completed, 3 rel thermo checks failed", walltime: 8.0, walltime_norm: 1.3333333333333333 } -in.deposit.molecule.rigid-nve-small: { folder: examples/deposit, status: "completed, thermo checks passed", walltime: 9.0, walltime_norm: 1.5 } -in.deposit.molecule.rigid-nvt-small: { folder: examples/deposit, status: "completed, thermo checks passed", walltime: 9.0, walltime_norm: 1.5 } -in.deposit.molecule.rigid-small: { folder: examples/deposit, status: "completed, thermo checks passed", walltime: 8.0, walltime_norm: 1.3333333333333333 } -in.deposit.molecule.shake: { folder: examples/deposit, status: "completed, 3 rel thermo checks failed", walltime: 7.0, walltime_norm: 1.1666666666666667 } -in.charmmfsw: { folder: examples/charmmfsw, status: "completed, thermo checks passed", walltime: 27.0, walltime_norm: 4.5 } -in.indent: { folder: examples/indent, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } -in.indent.min: { folder: examples/indent, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.qeq.buck: { folder: examples/qeq, status: "completed, thermo checks passed", walltime: 29.0, walltime_norm: 4.833333333333333 } +in.ehex: { folder: examples/KAPPA, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.heat: { folder: examples/KAPPA, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.heatflux: { folder: examples/KAPPA, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.langevin: { folder: examples/KAPPA, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.mp: { folder: examples/KAPPA, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.pour: { folder: examples/pour, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 20.0, walltime_norm: 3.3333333333333335 } +in.pour.2d: { folder: examples/pour, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.pour.2d.molecule: { folder: examples/pour, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.deposit.atom: { folder: examples/deposit, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 3.0, walltime_norm: 0.5 } +in.deposit.molecule: { folder: examples/deposit, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.deposit.molecule.rigid-nve-small: { folder: examples/deposit, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.deposit.molecule.rigid-nvt-small: { folder: examples/deposit, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.deposit.molecule.rigid-small: { folder: examples/deposit, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.deposit.molecule.shake: { folder: examples/deposit, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.charmmfsw: { folder: examples/charmmfsw, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 17.0, walltime_norm: 2.8333333333333335 } +in.indent: { folder: examples/indent, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.indent.min: { folder: examples/indent, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.qeq.buck: { folder: examples/qeq, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 21.0, walltime_norm: 3.5 } in.qeq.reaxff: { folder: examples/qeq, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.dreiding: { folder: examples/dreiding, status: "completed, error parsing log.lammps into YAML", walltime: 0.0, walltime_norm: 0.0 } -in.22DMH.real: { folder: examples/relres, status: "failed, no Total wall time in the output.", walltime: -1 } -in.22DMH.relres: { folder: examples/relres, status: "failed, incomplete runs", walltime: 10.0, walltime_norm: 1.6666666666666667 } -in.22DMH.respa: { folder: examples/relres, status: "completed, thermo checks passed", walltime: 7.0, walltime_norm: 1.1666666666666667 } +in.22DMH.real: { folder: examples/relres, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 57.0, walltime_norm: 9.5 } +in.22DMH.relres: { folder: examples/relres, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 8.0, walltime_norm: 1.3333333333333333 } +in.22DMH.respa: { folder: examples/relres, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 6.0, walltime_norm: 1.0 } in.track: { folder: examples/tracker, status: "failed, ERROR: Illegal pair_style command (src/MISC/pair_tracker.cpp:221).", walltime: -1 } -in.pour.drum: { folder: examples/granular, status: "completed, thermo checks passed", walltime: 15.0, walltime_norm: 2.5 } -in.pour.flatwall: { folder: examples/granular, status: "completed, thermo checks passed", walltime: 14.0, walltime_norm: 2.3333333333333335 } -in.pour.heat: { folder: examples/granular, status: "failed, no Total wall time in the output.", walltime: -1 } -in.restitution: { folder: examples/granular, status: "completed, thermo checks passed", walltime: 7.0, walltime_norm: 1.1666666666666667 } -in.micelle: { folder: examples/micelle, status: "completed, 1 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } -in.micelle-rigid: { folder: examples/micelle, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.replicate.bond.x: { folder: examples/replicate, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.replicate.bond.x.noloop: { folder: examples/replicate, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.replicate.bond.x.y: { folder: examples/replicate, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.replicate.bond.xy: { folder: examples/replicate, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.replicate.cnt: { folder: examples/replicate, status: "completed, thermo checks passed", walltime: 13.0, walltime_norm: 2.1666666666666665 } -in.srd.mixture: { folder: examples/srd, status: "completed, 3 rel thermo checks failed", walltime: 3.0, walltime_norm: 0.5 } -in.srd.pure: { folder: examples/srd, status: "completed, thermo checks passed", walltime: 6.0, walltime_norm: 1.0 } -in.ttm: { folder: examples/ttm, status: "completed, thermo checks passed", walltime: 17.0, walltime_norm: 2.8333333333333335 } -in.ttm.grid: { folder: examples/ttm, status: "completed, thermo checks passed", walltime: 17.0, walltime_norm: 2.8333333333333335 } -in.ttm.mod: { folder: examples/ttm, status: "completed, thermo checks passed", walltime: 6.0, walltime_norm: 1.0 } -in.colloid: { folder: examples/multi, status: "completed, 3 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } -in.granular: { folder: examples/multi, status: "completed, 3 rel thermo checks failed", walltime: 1.0, walltime_norm: 0.16666666666666666 } -in.powerlaw: { folder: examples/multi, status: "completed, 3 rel thermo checks failed", walltime: 3.0, walltime_norm: 0.5 } -in.msst: { folder: examples/msst, status: "completed, thermo checks passed", walltime: 8.0, walltime_norm: 1.3333333333333333 } -in.gjf.vfull: { folder: examples/gjf, status: "completed, 3 rel thermo checks failed", walltime: 1.0, walltime_norm: 0.16666666666666666 } -in.gjf.vhalf: { folder: examples/gjf, status: "completed, 3 rel thermo checks failed", walltime: 1.0, walltime_norm: 0.16666666666666666 } -in.spin.cobalt_fcc: { folder: examples/SPIN/cobalt_fcc, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } -in.spin.nickel: { folder: examples/SPIN/nickel, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } -in.spin.nickel_cubic: { folder: examples/SPIN/nickel, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } -in.spin.cobalt_hcp: { folder: examples/SPIN/cobalt_hcp, status: "completed, 4 rel thermo checks failed", walltime: 3.0, walltime_norm: 0.5 } -in.spin.iron: { folder: examples/SPIN/iron, status: "completed, 4 rel thermo checks failed", walltime: 1.0, walltime_norm: 0.16666666666666666 } -in.spin.iron_cubic: { folder: examples/SPIN/iron, status: "completed, 2 rel thermo checks failed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.pour.drum: { folder: examples/granular, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.pour.flatwall: { folder: examples/granular, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.pour.heat: { folder: examples/granular, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 83.0, walltime_norm: 13.833333333333334 } +in.restitution: { folder: examples/granular, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 5.0, walltime_norm: 0.8333333333333334 } +in.micelle: { folder: examples/micelle, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 1 }, walltime: 0.0, walltime_norm: 0.0 } +in.micelle-rigid: { folder: examples/micelle, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.replicate.bond.x: { folder: examples/replicate, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.replicate.bond.x.noloop: { folder: examples/replicate, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.replicate.bond.x.y: { folder: examples/replicate, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.replicate.bond.xy: { folder: examples/replicate, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.replicate.cnt: { folder: examples/replicate, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 8.0, walltime_norm: 1.3333333333333333 } +in.srd.mixture: { folder: examples/srd, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.srd.pure: { folder: examples/srd, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.ttm: { folder: examples/ttm, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.ttm.grid: { folder: examples/ttm, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.ttm.mod: { folder: examples/ttm, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.colloid: { folder: examples/multi, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 0.0, walltime_norm: 0.0 } +in.granular: { folder: examples/multi, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.powerlaw: { folder: examples/multi, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.msst: { folder: examples/msst, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 6.0, walltime_norm: 1.0 } +in.gjf.vfull: { folder: examples/gjf, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.gjf.vhalf: { folder: examples/gjf, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.spin.cobalt_fcc: { folder: examples/SPIN/cobalt_fcc, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.spin.nickel: { folder: examples/SPIN/nickel, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.spin.nickel_cubic: { folder: examples/SPIN/nickel, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.spin.cobalt_hcp: { folder: examples/SPIN/cobalt_hcp, status: "completed", failed_checks: { abs_diff_failed: 4, rel_diff_failed: 4 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.spin.iron: { folder: examples/SPIN/iron, status: "completed", failed_checks: { abs_diff_failed: 4, rel_diff_failed: 4 }, walltime: 0.0, walltime_norm: 0.0 } +in.spin.iron_cubic: { folder: examples/SPIN/iron, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 2 }, walltime: 0.0, walltime_norm: 0.0 } in.gneb.skyrmion: { folder: examples/SPIN/gneb/skyrmion, status: "failed, ERROR: Did not assign all atoms correctly (src/read_data.cpp:1562).", walltime: -1 } in.gneb.iron: { folder: examples/SPIN/gneb/iron, status: "failed, ERROR: Cannot use NEBSpin with a single replica (src/SPIN/neb_spin.cpp:133).", walltime: -1 } -in.spin.read_data: { folder: examples/SPIN/read_restart, status: "failed, no Total wall time in the output.", walltime: -1 } +in.spin.read_data: { folder: examples/SPIN/read_restart, status: "failed, no Total wall time in the output, -------------------------------------------------------------------------- +MPI_ABORT was invoked on rank 0 in communicator MPI_COMM_WORLD +with errorcode 1. + +NOTE: invoking MPI_ABORT causes Open MPI to kill all MPI processes. +You may or may not see output from other processes, depending on +exactly when Open MPI kills them. +-------------------------------------------------------------------------- +", walltime: -1 } in.spin.restart: { folder: examples/SPIN/read_restart, status: "failed, ERROR: Invalid flag in force field section of restart file (src/read_restart.cpp:948).", walltime: -1 } -in.spin.write_restart: { folder: examples/SPIN/read_restart, status: "completed, 2 rel thermo checks failed", walltime: 4.0, walltime_norm: 0.6666666666666666 } -in.spin.bfo_min: { folder: examples/SPIN/spinmin, status: "completed, thermo checks passed", walltime: 10.0, walltime_norm: 1.6666666666666667 } -in.spin.bfo_min_cg: { folder: examples/SPIN/spinmin, status: "completed, thermo checks passed", walltime: 11.0, walltime_norm: 1.8333333333333333 } -in.spin.bfo_min_lbfgs: { folder: examples/SPIN/spinmin, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.spin.iron_min: { folder: examples/SPIN/spinmin, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.spin.setforce: { folder: examples/SPIN/setforce_spin, status: "failed, no Total wall time in the output.", walltime: -1 } -in.spin.bfo: { folder: examples/SPIN/bfo, status: "completed, 2 rel thermo checks failed", walltime: 10.0, walltime_norm: 1.6666666666666667 } -in.spin.iron_dipole_cut: { folder: examples/SPIN/dipole_spin, status: "completed, thermo checks passed", walltime: 9.0, walltime_norm: 1.5 } -in.spin.iron_dipole_ewald: { folder: examples/SPIN/dipole_spin, status: "completed, 1 abs thermo checks failed", walltime: 30.0, walltime_norm: 5.0 } -in.spin.iron_dipole_pppm: { folder: examples/SPIN/dipole_spin, status: "completed, thermo checks passed", walltime: 17.0, walltime_norm: 2.8333333333333335 } -in.spin.iron-nve: { folder: examples/SPIN/test_problems/validation_nve, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 32.0, walltime_norm: 5.333333333333333 } -in.spin.nvt_lattice: { folder: examples/SPIN/test_problems/validation_nvt, status: "failed, no Total wall time in the output.", walltime: -1 } +in.spin.write_restart: { folder: examples/SPIN/read_restart, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 2 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.spin.bfo_min: { folder: examples/SPIN/spinmin, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 6.0, walltime_norm: 1.0 } +in.spin.bfo_min_cg: { folder: examples/SPIN/spinmin, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 7.0, walltime_norm: 1.1666666666666667 } +in.spin.bfo_min_lbfgs: { folder: examples/SPIN/spinmin, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.spin.iron_min: { folder: examples/SPIN/spinmin, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.spin.setforce: { folder: examples/SPIN/setforce_spin, status: "failed, no Total wall time in the output, [fv-az1014-42:16323] *** Process received signal *** +[fv-az1014-42:16323] Signal: Segmentation fault (11) +[fv-az1014-42:16323] Signal code: Address not mapped (1) +[fv-az1014-42:16323] Failing at address: 0x390 +[fv-az1014-42:16323] [ 0] /lib/x86_64-linux-gnu/libc.so.6(+0x42520)[0x7f09e7842520] +[fv-az1014-42:16323] [ 1] /home/runner/work/lammps/lammps/build/lmp(_ZN9LAMMPS_NS11ComputeSpin14compute_vectorEv+0x2d8)[0x5590ad415268] +[fv-az1014-42:16323] [ 2] /home/runner/work/lammps/lammps/build/lmp(_ZN9LAMMPS_NS8Variable8evaluateEPcPPNS0_4TreeEi+0x6e7f)[0x5590ad0078ef] +[fv-az1014-42:16323] [ 3] /home/runner/work/lammps/lammps/build/lmp(_ZN9LAMMPS_NS8Variable13compute_equalEi+0x22b)[0x5590ad00d2ab] +[fv-az1014-42:16323] [ 4] /home/runner/work/lammps/lammps/build/lmp(_ZN9LAMMPS_NS6Thermo16compute_variableEv+0x5b)[0x5590acfbfa6b] +[fv-az1014-42:16323] [ 5] /home/runner/work/lammps/lammps/build/lmp(_ZN9LAMMPS_NS6Thermo7computeEi+0x203)[0x5590acfc9dc3] +[fv-az1014-42:16323] [ 6] /home/runner/work/lammps/lammps/build/lmp(_ZN9LAMMPS_NS6Output5setupEi+0x64)[0x5590acf57f14] +[fv-az1014-42:16323] [ 7] /home/runner/work/lammps/lammps/build/lmp(_ZN9LAMMPS_NS3Min5setupEi+0x57d)[0x5590acee421d] +[fv-az1014-42:16323] [ 8] /home/runner/work/lammps/lammps/build/lmp(_ZN9LAMMPS_NS8Minimize7commandEiPPc+0x1d7)[0x5590acee5a67] +[fv-az1014-42:16323] [ 9] /home/runner/work/lammps/lammps/build/lmp(_ZN9LAMMPS_NS5Input15execute_commandEv+0xb1d)[0x5590ace91b9d] +[fv-az1014-42:16323] [10] /home/runner/work/lammps/lammps/build/lmp(_ZN9LAMMPS_NS5Input4fileEv+0x19e)[0x5590ace91f5e] +[fv-az1014-42:16323] [11] /home/runner/work/lammps/lammps/build/lmp(main+0x51)[0x5590ace7ed41] +[fv-az1014-42:16323] [12] /lib/x86_64-linux-gnu/libc.so.6(+0x29d90)[0x7f09e7829d90] +[fv-az1014-42:16323] [13] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80)[0x7f09e7829e40] +[fv-az1014-42:16323] [14] /home/runner/work/lammps/lammps/build/lmp(_start+0x25)[0x5590ace834e5] +[fv-az1014-42:16323] *** End of error message *** +Segmentation fault (core dumped) +", walltime: -1 } +in.spin.bfo: { folder: examples/SPIN/bfo, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 2 }, walltime: 7.0, walltime_norm: 1.1666666666666667 } +in.spin.iron_dipole_cut: { folder: examples/SPIN/dipole_spin, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 6.0, walltime_norm: 1.0 } +in.spin.iron_dipole_ewald: { folder: examples/SPIN/dipole_spin, status: "completed", failed_checks: { abs_diff_failed: 1, rel_diff_failed: 0 }, walltime: 20.0, walltime_norm: 3.3333333333333335 } +in.spin.iron_dipole_pppm: { folder: examples/SPIN/dipole_spin, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.spin.iron-nve: { folder: examples/SPIN/test_problems/validation_nve, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 19.0, walltime_norm: 3.1666666666666665 } +in.spin.nvt_lattice: { folder: examples/SPIN/test_problems/validation_nvt, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 95.0, walltime_norm: 15.833333333333334 } in.spin.nvt_spin: { folder: examples/SPIN/test_problems/validation_nvt, status: "failed, ERROR: Fix langevin period must be > 0.0 (src/fix_langevin.cpp:80).", walltime: -1 } in.mliap.ace.compute: { folder: examples/mliap, status: "completed, but no Step nor Loop in the output.", walltime: 0.0, walltime_norm: 0.0 } -in.mliap.nn.Cu: { folder: examples/mliap, status: "completed, thermo checks passed", walltime: 6.0, walltime_norm: 1.0 } -in.mliap.nn.Ta06A: { folder: examples/mliap, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.mliap.nn.Cu: { folder: examples/mliap, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.mliap.nn.Ta06A: { folder: examples/mliap, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } in.mliap.pytorch.Ta06A: { folder: examples/mliap, status: "failed, ERROR: Using pair_style mliap model mliappy requires ML-IAP with python support (src/ML-IAP/pair_mliap.cpp:173).", walltime: -1 } in.mliap.pytorch.ace: { folder: examples/mliap, status: "failed, ERROR: Using pair_style mliap model mliappy requires ML-IAP with python support (src/ML-IAP/pair_mliap.cpp:173).", walltime: -1 } in.mliap.pytorch.ace.NN: { folder: examples/mliap, status: "failed, ERROR: Using pair_style mliap model mliappy requires ML-IAP with python support (src/ML-IAP/pair_mliap.cpp:173).", walltime: -1 } in.mliap.pytorch.relu1hidden: { folder: examples/mliap, status: "failed, ERROR: Using pair_style mliap model mliappy requires ML-IAP with python support (src/ML-IAP/pair_mliap.cpp:173).", walltime: -1 } -in.mliap.quadratic.compute: { folder: examples/mliap, status: "failed, no Total wall time in the output.", walltime: -1 } -in.mliap.snap.Ta06A: { folder: examples/mliap, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } -in.mliap.snap.WBe.PRB2019: { folder: examples/mliap, status: "completed, thermo checks passed", walltime: 4.0, walltime_norm: 0.6666666666666666 } -in.mliap.snap.chem: { folder: examples/mliap, status: "completed, thermo checks passed", walltime: 26.0, walltime_norm: 4.333333333333333 } -in.mliap.snap.compute: { folder: examples/mliap, status: "failed, no Total wall time in the output.", walltime: -1 } -in.mliap.snap.quadratic: { folder: examples/mliap, status: "completed, thermo checks passed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.mliap.quadratic.compute: { folder: examples/mliap, status: "failed, no Total wall time in the output, munmap_chunk(): invalid pointer +[fv-az1014-42:16535] *** Process received signal *** +[fv-az1014-42:16535] Signal: Aborted (6) +[fv-az1014-42:16535] Signal code: (-6) +corrupted double-linked list +Aborted (core dumped) +", walltime: -1 } +in.mliap.snap.Ta06A: { folder: examples/mliap, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.mliap.snap.WBe.PRB2019: { folder: examples/mliap, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.mliap.snap.chem: { folder: examples/mliap, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 15.0, walltime_norm: 2.5 } +in.mliap.snap.compute: { folder: examples/mliap, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.mliap.snap.quadratic: { folder: examples/mliap, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } in.mliap.so3.Ni_Mo: { folder: examples/mliap, status: "completed, but no Step nor Loop in the output.", walltime: 0.0, walltime_norm: 0.0 } -in.mliap.so3.nn.Si: { folder: examples/mliap, status: "completed, thermo checks passed", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.mliap.so3.nn.Si: { folder: examples/mliap, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 3.0, walltime_norm: 0.5 } in.mliap.unified.lj.Ar: { folder: examples/mliap, status: "failed, ERROR: Could not process Python string: .", walltime: -1 } in.run: { folder: examples/mliap/jax, status: "failed, ERROR: Using pair_style mliap unified requires ML-IAP with python support (src/ML-IAP/pair_mliap.cpp:213).", walltime: -1 } -in.eim: { folder: examples/eim, status: "completed, 2 rel thermo checks failed", walltime: 3.0, walltime_norm: 0.5 } -in.shear: { folder: examples/shear, status: "completed, thermo checks passed", walltime: 6.0, walltime_norm: 1.0 } -in.shear.void: { folder: examples/shear, status: "completed, thermo checks passed", walltime: 5.0, walltime_norm: 0.8333333333333334 } +in.eim: { folder: examples/eim, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 2 }, walltime: 3.0, walltime_norm: 0.5 } +in.shear: { folder: examples/shear, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.shear.void: { folder: examples/shear, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 3.0, walltime_norm: 0.5 } in.aimd.alone: { folder: examples/mdi, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.aimd.driver: { folder: examples/mdi, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } in.aimd.driver.plugin: { folder: examples/mdi, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } @@ -151,117 +202,117 @@ in.snapshot.alone: { folder: examples/mdi, status: "completed, numerical checks in.snapshot.driver: { folder: examples/mdi, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } in.snapshot.driver.plugin: { folder: examples/mdi, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } in.snapshot.engine: { folder: examples/mdi, status: "failed, unknown command, package not installed, ERROR: Unknown command: mdi engine (src/input.cpp:314)", walltime: -1 } -in.lammps: { folder: examples/PACKAGES/dpd-smooth/2d-diffusion, status: "failed, no Total wall time in the output.", walltime: -1 } -in.lammps: { folder: examples/PACKAGES/dpd-smooth/2d-diffusion-in-shear-flow, status: "failed, no Total wall time in the output.", walltime: -1 } -in.lammps: { folder: examples/PACKAGES/dpd-smooth/equipartition-verification, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 49.0, walltime_norm: 8.166666666666666 } +in.lammps: { folder: examples/PACKAGES/dpd-smooth/2d-diffusion, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 57.0, walltime_norm: 9.5 } +in.lammps: { folder: examples/PACKAGES/dpd-smooth/2d-diffusion-in-shear-flow, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 102.0, walltime_norm: 17.0 } +in.lammps: { folder: examples/PACKAGES/dpd-smooth/equipartition-verification, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 41.0, walltime_norm: 6.833333333333333 } in.fitpod: { folder: examples/PACKAGES/pod/InP, status: "failed, ERROR: Cannot fit potential without data files. The data paths may not be valid. Please check the data paths in the POD data file. (src/ML-POD/fitpod_command.cpp:718).", walltime: -1 } -in.pod: { folder: examples/PACKAGES/pod/InP, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } +in.pod: { folder: examples/PACKAGES/pod/InP, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } in.fitpod: { folder: examples/PACKAGES/pod/Ta, status: "completed, but no Step nor Loop in the output.", walltime: 0.0, walltime_norm: 0.0 } -in.pod: { folder: examples/PACKAGES/pod/Ta, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.pod: { folder: examples/PACKAGES/pod/Ta, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } in.pod.compute: { folder: examples/PACKAGES/pod/Ta, status: "failed, ERROR: Per-atom data too large (src/ML-POD/compute_podd_atom.cpp:62).", walltime: -1 } -in.duplex2: { folder: examples/PACKAGES/cgdna/examples/real_units/oxRNA2/duplex2, status: "completed, 1 rel thermo checks failed", walltime: 19.0, walltime_norm: 3.1666666666666665 } -in.duplex2: { folder: examples/PACKAGES/cgdna/examples/real_units/oxRNA2/potential_file, status: "completed, 1 rel thermo checks failed", walltime: 19.0, walltime_norm: 3.1666666666666665 } -in.duplex2: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA/duplex2, status: "completed, thermo checks passed", walltime: 21.0, walltime_norm: 3.5 } -in.duplex1: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA/duplex1, status: "completed, thermo checks passed", walltime: 10.0, walltime_norm: 1.6666666666666667 } -in.duplex1: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA/potential_file, status: "completed, thermo checks passed", walltime: 10.0, walltime_norm: 1.6666666666666667 } -in.duplex2: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA2/duplex2, status: "completed, thermo checks passed", walltime: 24.0, walltime_norm: 4.0 } -in.duplex1: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA2/duplex1, status: "completed, thermo checks passed", walltime: 12.0, walltime_norm: 2.0 } -in.dsring: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA2/dsring, status: "completed, thermo checks passed", walltime: 46.0, walltime_norm: 7.666666666666667 } -in.duplex1: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA2/potential_file, status: "completed, thermo checks passed", walltime: 11.0, walltime_norm: 1.8333333333333333 } -in.duplex4.4type: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA2/unique_bp, status: "completed, thermo checks passed", walltime: 50.0, walltime_norm: 8.333333333333334 } -in.duplex4.8type: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA2/unique_bp, status: "completed, thermo checks passed", walltime: 50.0, walltime_norm: 8.333333333333334 } -in.duplex3: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA2/duplex3, status: "completed, thermo checks passed", walltime: 11.0, walltime_norm: 1.8333333333333333 } -in.duplex2: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxRNA2/duplex2, status: "completed, thermo checks passed", walltime: 19.0, walltime_norm: 3.1666666666666665 } -in.duplex2: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxRNA2/potential_file, status: "completed, thermo checks passed", walltime: 19.0, walltime_norm: 3.1666666666666665 } -in.duplex2: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA/duplex2, status: "completed, thermo checks passed", walltime: 22.0, walltime_norm: 3.6666666666666665 } -in.duplex1: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA/duplex1, status: "completed, thermo checks passed", walltime: 10.0, walltime_norm: 1.6666666666666667 } -in.duplex1: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA/potential_file, status: "completed, thermo checks passed", walltime: 10.0, walltime_norm: 1.6666666666666667 } -in.duplex2: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA2/duplex2, status: "completed, thermo checks passed", walltime: 24.0, walltime_norm: 4.0 } -in.duplex1: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA2/duplex1, status: "completed, thermo checks passed", walltime: 11.0, walltime_norm: 1.8333333333333333 } -in.dsring: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA2/dsring, status: "completed, thermo checks passed", walltime: 46.0, walltime_norm: 7.666666666666667 } -in.duplex1: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA2/potential_file, status: "completed, thermo checks passed", walltime: 11.0, walltime_norm: 1.8333333333333333 } -in.duplex4.4type: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA2/unique_bp, status: "completed, thermo checks passed", walltime: 50.0, walltime_norm: 8.333333333333334 } -in.duplex4.8type: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA2/unique_bp, status: "completed, thermo checks passed", walltime: 50.0, walltime_norm: 8.333333333333334 } -in.duplex3: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA2/duplex3, status: "completed, thermo checks passed", walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.duplex2: { folder: examples/PACKAGES/cgdna/examples/real_units/oxRNA2/duplex2, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 1 }, walltime: 22.0, walltime_norm: 3.6666666666666665 } +in.duplex2: { folder: examples/PACKAGES/cgdna/examples/real_units/oxRNA2/potential_file, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 1 }, walltime: 19.0, walltime_norm: 3.1666666666666665 } +in.duplex2: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA/duplex2, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 21.0, walltime_norm: 3.5 } +in.duplex1: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA/duplex1, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.duplex1: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA/potential_file, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.duplex2: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA2/duplex2, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 24.0, walltime_norm: 4.0 } +in.duplex1: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA2/duplex1, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.dsring: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA2/dsring, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 46.0, walltime_norm: 7.666666666666667 } +in.duplex1: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA2/potential_file, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.duplex4.4type: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA2/unique_bp, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 51.0, walltime_norm: 8.5 } +in.duplex4.8type: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA2/unique_bp, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 50.0, walltime_norm: 8.333333333333334 } +in.duplex3: { folder: examples/PACKAGES/cgdna/examples/real_units/oxDNA2/duplex3, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.duplex2: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxRNA2/duplex2, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 19.0, walltime_norm: 3.1666666666666665 } +in.duplex2: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxRNA2/potential_file, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 19.0, walltime_norm: 3.1666666666666665 } +in.duplex2: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA/duplex2, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 22.0, walltime_norm: 3.6666666666666665 } +in.duplex1: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA/duplex1, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.duplex1: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA/potential_file, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.duplex2: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA2/duplex2, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 25.0, walltime_norm: 4.166666666666667 } +in.duplex1: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA2/duplex1, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.dsring: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA2/dsring, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 46.0, walltime_norm: 7.666666666666667 } +in.duplex1: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA2/potential_file, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.duplex4.4type: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA2/unique_bp, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 50.0, walltime_norm: 8.333333333333334 } +in.duplex4.8type: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA2/unique_bp, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 50.0, walltime_norm: 8.333333333333334 } +in.duplex3: { folder: examples/PACKAGES/cgdna/examples/lj_units/oxDNA2/duplex3, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 11.0, walltime_norm: 1.8333333333333333 } in.temper_npt: { folder: examples/PACKAGES/temper_npt, status: "failed, ERROR: World variable count doesn't match # of partitions (src/variable.cpp:255).", walltime: -1 } in.peptide-plumed: { folder: examples/PACKAGES/plumed, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'plumed' is part of the PLUMED package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } -in.methanol: { folder: examples/PACKAGES/bocs, status: "completed, 4 rel thermo checks failed", walltime: 23.0, walltime_norm: 3.8333333333333335 } -in.pedone.melt: { folder: examples/PACKAGES/pedone, status: "completed, 2 rel thermo checks failed", walltime: 13.0, walltime_norm: 2.1666666666666665 } -in.pedone.relax: { folder: examples/PACKAGES/pedone, status: "completed, 2 rel thermo checks failed", walltime: 16.0, walltime_norm: 2.6666666666666665 } -in.methanol_implicit_water: { folder: examples/PACKAGES/local_density/methanol_implicit_water, status: "failed, no Total wall time in the output.", walltime: -1 } -in.benzene_water: { folder: examples/PACKAGES/local_density/benzene_water, status: "completed, but no Step nor Loop in the output.", walltime: 24.0, walltime_norm: 4.0 } +in.methanol: { folder: examples/PACKAGES/bocs, status: "completed", failed_checks: { abs_diff_failed: 4, rel_diff_failed: 4 }, walltime: 23.0, walltime_norm: 3.8333333333333335 } +in.pedone.melt: { folder: examples/PACKAGES/pedone, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 2 }, walltime: 13.0, walltime_norm: 2.1666666666666665 } +in.pedone.relax: { folder: examples/PACKAGES/pedone, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 2 }, walltime: 15.0, walltime_norm: 2.5 } +in.methanol_implicit_water: { folder: examples/PACKAGES/local_density/methanol_implicit_water, status: "completed, but no Step nor Loop in the output.", walltime: 62.0, walltime_norm: 10.333333333333334 } +in.benzene_water: { folder: examples/PACKAGES/local_density/benzene_water, status: "completed, but no Step nor Loop in the output.", walltime: 25.0, walltime_norm: 4.166666666666667 } in.gauss-diel: { folder: examples/PACKAGES/gauss_diel, status: "completed, error parsing log.lammps into YAML", walltime: 8.0, walltime_norm: 1.3333333333333333 } in.gauss-diel-cg: { folder: examples/PACKAGES/gauss_diel, status: "completed, error parsing log.lammps into YAML", walltime: 3.0, walltime_norm: 0.5 } in.gauss-diel-split: { folder: examples/PACKAGES/gauss_diel, status: "completed, error parsing log.lammps into YAML", walltime: 3.0, walltime_norm: 0.5 } in.alloy: { folder: examples/PACKAGES/alchemy, status: "failed, ERROR: World variable count doesn't match # of partitions (src/variable.cpp:255).", walltime: -1 } in.twowater: { folder: examples/PACKAGES/alchemy, status: "failed, ERROR: World variable count doesn't match # of partitions (src/variable.cpp:255).", walltime: -1 } -in.sds-hybrid: { folder: examples/PACKAGES/cgspica/sds-monolayer, status: "completed, thermo checks passed", walltime: 6.0, walltime_norm: 1.0 } -in.sds-regular: { folder: examples/PACKAGES/cgspica/sds-monolayer, status: "completed, thermo checks passed", walltime: 6.0, walltime_norm: 1.0 } -in.pegc12e8: { folder: examples/PACKAGES/cgspica/peg-verlet, status: "failed, no Total wall time in the output.", walltime: -1 } -in.pegc12e8-angle: { folder: examples/PACKAGES/cgspica/peg-verlet, status: "failed, no Total wall time in the output.", walltime: -1 } -in.hkust1: { folder: examples/PACKAGES/mofff, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.hkust1_long: { folder: examples/PACKAGES/mofff, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.e3b-tip4p2005: { folder: examples/PACKAGES/e3b, status: "completed, thermo checks passed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.sds-hybrid: { folder: examples/PACKAGES/cgspica/sds-monolayer, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 6.0, walltime_norm: 1.0 } +in.sds-regular: { folder: examples/PACKAGES/cgspica/sds-monolayer, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 6.0, walltime_norm: 1.0 } +in.pegc12e8: { folder: examples/PACKAGES/cgspica/peg-verlet, status: "completed, error parsing log.lammps into YAML", walltime: 69.0, walltime_norm: 11.5 } +in.pegc12e8-angle: { folder: examples/PACKAGES/cgspica/peg-verlet, status: "completed, error parsing log.lammps into YAML", walltime: 69.0, walltime_norm: 11.5 } +in.hkust1: { folder: examples/PACKAGES/mofff, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.hkust1_long: { folder: examples/PACKAGES/mofff, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.e3b-tip4p2005: { folder: examples/PACKAGES/e3b, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } in.uf3.Nb: { folder: examples/PACKAGES/uf3, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } -in.fep01.lmp: { folder: examples/PACKAGES/fep/CC-CO/fep01, status: "failed, no Total wall time in the output.", walltime: -1 } -in.fep10.lmp: { folder: examples/PACKAGES/fep/CC-CO/fep10, status: "failed, no Total wall time in the output.", walltime: -1 } -in.insertion: { folder: examples/PACKAGES/fep/C7inEthanol/fep01, status: "failed, no Total wall time in the output.", walltime: -1 } -in.deletion: { folder: examples/PACKAGES/fep/C7inEthanol/fep10, status: "failed, no Total wall time in the output.", walltime: -1 } -in.fep01.lmp: { folder: examples/PACKAGES/fep/CH4-CF4/fep01, status: "failed, no Total wall time in the output.", walltime: -1 } -in.fep10.lmp: { folder: examples/PACKAGES/fep/CH4-CF4/fep10, status: "failed, no Total wall time in the output.", walltime: -1 } -in.bar10.lmp: { folder: examples/PACKAGES/fep/CH4-CF4/bar10, status: "failed, no Total wall time in the output.", walltime: -1 } -in.bar01.lmp: { folder: examples/PACKAGES/fep/CH4-CF4/bar01, status: "failed, no Total wall time in the output.", walltime: -1 } -in.fep01.lmp: { folder: examples/PACKAGES/fep/CH4hyd/fep01, status: "failed, no Total wall time in the output.", walltime: -1 } -in.fdti01.lmp: { folder: examples/PACKAGES/fep/CH4hyd/fdti01, status: "failed, no Total wall time in the output.", walltime: -1 } -in.fep10.lmp: { folder: examples/PACKAGES/fep/CH4hyd/fep10, status: "failed, no Total wall time in the output.", walltime: -1 } -in.fdti10.lmp: { folder: examples/PACKAGES/fep/CH4hyd/fdti10, status: "failed, no Total wall time in the output.", walltime: -1 } -in.spce.lmp: { folder: examples/PACKAGES/fep/ta, status: "failed, no Total wall time in the output.", walltime: -1 } +in.fep01.lmp: { folder: examples/PACKAGES/fep/CC-CO/fep01, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.fep10.lmp: { folder: examples/PACKAGES/fep/CC-CO/fep10, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.insertion: { folder: examples/PACKAGES/fep/C7inEthanol/fep01, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.deletion: { folder: examples/PACKAGES/fep/C7inEthanol/fep10, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.fep01.lmp: { folder: examples/PACKAGES/fep/CH4-CF4/fep01, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.fep10.lmp: { folder: examples/PACKAGES/fep/CH4-CF4/fep10, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.bar10.lmp: { folder: examples/PACKAGES/fep/CH4-CF4/bar10, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.bar01.lmp: { folder: examples/PACKAGES/fep/CH4-CF4/bar01, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.fep01.lmp: { folder: examples/PACKAGES/fep/CH4hyd/fep01, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.fdti01.lmp: { folder: examples/PACKAGES/fep/CH4hyd/fdti01, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.fep10.lmp: { folder: examples/PACKAGES/fep/CH4hyd/fep10, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.fdti10.lmp: { folder: examples/PACKAGES/fep/CH4hyd/fdti10, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.spce.lmp: { folder: examples/PACKAGES/fep/ta, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } in.gap: { folder: examples/PACKAGES/quip, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized pair style 'quip' is part of the ML-QUIP package which is not enabled in this LAMMPS binary. (src/force.cpp:275)", walltime: -1 } in.molecular: { folder: examples/PACKAGES/quip, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized pair style 'quip' is part of the ML-QUIP package which is not enabled in this LAMMPS binary. (src/force.cpp:275)", walltime: -1 } in.sw: { folder: examples/PACKAGES/quip, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized pair style 'quip' is part of the ML-QUIP package which is not enabled in this LAMMPS binary. (src/force.cpp:275)", walltime: -1 } in.srp_react: { folder: examples/PACKAGES/srp_react, status: "failed, ERROR: Invalid bond type 0 for pair style srp (src/MISC/pair_srp.cpp:403).", walltime: -1 } -in.spce: { folder: examples/PACKAGES/manybody_table, status: "completed, 3 rel thermo checks failed", walltime: 9.0, walltime_norm: 1.5 } -in.spce2: { folder: examples/PACKAGES/manybody_table, status: "completed, 3 rel thermo checks failed", walltime: 11.0, walltime_norm: 1.8333333333333333 } -in.spce_sw: { folder: examples/PACKAGES/manybody_table, status: "completed, 1 rel thermo checks failed", walltime: 9.0, walltime_norm: 1.5 } +in.spce: { folder: examples/PACKAGES/manybody_table, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 5.0, walltime_norm: 0.8333333333333334 } +in.spce2: { folder: examples/PACKAGES/manybody_table, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 6.0, walltime_norm: 1.0 } +in.spce_sw: { folder: examples/PACKAGES/manybody_table, status: "completed", failed_checks: { abs_diff_failed: 1, rel_diff_failed: 1 }, walltime: 5.0, walltime_norm: 0.8333333333333334 } in.confined: { folder: examples/PACKAGES/dielectric, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.nopbc: { folder: examples/PACKAGES/dielectric, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } -in.methane_qtb: { folder: examples/PACKAGES/qtb/methane_qtb, status: "completed, thermo checks passed", walltime: 19.0, walltime_norm: 3.1666666666666665 } -in.alpha_quartz_qtb: { folder: examples/PACKAGES/qtb/alpha_quartz_qtb, status: "completed, 2 rel thermo checks failed", walltime: 27.0, walltime_norm: 4.5 } -in.alpha_quartz_qbmsst: { folder: examples/PACKAGES/qtb/alpha_quartz_qbmsst, status: "failed, no Total wall time in the output.", walltime: -1 } -in.methane_qbmsst: { folder: examples/PACKAGES/qtb/methane_qbmsst, status: "failed, no Total wall time in the output.", walltime: -1 } -in.tmd: { folder: examples/PACKAGES/tmd, status: "completed, error parsing log.lammps into YAML", walltime: 10.0, walltime_norm: 1.6666666666666667 } -in.meam-spline.Si: { folder: examples/PACKAGES/meam_spline, status: "completed, thermo checks passed", walltime: 4.0, walltime_norm: 0.6666666666666666 } -in.meam-spline.TiO2: { folder: examples/PACKAGES/meam_spline, status: "failed, no Total wall time in the output.", walltime: -1 } +in.methane_qtb: { folder: examples/PACKAGES/qtb/methane_qtb, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 9.0, walltime_norm: 1.5 } +in.alpha_quartz_qtb: { folder: examples/PACKAGES/qtb/alpha_quartz_qtb, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 2 }, walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.alpha_quartz_qbmsst: { folder: examples/PACKAGES/qtb/alpha_quartz_qbmsst, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 42.0, walltime_norm: 7.0 } +in.methane_qbmsst: { folder: examples/PACKAGES/qtb/methane_qbmsst, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 97.0, walltime_norm: 16.166666666666668 } +in.tmd: { folder: examples/PACKAGES/tmd, status: "completed, error parsing log.lammps into YAML", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.meam-spline.Si: { folder: examples/PACKAGES/meam_spline, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.meam-spline.TiO2: { folder: examples/PACKAGES/meam_spline, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 79.0, walltime_norm: 13.166666666666666 } in.silicon: { folder: examples/PACKAGES/phonon/dynamical_matrix_command/Silicon, status: "completed, but no Step nor Loop in the output.", walltime: 0.0, walltime_norm: 0.0 } -in.EAM3D: { folder: examples/PACKAGES/phonon/3-3D-FCC-Cu-EAM, status: "failed, no Total wall time in the output.", walltime: -1 } -in.disp: { folder: examples/PACKAGES/phonon/3-3D-FCC-Cu-EAM, status: "failed, unknown command, package not installed, ERROR: Unknown command: 1 (src/input.cpp:314)", walltime: -1 } -in.disp2: { folder: examples/PACKAGES/phonon/3-3D-FCC-Cu-EAM, status: "failed, unknown command, package not installed, ERROR: Unknown command: 1 (src/input.cpp:314)", walltime: -1 } +in.EAM3D: { folder: examples/PACKAGES/phonon/3-3D-FCC-Cu-EAM, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.disp: { folder: examples/PACKAGES/phonon/3-3D-FCC-Cu-EAM, status: "skipped", walltime: -2 } +in.disp2: { folder: examples/PACKAGES/phonon/3-3D-FCC-Cu-EAM, status: "skipped", walltime: -2 } in.dos: { folder: examples/PACKAGES/phonon/3-3D-FCC-Cu-EAM, status: "skipped", walltime: -2 } -in.Ana: { folder: examples/PACKAGES/phonon/1-1D-mono, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 9.0, walltime_norm: 1.5 } -in.disp: { folder: examples/PACKAGES/phonon/1-1D-mono, status: "failed, unknown command, package not installed, ERROR: Unknown command: 1 (src/input.cpp:314)", walltime: -1 } -in.Ana: { folder: examples/PACKAGES/phonon/2-1D-diatomic, status: "completed, thermo checks passed", walltime: 24.0, walltime_norm: 4.0 } -in.disp: { folder: examples/PACKAGES/phonon/2-1D-diatomic, status: "failed, unknown command, package not installed, ERROR: Unknown command: 10 (src/input.cpp:314)", walltime: -1 } -in.disp: { folder: examples/PACKAGES/phonon/4-Graphene, status: "failed, unknown command, package not installed, ERROR: Unknown command: 100 (src/input.cpp:314)", walltime: -1 } -in.graphene: { folder: examples/PACKAGES/phonon/4-Graphene, status: "failed, no Total wall time in the output.", walltime: -1 } +in.Ana: { folder: examples/PACKAGES/phonon/1-1D-mono, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 6.0, walltime_norm: 1.0 } +in.disp: { folder: examples/PACKAGES/phonon/1-1D-mono, status: "skipped", walltime: -2 } +in.Ana: { folder: examples/PACKAGES/phonon/2-1D-diatomic, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 17.0, walltime_norm: 2.8333333333333335 } +in.disp: { folder: examples/PACKAGES/phonon/2-1D-diatomic, status: "skipped", walltime: -2 } +in.disp: { folder: examples/PACKAGES/phonon/4-Graphene, status: "skipped", walltime: -2 } +in.graphene: { folder: examples/PACKAGES/phonon/4-Graphene, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } in.dpde-shardlow: { folder: examples/PACKAGES/dpd-react/dpde-shardlow, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } -in.dpde-vv: { folder: examples/PACKAGES/dpd-react/dpde-vv, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 18.0, walltime_norm: 3.0 } +in.dpde-vv: { folder: examples/PACKAGES/dpd-react/dpde-vv, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 14.0, walltime_norm: 2.3333333333333335 } in.dpd-shardlow: { folder: examples/PACKAGES/dpd-react/dpd-shardlow, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } -in.dpd-vv: { folder: examples/PACKAGES/dpd-react/dpd-vv, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 14.0, walltime_norm: 2.3333333333333335 } +in.dpd-vv: { folder: examples/PACKAGES/dpd-react/dpd-vv, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 11.0, walltime_norm: 1.8333333333333333 } in.dpdp-shardlow: { folder: examples/PACKAGES/dpd-react/dpdp-shardlow, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } -in.multi-lucy: { folder: examples/PACKAGES/dpd-react/multi-lucy, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 2.0, walltime_norm: 0.3333333333333333 } -in.dpdh-shardlow: { folder: examples/PACKAGES/dpd-react/dpdh-shardlow, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.multi-lucy: { folder: examples/PACKAGES/dpd-react/multi-lucy, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.dpdh-shardlow: { folder: examples/PACKAGES/dpd-react/dpdh-shardlow, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 3.0, walltime_norm: 0.5 } in.dpdrx-shardlow: { folder: examples/PACKAGES/dpd-react/dpdrx-shardlow, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } -in.GD: { folder: examples/PACKAGES/flow_gauss, status: "completed, thermo checks passed", walltime: 2.0, walltime_norm: 0.3333333333333333 } -in.basal: { folder: examples/PACKAGES/basal, status: "failed, no Total wall time in the output.", walltime: -1 } +in.GD: { folder: examples/PACKAGES/flow_gauss, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.basal: { folder: examples/PACKAGES/basal, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } in.cascade_AlCu: { folder: examples/PACKAGES/electron_stopping, status: "failed, ERROR: Must set 'extscalar' when setting 'scalar_flag' for fix electron/stopping/fit. Contact the developer. (src/fix.cpp:135).", walltime: -1 } in.cascade_SiSi: { folder: examples/PACKAGES/electron_stopping, status: "failed, ERROR: Must set 'extscalar' when setting 'scalar_flag' for fix electron/stopping/fit. Contact the developer. (src/fix.cpp:135).", walltime: -1 } -in.elstop: { folder: examples/PACKAGES/electron_stopping, status: "completed, thermo checks passed", walltime: 33.0, walltime_norm: 5.5 } -in.elstop.only: { folder: examples/PACKAGES/electron_stopping, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } -in.chreg-acid: { folder: examples/PACKAGES/charge_regulation, status: "completed, 1 rel thermo checks failed", walltime: 2.0, walltime_norm: 0.3333333333333333 } -in.chreg-acid-real: { folder: examples/PACKAGES/charge_regulation, status: "completed, 1 rel thermo checks failed", walltime: 1.0, walltime_norm: 0.16666666666666666 } -in.chreg-polymer: { folder: examples/PACKAGES/charge_regulation, status: "completed, 1 rel thermo checks failed", walltime: 2.0, walltime_norm: 0.3333333333333333 } -in.adatom: { folder: examples/PACKAGES/agni, status: "completed, thermo checks passed", walltime: 27.0, walltime_norm: 4.5 } -in.vacancy: { folder: examples/PACKAGES/agni, status: "completed, thermo checks passed", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.elstop: { folder: examples/PACKAGES/electron_stopping, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 26.0, walltime_norm: 4.333333333333333 } +in.elstop.only: { folder: examples/PACKAGES/electron_stopping, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.chreg-acid: { folder: examples/PACKAGES/charge_regulation, status: "completed", failed_checks: { abs_diff_failed: 1, rel_diff_failed: 1 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.chreg-acid-real: { folder: examples/PACKAGES/charge_regulation, status: "completed", failed_checks: { abs_diff_failed: 1, rel_diff_failed: 1 }, walltime: 0.0, walltime_norm: 0.0 } +in.chreg-polymer: { folder: examples/PACKAGES/charge_regulation, status: "completed", failed_checks: { abs_diff_failed: 1, rel_diff_failed: 1 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.adatom: { folder: examples/PACKAGES/agni, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 16.0, walltime_norm: 2.6666666666666665 } +in.vacancy: { folder: examples/PACKAGES/agni, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } in.bucky-plus-cnt: { folder: examples/PACKAGES/imd, status: "skipped", walltime: -2 } in.bucky-plus-cnt-gpu: { folder: examples/PACKAGES/imd, status: "skipped", walltime: -2 } in.deca-ala-solv-filter_imd: { folder: examples/PACKAGES/imd, status: "skipped", walltime: -2 } @@ -280,35 +331,35 @@ in.vac0-bcc: { folder: examples/PACKAGES/mgpt, status: "failed, unrecognized com in.vacmin-bcc: { folder: examples/PACKAGES/mgpt, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized pair style 'mgpt' is part of the MGPT package which is not enabled in this LAMMPS binary. (src/force.cpp:275)", walltime: -1 } in.vtk: { folder: examples/PACKAGES/vtk, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized dump style 'vtk' is part of the VTK package which is not enabled in this LAMMPS binary. (src/output.cpp:776)", walltime: -1 } in.vtp: { folder: examples/PACKAGES/vtk, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized dump style 'vtk' is part of the VTK package which is not enabled in this LAMMPS binary. (src/output.cpp:776)", walltime: -1 } -in.dpdext: { folder: examples/PACKAGES/dpd-basic/dpdext, status: "completed, 2 rel thermo checks failed", walltime: 1.0, walltime_norm: 0.16666666666666666 } -in.dpd: { folder: examples/PACKAGES/dpd-basic/dpd, status: "completed, 2 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } -in.dpdext_tstat: { folder: examples/PACKAGES/dpd-basic/dpdext_tstat, status: "completed, thermo checks passed", walltime: 30.0, walltime_norm: 5.0 } -in.dpd_tstat: { folder: examples/PACKAGES/dpd-basic/dpd_tstat, status: "completed, thermo checks passed", walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.dpdext: { folder: examples/PACKAGES/dpd-basic/dpdext, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 2 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.dpd: { folder: examples/PACKAGES/dpd-basic/dpd, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 2 }, walltime: 0.0, walltime_norm: 0.0 } +in.dpdext_tstat: { folder: examples/PACKAGES/dpd-basic/dpdext_tstat, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 30.0, walltime_norm: 5.0 } +in.dpd_tstat: { folder: examples/PACKAGES/dpd-basic/dpd_tstat, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 11.0, walltime_norm: 1.8333333333333333 } in.dpd_coul_slater_long: { folder: examples/PACKAGES/dpd-basic/dpd_coul_slater_long, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 2.0, walltime_norm: 0.3333333333333333 } -in.piston: { folder: examples/PACKAGES/electrode/piston, status: "failed, no Total wall time in the output.", walltime: -1 } +in.piston: { folder: examples/PACKAGES/electrode/piston, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } in.cg: { folder: examples/PACKAGES/electrode/madelung, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } -in.eta: { folder: examples/PACKAGES/electrode/madelung, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.eta_cg: { folder: examples/PACKAGES/electrode/madelung, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.eta_mix: { folder: examples/PACKAGES/electrode/madelung, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.eta: { folder: examples/PACKAGES/electrode/madelung, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.eta_cg: { folder: examples/PACKAGES/electrode/madelung, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.eta_mix: { folder: examples/PACKAGES/electrode/madelung, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } in.ewald-ew2d: { folder: examples/PACKAGES/electrode/madelung, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.ewald-ew3dc: { folder: examples/PACKAGES/electrode/madelung, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.ewald-ffield: { folder: examples/PACKAGES/electrode/madelung, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.pppm-ew3dc: { folder: examples/PACKAGES/electrode/madelung, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.pppm-ffield: { folder: examples/PACKAGES/electrode/madelung, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } -in.ffield: { folder: examples/PACKAGES/electrode/au-aq, status: "failed, no Total wall time in the output.", walltime: -1 } -in.tf: { folder: examples/PACKAGES/electrode/au-aq, status: "failed, no Total wall time in the output.", walltime: -1 } -in.conp: { folder: examples/PACKAGES/electrode/graph-il, status: "failed, no Total wall time in the output.", walltime: -1 } -in.conq: { folder: examples/PACKAGES/electrode/graph-il, status: "failed, no Total wall time in the output.", walltime: -1 } -in.conq2: { folder: examples/PACKAGES/electrode/graph-il, status: "failed, no Total wall time in the output.", walltime: -1 } -in.etypes: { folder: examples/PACKAGES/electrode/graph-il, status: "failed, no Total wall time in the output.", walltime: -1 } -in.ffield: { folder: examples/PACKAGES/electrode/graph-il, status: "failed, no Total wall time in the output.", walltime: -1 } -in.ramp: { folder: examples/PACKAGES/electrode/graph-il, status: "failed, no Total wall time in the output.", walltime: -1 } -in.thermo: { folder: examples/PACKAGES/electrode/graph-il, status: "failed, no Total wall time in the output.", walltime: -1 } -in.planar-ewald-ew2d: { folder: examples/PACKAGES/electrode/planar, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.planar-ewald-ew3dc: { folder: examples/PACKAGES/electrode/planar, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.planar-ewald-ffield: { folder: examples/PACKAGES/electrode/planar, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.planar-pppm-ew3dc: { folder: examples/PACKAGES/electrode/planar, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } -in.planar-pppm-ffield: { folder: examples/PACKAGES/electrode/planar, status: "completed, thermo checks passed", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.ffield: { folder: examples/PACKAGES/electrode/au-aq, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.tf: { folder: examples/PACKAGES/electrode/au-aq, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.conp: { folder: examples/PACKAGES/electrode/graph-il, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 96.0, walltime_norm: 16.0 } +in.conq: { folder: examples/PACKAGES/electrode/graph-il, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 90.0, walltime_norm: 15.0 } +in.conq2: { folder: examples/PACKAGES/electrode/graph-il, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 62.0, walltime_norm: 10.333333333333334 } +in.etypes: { folder: examples/PACKAGES/electrode/graph-il, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 58.0, walltime_norm: 9.666666666666666 } +in.ffield: { folder: examples/PACKAGES/electrode/graph-il, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 41.0, walltime_norm: 6.833333333333333 } +in.ramp: { folder: examples/PACKAGES/electrode/graph-il, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 60.0, walltime_norm: 10.0 } +in.thermo: { folder: examples/PACKAGES/electrode/graph-il, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 57.0, walltime_norm: 9.5 } +in.planar-ewald-ew2d: { folder: examples/PACKAGES/electrode/planar, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.planar-ewald-ew3dc: { folder: examples/PACKAGES/electrode/planar, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.planar-ewald-ffield: { folder: examples/PACKAGES/electrode/planar, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.planar-pppm-ew3dc: { folder: examples/PACKAGES/electrode/planar, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.planar-pppm-ffield: { folder: examples/PACKAGES/electrode/planar, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } in.convective_pulse: { folder: examples/PACKAGES/atc/drift_diffusion, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } in.ddm_schrodinger: { folder: examples/PACKAGES/atc/drift_diffusion, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } in.finite_well: { folder: examples/PACKAGES/atc/drift_diffusion, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } @@ -377,7 +428,15 @@ in.bar1d_flux: { folder: examples/PACKAGES/atc/elastic, status: "failed, unrecog in.bar1d_frac_step: { folder: examples/PACKAGES/atc/elastic, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } in.bar1d_ghost_flux: { folder: examples/PACKAGES/atc/elastic, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } in.bar1d_thermo_elastic: { folder: examples/PACKAGES/atc/elastic, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } -in.eam_energy: { folder: examples/PACKAGES/atc/elastic, status: "failed, no Total wall time in the output.", walltime: -1 } +in.eam_energy: { folder: examples/PACKAGES/atc/elastic, status: "failed, no Total wall time in the output, -------------------------------------------------------------------------- +MPI_ABORT was invoked on rank 0 in communicator MPI_COMM_WORLD +with errorcode 1. + +NOTE: invoking MPI_ABORT causes Open MPI to kill all MPI processes. +You may or may not see output from other processes, depending on +exactly when Open MPI kills them. +-------------------------------------------------------------------------- +", walltime: -1 } in.electron_density: { folder: examples/PACKAGES/atc/elastic, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } in.no_atoms: { folder: examples/PACKAGES/atc/elastic, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } in.no_atoms_cb: { folder: examples/PACKAGES/atc/elastic, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'atc' is part of the ATC package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } @@ -394,72 +453,72 @@ in.polymer: { folder: examples/PACKAGES/latboltz/polymer, status: "failed, unrec in.confined_colloids: { folder: examples/PACKAGES/latboltz/confined_colloid, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'lb/fluid' is part of the LATBOLTZ package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } in.trapnewsphere: { folder: examples/PACKAGES/latboltz/diffusingsphere, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'lb/fluid' is part of the LATBOLTZ package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } in.translocation: { folder: examples/PACKAGES/latboltz/translocation, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'lb/fluid' is part of the LATBOLTZ package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } -in.toycar: { folder: examples/PACKAGES/latboltz/toycar, status: "failed, no Total wall time in the output.", walltime: -1 } +in.toycar: { folder: examples/PACKAGES/latboltz/toycar, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } in.microrheology: { folder: examples/PACKAGES/latboltz/microrheology, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'lb/fluid' is part of the LATBOLTZ package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } in.dragtest: { folder: examples/PACKAGES/latboltz/dragforce, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'lb/fluid' is part of the LATBOLTZ package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } in.planewall: { folder: examples/PACKAGES/latboltz/planewall, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'lb/fluid' is part of the LATBOLTZ package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } -in.compute: { folder: examples/PACKAGES/pace/compute, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.pace.product: { folder: examples/PACKAGES/pace, status: "completed, thermo checks passed", walltime: 16.0, walltime_norm: 2.6666666666666665 } -in.pace.recursive: { folder: examples/PACKAGES/pace, status: "completed, thermo checks passed", walltime: 12.0, walltime_norm: 2.0 } -in.addtorque: { folder: examples/PACKAGES/addtorque, status: "failed, no Total wall time in the output.", walltime: -1 } -in.cnp: { folder: examples/PACKAGES/cnp, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 29.0, walltime_norm: 4.833333333333333 } -in.CH4fc.ang: { folder: examples/PACKAGES/eff/fixed-core/CH4, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 5.0, walltime_norm: 0.8333333333333334 } +in.compute: { folder: examples/PACKAGES/pace/compute, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.pace.product: { folder: examples/PACKAGES/pace, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 9.0, walltime_norm: 1.5 } +in.pace.recursive: { folder: examples/PACKAGES/pace, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 7.0, walltime_norm: 1.1666666666666667 } +in.addtorque: { folder: examples/PACKAGES/addtorque, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 3 }, walltime: 51.0, walltime_norm: 8.5 } +in.cnp: { folder: examples/PACKAGES/cnp, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 18.0, walltime_norm: 3.0 } +in.CH4fc.ang: { folder: examples/PACKAGES/eff/fixed-core/CH4, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 3.0, walltime_norm: 0.5 } in.CH4fc.bohr: { folder: examples/PACKAGES/eff/fixed-core/CH4, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.CH4fc.spe.ang: { folder: examples/PACKAGES/eff/fixed-core/CH4, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.CH4fc.spe.bohr: { folder: examples/PACKAGES/eff/fixed-core/CH4, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.C2H6fc.ang: { folder: examples/PACKAGES/eff/fixed-core/C2H6, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 2.0, walltime_norm: 0.3333333333333333 } in.C2H6fc.bohr: { folder: examples/PACKAGES/eff/fixed-core/C2H6, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } -in.ch4.dynamics: { folder: examples/PACKAGES/eff/CH4, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.ch4.min: { folder: examples/PACKAGES/eff/CH4, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.ch4_ionized.dynamics: { folder: examples/PACKAGES/eff/CH4, status: "completed, thermo checks passed", walltime: 16.0, walltime_norm: 2.6666666666666665 } +in.ch4.dynamics: { folder: examples/PACKAGES/eff/CH4, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.ch4.min: { folder: examples/PACKAGES/eff/CH4, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.ch4_ionized.dynamics: { folder: examples/PACKAGES/eff/CH4, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 16.0, walltime_norm: 2.6666666666666665 } in.Be-solid.spe: { folder: examples/PACKAGES/eff/Be-solid, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 14.0, walltime_norm: 2.3333333333333335 } in.adamantane_ionized.nve: { folder: examples/PACKAGES/eff/Auger-Adamantane, status: "failed, ERROR: Lost atoms: original 101 current 100 (src/thermo.cpp:494).", walltime: -1 } in.SiH4: { folder: examples/PACKAGES/eff/ECP/SiH4, status: "completed, error parsing log.lammps into YAML", walltime: 0.0, walltime_norm: 0.0 } in.SiH4.ang: { folder: examples/PACKAGES/eff/ECP/SiH4, status: "completed, error parsing log.lammps into YAML", walltime: 0.0, walltime_norm: 0.0 } -in.Si2H6: { folder: examples/PACKAGES/eff/ECP/Si2H6, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.Si2H6.ang: { folder: examples/PACKAGES/eff/ECP/Si2H6, status: "completed, thermo checks passed", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.Si2H6: { folder: examples/PACKAGES/eff/ECP/Si2H6, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.Si2H6.ang: { folder: examples/PACKAGES/eff/ECP/Si2H6, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 4.0, walltime_norm: 0.6666666666666666 } in.SiC: { folder: examples/PACKAGES/eff/ECP/SiC/bulk, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 31.0, walltime_norm: 5.166666666666667 } -in.h2bulk.npt: { folder: examples/PACKAGES/eff/H_plasma, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 47.0, walltime_norm: 7.833333333333333 } -in.h2bulk.nve: { folder: examples/PACKAGES/eff/H_plasma, status: "failed, no Total wall time in the output.", walltime: -1 } -in.h2bulk.nve.ang: { folder: examples/PACKAGES/eff/H_plasma, status: "failed, no Total wall time in the output.", walltime: -1 } +in.h2bulk.npt: { folder: examples/PACKAGES/eff/H_plasma, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 46.0, walltime_norm: 7.666666666666667 } +in.h2bulk.nve: { folder: examples/PACKAGES/eff/H_plasma, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 78.0, walltime_norm: 13.0 } +in.h2bulk.nve.ang: { folder: examples/PACKAGES/eff/H_plasma, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 87.0, walltime_norm: 14.5 } in.Li-dendritic.min: { folder: examples/PACKAGES/eff/Li-dendritic, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 35.0, walltime_norm: 5.833333333333333 } -in.Li-dendritic.nvt: { folder: examples/PACKAGES/eff/Li-dendritic, status: "failed, no Total wall time in the output.", walltime: -1 } -in.Li.ang: { folder: examples/PACKAGES/eff/Li-solid, status: "failed, no Total wall time in the output.", walltime: -1 } +in.Li-dendritic.nvt: { folder: examples/PACKAGES/eff/Li-dendritic, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 69.0, walltime_norm: 11.5 } +in.Li.ang: { folder: examples/PACKAGES/eff/Li-solid, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 74.0, walltime_norm: 12.333333333333334 } in.Li.bohr: { folder: examples/PACKAGES/eff/Li-solid, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 53.0, walltime_norm: 8.833333333333334 } in.h2: { folder: examples/PACKAGES/eff/H2, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 4.0, walltime_norm: 0.6666666666666666 } in.h_atom.spe.ang: { folder: examples/PACKAGES/eff/H, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.h_atom.spe.bohr: { folder: examples/PACKAGES/eff/H, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } -in.npt_biaxial: { folder: examples/PACKAGES/uef/npt_biaxial, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } -in.nvt_uniaxial: { folder: examples/PACKAGES/uef/nvt_uniaxial, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } -in.crystal: { folder: examples/PACKAGES/rhok, status: "completed, 2 rel thermo checks failed", walltime: 24.0, walltime_norm: 4.0 } +in.npt_biaxial: { folder: examples/PACKAGES/uef/npt_biaxial, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 3.0, walltime_norm: 0.5 } +in.nvt_uniaxial: { folder: examples/PACKAGES/uef/nvt_uniaxial, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 3.0, walltime_norm: 0.5 } +in.crystal: { folder: examples/PACKAGES/rhok, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 2 }, walltime: 23.0, walltime_norm: 3.8333333333333335 } in.pinning: { folder: examples/PACKAGES/rhok, status: "failed, ERROR: Cannot open file data.halfhalf: No such file or directory (src/read_data.cpp:367).", walltime: -1 } -in.setup: { folder: examples/PACKAGES/rhok, status: "completed, 2 rel thermo checks failed", walltime: 17.0, walltime_norm: 2.8333333333333335 } +in.setup: { folder: examples/PACKAGES/rhok, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 2 }, walltime: 17.0, walltime_norm: 2.8333333333333335 } in.peptide-colvars: { folder: examples/PACKAGES/colvars, status: "completed, error parsing log.lammps into YAML", walltime: 4.0, walltime_norm: 0.6666666666666666 } in.peptide-colvars2: { folder: examples/PACKAGES/colvars, status: "completed, error parsing log.lammps into YAML", walltime: 1.0, walltime_norm: 0.16666666666666666 } -in.peptide-spring: { folder: examples/PACKAGES/colvars, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } -in.peptide-spring2: { folder: examples/PACKAGES/colvars, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.peptide-spring: { folder: examples/PACKAGES/colvars, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.peptide-spring2: { folder: examples/PACKAGES/colvars, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } in.hdnnp: { folder: examples/PACKAGES/hdnnp, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized pair style 'hdnnp' is part of the ML-HDNNP package which is not enabled in this LAMMPS binary. (src/force.cpp:275)", walltime: -1 } in.hybrid: { folder: examples/PACKAGES/hdnnp, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized pair style 'hdnnp' is part of the ML-HDNNP package which is not enabled in this LAMMPS binary. (src/force.cpp:275)", walltime: -1 } in.edip-Si: { folder: examples/PACKAGES/edip, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.edip-Si-multi: { folder: examples/PACKAGES/edip, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.edip-SiC: { folder: examples/PACKAGES/edip, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } -in.large_nylon_melt: { folder: examples/PACKAGES/reaction/nylon,6-6_melt, status: "failed, no Total wall time in the output.", walltime: -1 } -in.tiny_polystyrene.stabilized: { folder: examples/PACKAGES/reaction/tiny_polystyrene, status: "completed, 2 rel thermo checks failed", walltime: 18.0, walltime_norm: 3.0 } -in.tiny_epoxy.stabilized: { folder: examples/PACKAGES/reaction/tiny_epoxy, status: "completed, 1 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } -in.grow_styrene: { folder: examples/PACKAGES/reaction/create_atoms_polystyrene, status: "completed, 2 rel thermo checks failed", walltime: 7.0, walltime_norm: 1.1666666666666667 } +in.large_nylon_melt: { folder: examples/PACKAGES/reaction/nylon,6-6_melt, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 2 }, walltime: 60.0, walltime_norm: 10.0 } +in.tiny_polystyrene.stabilized: { folder: examples/PACKAGES/reaction/tiny_polystyrene, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 2 }, walltime: 18.0, walltime_norm: 3.0 } +in.tiny_epoxy.stabilized: { folder: examples/PACKAGES/reaction/tiny_epoxy, status: "completed", failed_checks: { abs_diff_failed: 1, rel_diff_failed: 1 }, walltime: 0.0, walltime_norm: 0.0 } +in.grow_styrene: { folder: examples/PACKAGES/reaction/create_atoms_polystyrene, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 2 }, walltime: 6.0, walltime_norm: 1.0 } in.tiny_nylon.stabilized: { folder: examples/PACKAGES/reaction/tiny_nylon, status: "failed, unknown command, package not installed, ERROR: Unknown command: react rxn2 all 1 0.0 5.0 mol3 mol4 rxn1_stp2_map rescale_charges yes (src/input.cpp:314)", walltime: -1 } -in.tiny_nylon.stabilized_variable_probability: { folder: examples/PACKAGES/reaction/tiny_nylon, status: "completed, 2 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } -in.tiny_nylon.unstabilized: { folder: examples/PACKAGES/reaction/tiny_nylon, status: "completed, 2 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } -in.BulkNi: { folder: examples/PACKAGES/diffraction, status: "failed, no Total wall time in the output.", walltime: -1 } -in.tdpd: { folder: examples/PACKAGES/dpd-meso/tdpd, status: "failed, no Total wall time in the output.", walltime: -1 } -in.tdpd-region: { folder: examples/PACKAGES/dpd-meso/tdpd, status: "failed, no Total wall time in the output.", walltime: -1 } -in.mdpd: { folder: examples/PACKAGES/dpd-meso/mdpd, status: "failed, no Total wall time in the output.", walltime: -1 } -in.edpd: { folder: examples/PACKAGES/dpd-meso/edpd, status: "failed, no Total wall time in the output.", walltime: -1 } -in.edpd-region: { folder: examples/PACKAGES/dpd-meso/edpd, status: "failed, no Total wall time in the output.", walltime: -1 } -in.cylinder: { folder: examples/PACKAGES/stressprofile, status: "completed, thermo checks passed", walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.tiny_nylon.stabilized_variable_probability: { folder: examples/PACKAGES/reaction/tiny_nylon, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 2 }, walltime: 0.0, walltime_norm: 0.0 } +in.tiny_nylon.unstabilized: { folder: examples/PACKAGES/reaction/tiny_nylon, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 2 }, walltime: 0.0, walltime_norm: 0.0 } +in.BulkNi: { folder: examples/PACKAGES/diffraction, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 158.0, walltime_norm: 26.333333333333332 } +in.tdpd: { folder: examples/PACKAGES/dpd-meso/tdpd, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 64.0, walltime_norm: 10.666666666666666 } +in.tdpd-region: { folder: examples/PACKAGES/dpd-meso/tdpd, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 63.0, walltime_norm: 10.5 } +in.mdpd: { folder: examples/PACKAGES/dpd-meso/mdpd, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 100.0, walltime_norm: 16.666666666666668 } +in.edpd: { folder: examples/PACKAGES/dpd-meso/edpd, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 87.0, walltime_norm: 14.5 } +in.edpd-region: { folder: examples/PACKAGES/dpd-meso/edpd, status: "completed", failed_checks: { abs_diff_failed: 6, rel_diff_failed: 6 }, walltime: 87.0, walltime_norm: 14.5 } +in.cylinder: { folder: examples/PACKAGES/stressprofile, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 4.0, walltime_norm: 0.6666666666666666 } in.flat: { folder: examples/PACKAGES/stressprofile, status: "failed, ERROR: Illegal compute stress/cartesian command: missing argument(s) (src/EXTRA-COMPUTE/compute_stress_cartesian.cpp:65).", walltime: -1 } -in.sphere: { folder: examples/PACKAGES/stressprofile, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } -in.srp: { folder: examples/PACKAGES/srp, status: "completed, thermo checks passed", walltime: 6.0, walltime_norm: 1.0 } +in.sphere: { folder: examples/PACKAGES/stressprofile, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.srp: { folder: examples/PACKAGES/srp, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 6.0, walltime_norm: 1.0 } in.scafacos: { folder: examples/PACKAGES/scafacos, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized kspace style 'scafacos' is part of the SCAFACOS package which is not enabled in this LAMMPS binary. (src/force.cpp:660)", walltime: -1 } in.scafacos.cw.ewald: { folder: examples/PACKAGES/scafacos, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized kspace style 'scafacos' is part of the SCAFACOS package which is not enabled in this LAMMPS binary. (src/force.cpp:660)", walltime: -1 } in.scafacos.cw.fmm: { folder: examples/PACKAGES/scafacos, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized kspace style 'scafacos' is part of the SCAFACOS package which is not enabled in this LAMMPS binary. (src/force.cpp:660)", walltime: -1 } @@ -474,161 +533,217 @@ in.scafacos.p2nfft: { folder: examples/PACKAGES/scafacos, status: "failed, unrec in.scafacos.p3m: { folder: examples/PACKAGES/scafacos, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized kspace style 'scafacos' is part of the SCAFACOS package which is not enabled in this LAMMPS binary. (src/force.cpp:660)", walltime: -1 } in.h_atom: { folder: examples/PACKAGES/awpmd, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized atom style 'wavepacket' is part of the AWPMD package which is not enabled in this LAMMPS binary. (src/atom.cpp:745)", walltime: -1 } in.h_molecule: { folder: examples/PACKAGES/awpmd, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized atom style 'wavepacket' is part of the AWPMD package which is not enabled in this LAMMPS binary. (src/atom.cpp:745)", walltime: -1 } -in.gold_gr: { folder: examples/PACKAGES/interlayer/saip_metal, status: "completed, thermo checks passed", walltime: 10.0, walltime_norm: 1.6666666666666667 } -in.atom-diffusion: { folder: examples/PACKAGES/interlayer/kolmogorov_crespi_z, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.bilayer-graphene: { folder: examples/PACKAGES/interlayer/kolmogorov_crespi_z, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } -in.gr_water: { folder: examples/PACKAGES/interlayer/aip_water_2dm, status: "completed, 2 rel thermo checks failed", walltime: 19.0, walltime_norm: 3.1666666666666665 } -in.gr_water.opt: { folder: examples/PACKAGES/interlayer/aip_water_2dm, status: "completed, 2 rel thermo checks failed", walltime: 10.0, walltime_norm: 1.6666666666666667 } -in.bilayer-graphene: { folder: examples/PACKAGES/interlayer/kolmogorov_crespi_full, status: "failed, no Total wall time in the output.", walltime: -1 } -in.CH_drip: { folder: examples/PACKAGES/interlayer/drip, status: "completed, 1 rel thermo checks failed", walltime: 1.0, walltime_norm: 0.16666666666666666 } -in.C_drip: { folder: examples/PACKAGES/interlayer/drip, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } -in.mos2: { folder: examples/PACKAGES/interlayer/ilp_tmds, status: "completed, 1 rel thermo checks failed", walltime: 56.0, walltime_norm: 9.333333333333334 } -in.bilayer-graphene: { folder: examples/PACKAGES/interlayer/ilp_graphene_hbn, status: "failed, no Total wall time in the output.", walltime: -1 } -in.bilayer-hBN: { folder: examples/PACKAGES/interlayer/ilp_graphene_hbn, status: "failed, no Total wall time in the output.", walltime: -1 } -in.grhBN: { folder: examples/PACKAGES/interlayer/ilp_graphene_hbn, status: "failed, no Total wall time in the output.", walltime: -1 } -in.ilp_graphene_hbn: { folder: examples/PACKAGES/interlayer/ilp_graphene_hbn, status: "failed, no Total wall time in the output.", walltime: -1 } +in.gold_gr: { folder: examples/PACKAGES/interlayer/saip_metal, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.atom-diffusion: { folder: examples/PACKAGES/interlayer/kolmogorov_crespi_z, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.bilayer-graphene: { folder: examples/PACKAGES/interlayer/kolmogorov_crespi_z, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.gr_water: { folder: examples/PACKAGES/interlayer/aip_water_2dm, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 2 }, walltime: 19.0, walltime_norm: 3.1666666666666665 } +in.gr_water.opt: { folder: examples/PACKAGES/interlayer/aip_water_2dm, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 2 }, walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.bilayer-graphene: { folder: examples/PACKAGES/interlayer/kolmogorov_crespi_full, status: "failed, mismatched columns in the log files", walltime: 121.0, walltime_norm: 20.166666666666668 } +in.CH_drip: { folder: examples/PACKAGES/interlayer/drip, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 1 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.C_drip: { folder: examples/PACKAGES/interlayer/drip, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.mos2: { folder: examples/PACKAGES/interlayer/ilp_tmds, status: "completed", failed_checks: { abs_diff_failed: 1, rel_diff_failed: 1 }, walltime: 57.0, walltime_norm: 9.5 } +in.bilayer-graphene: { folder: examples/PACKAGES/interlayer/ilp_graphene_hbn, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 70.0, walltime_norm: 11.666666666666666 } +in.bilayer-hBN: { folder: examples/PACKAGES/interlayer/ilp_graphene_hbn, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 134.0, walltime_norm: 22.333333333333332 } +in.grhBN: { folder: examples/PACKAGES/interlayer/ilp_graphene_hbn, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 123.0, walltime_norm: 20.5 } +in.ilp_graphene_hbn: { folder: examples/PACKAGES/interlayer/ilp_graphene_hbn, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 129.0, walltime_norm: 21.5 } in.smatbAgCuPancake: { folder: examples/PACKAGES/smtbq, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.smatbBulkFCC: { folder: examples/PACKAGES/smtbq, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.smtbq.Al: { folder: examples/PACKAGES/smtbq, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 3.0, walltime_norm: 0.5 } -in.smtbq.Al2O3: { folder: examples/PACKAGES/smtbq, status: "failed, no Total wall time in the output.", walltime: -1 } -in.smtbq.TiO2: { folder: examples/PACKAGES/smtbq, status: "failed, no Total wall time in the output.", walltime: -1 } -in.slater: { folder: examples/PACKAGES/slater, status: "failed, no Total wall time in the output.", walltime: -1 } +in.smtbq.Al2O3: { folder: examples/PACKAGES/smtbq, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.smtbq.TiO2: { folder: examples/PACKAGES/smtbq, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.slater: { folder: examples/PACKAGES/slater, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } in.slcsa: { folder: examples/PACKAGES/sna_nnn_slcsa, status: "completed, error parsing log.lammps into YAML", walltime: 42.0, walltime_norm: 7.0 } -in.orient_eco: { folder: examples/PACKAGES/orient_eco, status: "failed, no Total wall time in the output.", walltime: -1 } +in.orient_eco: { folder: examples/PACKAGES/orient_eco, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 128.0, walltime_norm: 21.333333333333332 } in.entropy: { folder: examples/PACKAGES/entropy, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 13.0, walltime_norm: 2.1666666666666665 } in.bpti: { folder: examples/PACKAGES/filter_corotate, status: "completed, error parsing log.lammps into YAML", walltime: 24.0, walltime_norm: 4.0 } -in.peptide: { folder: examples/PACKAGES/filter_corotate, status: "failed, no Total wall time in the output.", walltime: -1 } -in.graphene: { folder: examples/PACKAGES/ipi, status: "failed, no Total wall time in the output.", walltime: -1 } +in.peptide: { folder: examples/PACKAGES/filter_corotate, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 98.0, walltime_norm: 16.333333333333332 } +in.graphene: { folder: examples/PACKAGES/ipi, status: "failed, no Total wall time in the output, -------------------------------------------------------------------------- +MPI_ABORT was invoked on rank 0 in communicator MPI_COMM_WORLD +with errorcode 1. + +NOTE: invoking MPI_ABORT causes Open MPI to kill all MPI processes. +You may or may not see output from other processes, depending on +exactly when Open MPI kills them. +-------------------------------------------------------------------------- +", walltime: -1 } in.gREM-npt: { folder: examples/PACKAGES/grem/lj-single, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.gREM-nvt: { folder: examples/PACKAGES/grem/lj-single, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.gREM: { folder: examples/PACKAGES/grem/lj-6rep, status: "failed, ERROR: Cannot open file restart_file: No such file or directory (src/read_data.cpp:367).", walltime: -1 } in.gREM-temper: { folder: examples/PACKAGES/grem/lj-temper, status: "failed, ERROR: World variable count doesn't match # of partitions (src/variable.cpp:255).", walltime: -1 } -in.compute_stress_mop: { folder: examples/PACKAGES/mop, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.fix_wall: { folder: examples/PACKAGES/ees, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.fix_wall_region: { folder: examples/PACKAGES/ees, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.ti_spring: { folder: examples/PACKAGES/ti, status: "failed, no Total wall time in the output.", walltime: -1 } -in.extep-bn: { folder: examples/PACKAGES/extep, status: "completed, 3 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } -in.toluene.lang: { folder: examples/PACKAGES/drude/toluene, status: "failed, no Total wall time in the output.", walltime: -1 } -in.toluene.nh: { folder: examples/PACKAGES/drude/toluene, status: "failed, no Total wall time in the output.", walltime: -1 } -in.butane.lang: { folder: examples/PACKAGES/drude/butane, status: "completed, 3 rel thermo checks failed", walltime: 50.0, walltime_norm: 8.333333333333334 } -in.butane.nh: { folder: examples/PACKAGES/drude/butane, status: "completed, 5 rel thermo checks failed", walltime: 49.0, walltime_norm: 8.166666666666666 } -in.butane.tgnh: { folder: examples/PACKAGES/drude/butane, status: "completed, 4 rel thermo checks failed", walltime: 49.0, walltime_norm: 8.166666666666666 } -in.swm4-ndp.lang: { folder: examples/PACKAGES/drude/swm4-ndp, status: "failed, no Total wall time in the output.", walltime: -1 } -in.swm4-ndp.nh: { folder: examples/PACKAGES/drude/swm4-ndp, status: "completed, 5 rel thermo checks failed", walltime: 55.0, walltime_norm: 9.166666666666666 } -in.ethylene_glycol: { folder: examples/PACKAGES/drude/ethylene_glycol, status: "completed, 4 rel thermo checks failed", walltime: 25.0, walltime_norm: 4.166666666666667 } -in.ethanol.lang: { folder: examples/PACKAGES/drude/ethanol, status: "completed, 5 rel thermo checks failed", walltime: 43.0, walltime_norm: 7.166666666666667 } -in.ethanol.nh: { folder: examples/PACKAGES/drude/ethanol, status: "completed, 5 rel thermo checks failed", walltime: 42.0, walltime_norm: 7.0 } -in.ethanol.tgnh: { folder: examples/PACKAGES/drude/ethanol, status: "completed, 5 rel thermo checks failed", walltime: 44.0, walltime_norm: 7.333333333333333 } +in.compute_stress_mop: { folder: examples/PACKAGES/mop, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.fix_wall: { folder: examples/PACKAGES/ees, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.fix_wall_region: { folder: examples/PACKAGES/ees, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.ti_spring: { folder: examples/PACKAGES/ti, status: "failed, no Total wall time in the output, -------------------------------------------------------------------------- +MPI_ABORT was invoked on rank 0 in communicator MPI_COMM_WORLD +with errorcode 1. + +NOTE: invoking MPI_ABORT causes Open MPI to kill all MPI processes. +You may or may not see output from other processes, depending on +exactly when Open MPI kills them. +-------------------------------------------------------------------------- +", walltime: -1 } +in.extep-bn: { folder: examples/PACKAGES/extep, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 0.0, walltime_norm: 0.0 } +in.toluene.lang: { folder: examples/PACKAGES/drude/toluene, status: "completed", failed_checks: { abs_diff_failed: 4, rel_diff_failed: 5 }, walltime: 68.0, walltime_norm: 11.333333333333334 } +in.toluene.nh: { folder: examples/PACKAGES/drude/toluene, status: "completed", failed_checks: { abs_diff_failed: 5, rel_diff_failed: 5 }, walltime: 68.0, walltime_norm: 11.333333333333334 } +in.butane.lang: { folder: examples/PACKAGES/drude/butane, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 50.0, walltime_norm: 8.333333333333334 } +in.butane.nh: { folder: examples/PACKAGES/drude/butane, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 5 }, walltime: 49.0, walltime_norm: 8.166666666666666 } +in.butane.tgnh: { folder: examples/PACKAGES/drude/butane, status: "completed", failed_checks: { abs_diff_failed: 4, rel_diff_failed: 4 }, walltime: 49.0, walltime_norm: 8.166666666666666 } +in.swm4-ndp.lang: { folder: examples/PACKAGES/drude/swm4-ndp, status: "completed", failed_checks: { abs_diff_failed: 5, rel_diff_failed: 5 }, walltime: 65.0, walltime_norm: 10.833333333333334 } +in.swm4-ndp.nh: { folder: examples/PACKAGES/drude/swm4-ndp, status: "completed", failed_checks: { abs_diff_failed: 5, rel_diff_failed: 5 }, walltime: 54.0, walltime_norm: 9.0 } +in.ethylene_glycol: { folder: examples/PACKAGES/drude/ethylene_glycol, status: "completed", failed_checks: { abs_diff_failed: 4, rel_diff_failed: 4 }, walltime: 25.0, walltime_norm: 4.166666666666667 } +in.ethanol.lang: { folder: examples/PACKAGES/drude/ethanol, status: "completed", failed_checks: { abs_diff_failed: 5, rel_diff_failed: 5 }, walltime: 43.0, walltime_norm: 7.166666666666667 } +in.ethanol.nh: { folder: examples/PACKAGES/drude/ethanol, status: "completed", failed_checks: { abs_diff_failed: 5, rel_diff_failed: 5 }, walltime: 42.0, walltime_norm: 7.0 } +in.ethanol.tgnh: { folder: examples/PACKAGES/drude/ethanol, status: "completed", failed_checks: { abs_diff_failed: 5, rel_diff_failed: 5 }, walltime: 44.0, walltime_norm: 7.333333333333333 } in.force: { folder: examples/PACKAGES/tally, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 2.0, walltime_norm: 0.3333333333333333 } in.pe: { folder: examples/PACKAGES/tally, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 2.0, walltime_norm: 0.3333333333333333 } in.stress: { folder: examples/PACKAGES/tally, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 2.0, walltime_norm: 0.3333333333333333 } -in.system: { folder: examples/PACKAGES/momb, status: "completed, thermo checks passed", walltime: 13.0, walltime_norm: 2.1666666666666665 } +in.system: { folder: examples/PACKAGES/momb, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 13.0, walltime_norm: 2.1666666666666665 } in.momentum: { folder: examples/PACKAGES/momentum, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 41.0, walltime_norm: 6.833333333333333 } -in.alpha: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.alpha_relaxation: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.beta: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.energy_conservation.meam.sw: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "failed, no Total wall time in the output.", walltime: -1 } -in.fcc: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.hexagonal: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.omega: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.single_atom: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.bcc: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.bcc_relax: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.dc: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.dc_relax: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } -in.energy_conservation.meam.sw: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed, thermo checks passed", walltime: 11.0, walltime_norm: 1.8333333333333333 } -in.fcc: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.fcc_relax: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.hcp_relax: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.sc: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.sc_relax: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.single_atom: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.film_mesocnt: { folder: examples/PACKAGES/mesont, status: "failed, no Total wall time in the output.", walltime: -1 } -in.cauchystat: { folder: examples/PACKAGES/cauchy, status: "completed, 1 rel thermo checks failed", walltime: 1.0, walltime_norm: 0.16666666666666666 } -in.rubber_strip_pull: { folder: examples/PACKAGES/machdyn/rubber_strip_pull, status: "completed, thermo checks passed", walltime: 2.0, walltime_norm: 0.3333333333333333 } -in.aluminum_strip_pull: { folder: examples/PACKAGES/machdyn/aluminum_strip_pull, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } -in.funnel_flow: { folder: examples/PACKAGES/machdyn/funnel_flow, status: "completed, thermo checks passed", walltime: 29.0, walltime_norm: 4.833333333333333 } -in.fluid_structure_interaction: { folder: examples/PACKAGES/machdyn/fluid_structure_interaction, status: "completed, thermo checks passed", walltime: 32.0, walltime_norm: 5.333333333333333 } -in.rubber_rings_3d: { folder: examples/PACKAGES/machdyn/rubber_rings_3d, status: "completed, thermo checks passed", walltime: 21.0, walltime_norm: 3.5 } +in.alpha: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.alpha_relaxation: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.beta: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.energy_conservation.meam.sw: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 2 }, walltime: 74.0, walltime_norm: 12.333333333333334 } +in.fcc: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.hexagonal: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.omega: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.single_atom: { folder: examples/PACKAGES/meam_sw_spline/Ti, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.bcc: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.bcc_relax: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.dc: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.dc_relax: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.energy_conservation.meam.sw: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.fcc: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.fcc_relax: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.hcp_relax: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.sc: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.sc_relax: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.single_atom: { folder: examples/PACKAGES/meam_sw_spline/Si, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.film_mesocnt: { folder: examples/PACKAGES/mesont, status: "failed, no Total wall time in the output, -------------------------------------------------------------------------- +MPI_ABORT was invoked on rank 0 in communicator MPI_COMM_WORLD +with errorcode 1. + +NOTE: invoking MPI_ABORT causes Open MPI to kill all MPI processes. +You may or may not see output from other processes, depending on +exactly when Open MPI kills them. +-------------------------------------------------------------------------- +", walltime: -1 } +in.cauchystat: { folder: examples/PACKAGES/cauchy, status: "completed", failed_checks: { abs_diff_failed: 1, rel_diff_failed: 1 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.rubber_strip_pull: { folder: examples/PACKAGES/machdyn/rubber_strip_pull, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.aluminum_strip_pull: { folder: examples/PACKAGES/machdyn/aluminum_strip_pull, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.funnel_flow: { folder: examples/PACKAGES/machdyn/funnel_flow, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 29.0, walltime_norm: 4.833333333333333 } +in.fluid_structure_interaction: { folder: examples/PACKAGES/machdyn/fluid_structure_interaction, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 32.0, walltime_norm: 5.333333333333333 } +in.rubber_rings_3d: { folder: examples/PACKAGES/machdyn/rubber_rings_3d, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 21.0, walltime_norm: 3.5 } in.h2o-quantum: { folder: examples/PACKAGES/gle, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 13.0, walltime_norm: 2.1666666666666665 } in.h2o-smart: { folder: examples/PACKAGES/gle, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 13.0, walltime_norm: 2.1666666666666665 } -in.pafi: { folder: examples/PACKAGES/pafi, status: "failed, no Total wall time in the output.", walltime: -1 } +in.pafi: { folder: examples/PACKAGES/pafi, status: "failed, no Total wall time in the output, -------------------------------------------------------------------------- +MPI_ABORT was invoked on rank 0 in communicator MPI_COMM_WORLD +with errorcode 1. + +NOTE: invoking MPI_ABORT causes Open MPI to kill all MPI processes. +You may or may not see output from other processes, depending on +exactly when Open MPI kills them. +-------------------------------------------------------------------------- +", walltime: -1 } in.scp: { folder: examples/PACKAGES/pimd/prot-hairpin, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 15.0, walltime_norm: 2.5 } in.scp: { folder: examples/PACKAGES/pimd/para-h2, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 6.0, walltime_norm: 1.0 } in.lmp: { folder: examples/PACKAGES/pimd/langevin_reduced_units, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } -in.langevin.metal: { folder: examples/PACKAGES/pimd/langevin_metal_units, status: "completed, 2 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } -in.pimd-langevin.metal: { folder: examples/PACKAGES/pimd/langevin_metal_units, status: "completed, 2 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } -in.rann: { folder: examples/PACKAGES/rann, status: "failed, no Total wall time in the output.", walltime: -1 } +in.langevin.metal: { folder: examples/PACKAGES/pimd/langevin_metal_units, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 2 }, walltime: 0.0, walltime_norm: 0.0 } +in.pimd-langevin.metal: { folder: examples/PACKAGES/pimd/langevin_metal_units, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 2 }, walltime: 0.0, walltime_norm: 0.0 } +in.rann: { folder: examples/PACKAGES/rann, status: "failed, no Total wall time in the output, -------------------------------------------------------------------------- +MPI_ABORT was invoked on rank 0 in communicator MPI_COMM_WORLD +with errorcode 1. + +NOTE: invoking MPI_ABORT causes Open MPI to kill all MPI processes. +You may or may not see output from other processes, depending on +exactly when Open MPI kills them. +-------------------------------------------------------------------------- +", walltime: -1 } in.msd.2d: { folder: examples/DIFFUSE, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 36.0, walltime_norm: 6.0 } -in.vacf.2d: { folder: examples/DIFFUSE, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 38.0, walltime_norm: 6.333333333333333 } +in.vacf.2d: { folder: examples/DIFFUSE, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 39.0, walltime_norm: 6.5 } in.numdiff: { folder: examples/numdiff, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } -in.bpm.pour: { folder: examples/bpm/pour, status: "failed, no Total wall time in the output.", walltime: -1 } -in.bpm.impact.rotational: { folder: examples/bpm/impact, status: "failed, no Total wall time in the output.", walltime: -1 } +in.bpm.pour: { folder: examples/bpm/pour, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.bpm.impact.rotational: { folder: examples/bpm/impact, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 102.0, walltime_norm: 17.0 } in.bpm.impact.spring: { folder: examples/bpm/impact, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 12.0, walltime_norm: 2.0 } -in.rheo.balloon: { folder: examples/rheo/balloon, status: "failed, no Total wall time in the output.", walltime: -1 } -in.rheo.oxidation: { folder: examples/rheo/oxidation, status: "failed, no Total wall time in the output.", walltime: -1 } -in.rheo.taylor.green: { folder: examples/rheo/taylor-green, status: "failed, no Total wall time in the output.", walltime: -1 } -in.rheo.ice.cubes: { folder: examples/rheo/ice-cubes, status: "failed, no Total wall time in the output.", walltime: -1 } +in.rheo.balloon: { folder: examples/rheo/balloon, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.rheo.oxidation: { folder: examples/rheo/oxidation, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.rheo.taylor.green: { folder: examples/rheo/taylor-green, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 113.0, walltime_norm: 18.833333333333332 } +in.rheo.ice.cubes: { folder: examples/rheo/ice-cubes, status: "failed, no Total wall time in the output, -------------------------------------------------------------------------- +MPI_ABORT was invoked on rank 0 in communicator MPI_COMM_WORLD +with errorcode 1. + +NOTE: invoking MPI_ABORT causes Open MPI to kill all MPI processes. +You may or may not see output from other processes, depending on +exactly when Open MPI kills them. +-------------------------------------------------------------------------- +", walltime: -1 } in.rheo.poiseuille: { folder: examples/rheo/poiseuille, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 29.0, walltime_norm: 4.833333333333333 } -in.rheo.dam.break: { folder: examples/rheo/dam-break, status: "failed, no Total wall time in the output.", walltime: -1 } +in.rheo.dam.break: { folder: examples/rheo/dam-break, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 159.0, walltime_norm: 26.5 } in.peptide: { folder: examples/peptide, status: "completed, error parsing log.lammps into YAML", walltime: 4.0, walltime_norm: 0.6666666666666666 } -in.coreshell: { folder: examples/coreshell, status: "completed, 9 rel thermo checks failed", walltime: 10.0, walltime_norm: 1.6666666666666667 } -in.coreshell.dsf: { folder: examples/coreshell, status: "completed, 8 rel thermo checks failed", walltime: 20.0, walltime_norm: 3.3333333333333335 } -in.coreshell.thermostats: { folder: examples/coreshell, status: "completed, 14 rel thermo checks failed", walltime: 14.0, walltime_norm: 2.3333333333333335 } -in.coreshell.wolf: { folder: examples/coreshell, status: "completed, 5 rel thermo checks failed", walltime: 22.0, walltime_norm: 3.6666666666666665 } -in.marble_race: { folder: examples/mesh, status: "failed, no Total wall time in the output.", walltime: -1 } -in.mesh_box: { folder: examples/mesh, status: "completed, thermo checks passed", walltime: 10.0, walltime_norm: 1.6666666666666667 } -in.abcfire: { folder: examples/fire, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.abcfire_mod: { folder: examples/fire, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.cg: { folder: examples/fire, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.fire: { folder: examples/fire, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.fire_mod: { folder: examples/fire, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.meam.abcfire: { folder: examples/fire, status: "completed, thermo checks passed", walltime: 44.0, walltime_norm: 7.333333333333333 } -in.meam.fire: { folder: examples/fire, status: "failed, no Total wall time in the output.", walltime: -1 } +in.coreshell: { folder: examples/coreshell, status: "completed", failed_checks: { abs_diff_failed: 7, rel_diff_failed: 9 }, walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.coreshell.dsf: { folder: examples/coreshell, status: "completed", failed_checks: { abs_diff_failed: 7, rel_diff_failed: 8 }, walltime: 20.0, walltime_norm: 3.3333333333333335 } +in.coreshell.thermostats: { folder: examples/coreshell, status: "completed", failed_checks: { abs_diff_failed: 12, rel_diff_failed: 14 }, walltime: 14.0, walltime_norm: 2.3333333333333335 } +in.coreshell.wolf: { folder: examples/coreshell, status: "completed", failed_checks: { abs_diff_failed: 5, rel_diff_failed: 5 }, walltime: 22.0, walltime_norm: 3.6666666666666665 } +in.marble_race: { folder: examples/mesh, status: "completed", failed_checks: { abs_diff_failed: 5, rel_diff_failed: 5 }, walltime: 131.0, walltime_norm: 21.833333333333332 } +in.mesh_box: { folder: examples/mesh, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.abcfire: { folder: examples/fire, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.abcfire_mod: { folder: examples/fire, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.cg: { folder: examples/fire, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.fire: { folder: examples/fire, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.fire_mod: { folder: examples/fire, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.meam.abcfire: { folder: examples/fire, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 43.0, walltime_norm: 7.166666666666667 } +in.meam.fire: { folder: examples/fire, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 62.0, walltime_norm: 10.333333333333334 } in.neb.sivac.abcfire: { folder: examples/fire, status: "failed, ERROR: Cannot use NEB with a single replica (src/REPLICA/neb.cpp:141).", walltime: -1 } in.neb.sivac.abcfire_mod: { folder: examples/fire, status: "failed, ERROR: Cannot use NEB with a single replica (src/REPLICA/neb.cpp:141).", walltime: -1 } in.neb.sivac.fire: { folder: examples/fire, status: "failed, ERROR: Cannot use NEB with a single replica (src/REPLICA/neb.cpp:141).", walltime: -1 } in.neb.sivac.fire_mod: { folder: examples/fire, status: "failed, ERROR: Cannot use NEB with a single replica (src/REPLICA/neb.cpp:141).", walltime: -1 } in.neb.sivac.qm: { folder: examples/fire, status: "failed, ERROR: Cannot use NEB with a single replica (src/REPLICA/neb.cpp:141).", walltime: -1 } -in.bcc.orthog: { folder: examples/triclinic, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.bcc.primitive: { folder: examples/triclinic, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.data.general: { folder: examples/triclinic, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.fcc.orthog: { folder: examples/triclinic, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.fcc.primitive: { folder: examples/triclinic, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.hex.orthog: { folder: examples/triclinic, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.hex.primitive: { folder: examples/triclinic, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.sq2.orthog: { folder: examples/triclinic, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.sq2.primitive: { folder: examples/triclinic, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.tri.srd: { folder: examples/ASPHERE/tri, status: "failed, no Total wall time in the output.", walltime: -1 } -in.star: { folder: examples/ASPHERE/star, status: "completed, 3 rel thermo checks failed", walltime: 12.0, walltime_norm: 2.0 } -in.star.mp: { folder: examples/ASPHERE/star, status: "completed, 3 rel thermo checks failed", walltime: 12.0, walltime_norm: 2.0 } -in.box: { folder: examples/ASPHERE/box, status: "completed, 3 rel thermo checks failed", walltime: 18.0, walltime_norm: 3.0 } -in.box.mp: { folder: examples/ASPHERE/box, status: "completed, 3 rel thermo checks failed", walltime: 18.0, walltime_norm: 3.0 } -in.dimer: { folder: examples/ASPHERE/dimer, status: "completed, 3 rel thermo checks failed", walltime: 6.0, walltime_norm: 1.0 } -in.dimer.mp: { folder: examples/ASPHERE/dimer, status: "completed, 3 rel thermo checks failed", walltime: 16.0, walltime_norm: 2.6666666666666665 } -in.vesicle: { folder: examples/ASPHERE/vesicle, status: "completed, thermo checks passed", walltime: 10.0, walltime_norm: 1.6666666666666667 } -in.line: { folder: examples/ASPHERE/line, status: "failed, no Total wall time in the output.", walltime: -1 } -in.line.srd: { folder: examples/ASPHERE/line, status: "failed, no Total wall time in the output.", walltime: -1 } -in.poly: { folder: examples/ASPHERE/poly, status: "completed, thermo checks passed", walltime: 8.0, walltime_norm: 1.3333333333333333 } -in.poly.mp: { folder: examples/ASPHERE/poly, status: "completed, thermo checks passed", walltime: 9.0, walltime_norm: 1.5 } -in.flat_membrane: { folder: examples/ASPHERE/flat_membrane, status: "completed, 2 rel thermo checks failed", walltime: 7.0, walltime_norm: 1.1666666666666667 } -in.ellipsoid: { folder: examples/ASPHERE/ellipsoid, status: "completed, thermo checks passed", walltime: 9.0, walltime_norm: 1.5 } -in.ellipsoid.mp: { folder: examples/ASPHERE/ellipsoid, status: "completed, thermo checks passed", walltime: 9.0, walltime_norm: 1.5 } -in.ubiquitin: { folder: examples/amoeba, status: "completed, thermo checks passed", walltime: 13.0, walltime_norm: 2.1666666666666665 } -in.water_box.amoeba: { folder: examples/amoeba, status: "completed, thermo checks passed", walltime: 8.0, walltime_norm: 1.3333333333333333 } -in.water_box.hippo: { folder: examples/amoeba, status: "completed, thermo checks passed", walltime: 6.0, walltime_norm: 1.0 } -in.water_dimer.amoeba: { folder: examples/amoeba, status: "completed, 1 abs thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } -in.water_dimer.hippo: { folder: examples/amoeba, status: "completed, 1 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } -in.water_hexamer.amoeba: { folder: examples/amoeba, status: "completed, 1 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } -in.water_hexamer.hippo: { folder: examples/amoeba, status: "completed, 1 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } -in.nb3b: { folder: examples/nb3b, status: "completed, thermo checks passed", walltime: 5.0, walltime_norm: 0.8333333333333334 } -in.nb3b.screened: { folder: examples/nb3b, status: "completed, thermo checks passed", walltime: 30.0, walltime_norm: 5.0 } -in.min: { folder: examples/min, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.min.box: { folder: examples/min, status: "completed, thermo checks passed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.bcc.orthog: { folder: examples/triclinic, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.bcc.primitive: { folder: examples/triclinic, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.data.general: { folder: examples/triclinic, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.fcc.orthog: { folder: examples/triclinic, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.fcc.primitive: { folder: examples/triclinic, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.hex.orthog: { folder: examples/triclinic, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.hex.primitive: { folder: examples/triclinic, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.sq2.orthog: { folder: examples/triclinic, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.sq2.primitive: { folder: examples/triclinic, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.tri.srd: { folder: examples/ASPHERE/tri, status: "completed", failed_checks: { abs_diff_failed: 4, rel_diff_failed: 4 }, walltime: 143.0, walltime_norm: 23.833333333333332 } +in.star: { folder: examples/ASPHERE/star, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 12.0, walltime_norm: 2.0 } +in.star.mp: { folder: examples/ASPHERE/star, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 12.0, walltime_norm: 2.0 } +in.box: { folder: examples/ASPHERE/box, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 3 }, walltime: 18.0, walltime_norm: 3.0 } +in.box.mp: { folder: examples/ASPHERE/box, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 19.0, walltime_norm: 3.1666666666666665 } +in.dimer: { folder: examples/ASPHERE/dimer, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 6.0, walltime_norm: 1.0 } +in.dimer.mp: { folder: examples/ASPHERE/dimer, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 16.0, walltime_norm: 2.6666666666666665 } +in.vesicle: { folder: examples/ASPHERE/vesicle, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 9.0, walltime_norm: 1.5 } +in.line: { folder: examples/ASPHERE/line, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.line.srd: { folder: examples/ASPHERE/line, status: "failed, no Total wall time in the output, -------------------------------------------------------------------------- +MPI_ABORT was invoked on rank 0 in communicator MPI_COMM_WORLD +with errorcode 1. + +NOTE: invoking MPI_ABORT causes Open MPI to kill all MPI processes. +You may or may not see output from other processes, depending on +exactly when Open MPI kills them. +-------------------------------------------------------------------------- +", walltime: -1 } +in.poly: { folder: examples/ASPHERE/poly, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 8.0, walltime_norm: 1.3333333333333333 } +in.poly.mp: { folder: examples/ASPHERE/poly, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 8.0, walltime_norm: 1.3333333333333333 } +in.flat_membrane: { folder: examples/ASPHERE/flat_membrane, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 2 }, walltime: 7.0, walltime_norm: 1.1666666666666667 } +in.ellipsoid: { folder: examples/ASPHERE/ellipsoid, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 9.0, walltime_norm: 1.5 } +in.ellipsoid.mp: { folder: examples/ASPHERE/ellipsoid, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 9.0, walltime_norm: 1.5 } +in.ubiquitin: { folder: examples/amoeba, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 12.0, walltime_norm: 2.0 } +in.water_box.amoeba: { folder: examples/amoeba, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 8.0, walltime_norm: 1.3333333333333333 } +in.water_box.hippo: { folder: examples/amoeba, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 6.0, walltime_norm: 1.0 } +in.water_dimer.amoeba: { folder: examples/amoeba, status: "completed", failed_checks: { abs_diff_failed: 1, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.water_dimer.hippo: { folder: examples/amoeba, status: "completed", failed_checks: { abs_diff_failed: 1, rel_diff_failed: 1 }, walltime: 0.0, walltime_norm: 0.0 } +in.water_hexamer.amoeba: { folder: examples/amoeba, status: "completed", failed_checks: { abs_diff_failed: 1, rel_diff_failed: 1 }, walltime: 0.0, walltime_norm: 0.0 } +in.water_hexamer.hippo: { folder: examples/amoeba, status: "completed", failed_checks: { abs_diff_failed: 1, rel_diff_failed: 1 }, walltime: 0.0, walltime_norm: 0.0 } +in.nb3b: { folder: examples/nb3b, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 5.0, walltime_norm: 0.8333333333333334 } +in.nb3b.screened: { folder: examples/nb3b, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 29.0, walltime_norm: 4.833333333333333 } +in.min: { folder: examples/min, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.min.box: { folder: examples/min, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } in.balance: { folder: examples/balance, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.balance.bond.fast: { folder: examples/balance, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.balance.bond.slow: { folder: examples/balance, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } -in.balance.clock.dynamic: { folder: examples/balance, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 17.0, walltime_norm: 2.8333333333333335 } +in.balance.clock.dynamic: { folder: examples/balance, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 16.0, walltime_norm: 2.6666666666666665 } in.balance.clock.static: { folder: examples/balance, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 6.0, walltime_norm: 1.0 } in.balance.group.dynamic: { folder: examples/balance, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 2.0, walltime_norm: 0.3333333333333333 } in.balance.group.static: { folder: examples/balance, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 2.0, walltime_norm: 0.3333333333333333 } @@ -664,55 +779,55 @@ in.water.qmmm.plugin: { folder: examples/QUANTUM/NWChem, status: "failed, unreco in.zeolite.mm: { folder: examples/QUANTUM/NWChem, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.zeolite.qmmm: { folder: examples/QUANTUM/NWChem, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qmmm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } in.zeolite.qmmm.plugin: { folder: examples/QUANTUM/NWChem, status: "failed, unrecognized command, package not installed, ERROR: Unrecognized fix style 'mdi/qmmm' is part of the MDI package which is not enabled in this LAMMPS binary. (src/modify.cpp:924)", walltime: -1 } -in.wall.ccl: { folder: examples/wall, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.wall.diffusive: { folder: examples/wall, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.wall.flow: { folder: examples/wall, status: "completed, thermo checks passed", walltime: 6.0, walltime_norm: 1.0 } -in.wall.lepton: { folder: examples/wall, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.wall.maxwell: { folder: examples/wall, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.wall.specular: { folder: examples/wall, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.wall.table: { folder: examples/wall, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.meam: { folder: examples/meam, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.meam.shear: { folder: examples/meam, status: "completed, 3 rel thermo checks failed", walltime: 31.0, walltime_norm: 5.166666666666667 } -in.msmeam: { folder: examples/meam/msmeam, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.hugoniostat: { folder: examples/hugoniostat, status: "completed, thermo checks passed", walltime: 10.0, walltime_norm: 1.6666666666666667 } -in.comb.Cu: { folder: examples/comb, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.comb.Cu2O.elastic: { folder: examples/comb, status: "completed, thermo checks passed", walltime: 21.0, walltime_norm: 3.5 } -in.comb.HfO2: { folder: examples/comb, status: "completed, thermo checks passed", walltime: 4.0, walltime_norm: 0.6666666666666666 } -in.comb.Si: { folder: examples/comb, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.comb.Si.elastic: { folder: examples/comb, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.comb3: { folder: examples/comb, status: "completed, thermo checks passed", walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.wall.ccl: { folder: examples/wall, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.wall.diffusive: { folder: examples/wall, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.wall.flow: { folder: examples/wall, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 5.0, walltime_norm: 0.8333333333333334 } +in.wall.lepton: { folder: examples/wall, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.wall.maxwell: { folder: examples/wall, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.wall.specular: { folder: examples/wall, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.wall.table: { folder: examples/wall, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.meam: { folder: examples/meam, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.meam.shear: { folder: examples/meam, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 31.0, walltime_norm: 5.166666666666667 } +in.msmeam: { folder: examples/meam/msmeam, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.hugoniostat: { folder: examples/hugoniostat, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.comb.Cu: { folder: examples/comb, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.comb.Cu2O.elastic: { folder: examples/comb, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 21.0, walltime_norm: 3.5 } +in.comb.HfO2: { folder: examples/comb, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.comb.Si: { folder: examples/comb, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.comb.Si.elastic: { folder: examples/comb, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.comb3: { folder: examples/comb, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 11.0, walltime_norm: 1.8333333333333333 } in.tad: { folder: examples/tad, status: "failed, ERROR: Cannot use TAD with a single replica for NEB (src/REPLICA/tad.cpp:79).", walltime: -1 } -in.controller.temp: { folder: examples/controller, status: "completed, thermo checks passed", walltime: 6.0, walltime_norm: 1.0 } -in.controller.wall: { folder: examples/controller, status: "completed, thermo checks passed", walltime: 19.0, walltime_norm: 3.1666666666666665 } -in.reaxff.rdx: { folder: examples/reaxff, status: "completed, 3 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } -in.reaxff.rdx-shielded: { folder: examples/reaxff, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.reaxff.tatb: { folder: examples/reaxff, status: "completed, 2 rel thermo checks failed", walltime: 2.0, walltime_norm: 0.3333333333333333 } -in.reaxff.tatb-shielded: { folder: examples/reaxff, status: "completed, 2 rel thermo checks failed", walltime: 2.0, walltime_norm: 0.3333333333333333 } -in.ci-reax.CH: { folder: examples/reaxff/ci-reaxFF, status: "completed, thermo checks passed", walltime: 13.0, walltime_norm: 2.1666666666666665 } -in.reaxff.hns: { folder: examples/reaxff/HNS, status: "completed, thermo checks passed", walltime: 20.0, walltime_norm: 3.3333333333333335 } -in.VOH: { folder: examples/reaxff/VOH, status: "completed, 2 rel thermo checks failed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.controller.temp: { folder: examples/controller, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 6.0, walltime_norm: 1.0 } +in.controller.wall: { folder: examples/controller, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 19.0, walltime_norm: 3.1666666666666665 } +in.reaxff.rdx: { folder: examples/reaxff, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 0.0, walltime_norm: 0.0 } +in.reaxff.rdx-shielded: { folder: examples/reaxff, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.reaxff.tatb: { folder: examples/reaxff, status: "completed", failed_checks: { abs_diff_failed: 1, rel_diff_failed: 2 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.reaxff.tatb-shielded: { folder: examples/reaxff, status: "completed", failed_checks: { abs_diff_failed: 1, rel_diff_failed: 2 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.ci-reax.CH: { folder: examples/reaxff/ci-reaxFF, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 12.0, walltime_norm: 2.0 } +in.reaxff.hns: { folder: examples/reaxff/HNS, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 20.0, walltime_norm: 3.3333333333333335 } +in.VOH: { folder: examples/reaxff/VOH, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 2 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } in.water.acks2: { folder: examples/reaxff/water, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 13.0, walltime_norm: 2.1666666666666665 } in.water.acks2.field: { folder: examples/reaxff/water, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 17.0, walltime_norm: 2.8333333333333335 } in.water.qeq: { folder: examples/reaxff/water, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 6.0, walltime_norm: 1.0 } in.water.qeq.field: { folder: examples/reaxff/water, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 5.0, walltime_norm: 0.8333333333333334 } -in.ZnOH2: { folder: examples/reaxff/ZnOH2, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.FC: { folder: examples/reaxff/FC, status: "completed, thermo checks passed", walltime: 15.0, walltime_norm: 2.5 } -in.RDX: { folder: examples/reaxff/RDX, status: "completed, 3 rel thermo checks failed", walltime: 6.0, walltime_norm: 1.0 } -in.AuO: { folder: examples/reaxff/AuO, status: "completed, thermo checks passed", walltime: 6.0, walltime_norm: 1.0 } -in.CHO: { folder: examples/reaxff/CHO, status: "completed, 2 rel thermo checks failed", walltime: 4.0, walltime_norm: 0.6666666666666666 } -in.FeOH3: { folder: examples/reaxff/FeOH3, status: "completed, thermo checks passed", walltime: 2.0, walltime_norm: 0.3333333333333333 } -in.AB: { folder: examples/reaxff/AB, status: "completed, 3 rel thermo checks failed", walltime: 2.0, walltime_norm: 0.3333333333333333 } -in.grid.2d: { folder: examples/grid, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.grid.3d: { folder: examples/grid, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } -in.sph: { folder: examples/grid, status: "completed, thermo checks passed", walltime: 36.0, walltime_norm: 6.0 } +in.ZnOH2: { folder: examples/reaxff/ZnOH2, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.FC: { folder: examples/reaxff/FC, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 15.0, walltime_norm: 2.5 } +in.RDX: { folder: examples/reaxff/RDX, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 6.0, walltime_norm: 1.0 } +in.AuO: { folder: examples/reaxff/AuO, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 6.0, walltime_norm: 1.0 } +in.CHO: { folder: examples/reaxff/CHO, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 2 }, walltime: 3.0, walltime_norm: 0.5 } +in.FeOH3: { folder: examples/reaxff/FeOH3, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.AB: { folder: examples/reaxff/AB, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.grid.2d: { folder: examples/grid, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.grid.3d: { folder: examples/grid, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.sph: { folder: examples/grid, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 36.0, walltime_norm: 6.0 } in.yaml: { folder: examples/yaml, status: "completed, error parsing log.lammps into YAML", walltime: 3.0, walltime_norm: 0.5 } -in.hBN_shift: { folder: examples/tersoff, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.tersoff: { folder: examples/tersoff, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.friction: { folder: examples/friction, status: "completed, thermo checks passed", walltime: 2.0, walltime_norm: 0.3333333333333333 } -in.cmap: { folder: examples/cmap, status: "completed, 1 rel thermo checks failed", walltime: 1.0, walltime_norm: 0.16666666666666666 } -in.dipole: { folder: examples/dipole, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.colloid: { folder: examples/colloid, status: "completed, 3 rel thermo checks failed", walltime: 4.0, walltime_norm: 0.6666666666666666 } -in.streitz.ewald: { folder: examples/streitz, status: "completed, 2 rel thermo checks failed", walltime: 54.0, walltime_norm: 9.0 } +in.hBN_shift: { folder: examples/tersoff, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.tersoff: { folder: examples/tersoff, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.friction: { folder: examples/friction, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.cmap: { folder: examples/cmap, status: "completed", failed_checks: { abs_diff_failed: 1, rel_diff_failed: 1 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.dipole: { folder: examples/dipole, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.colloid: { folder: examples/colloid, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.streitz.ewald: { folder: examples/streitz, status: "completed", failed_checks: { abs_diff_failed: 1, rel_diff_failed: 2 }, walltime: 53.0, walltime_norm: 8.833333333333334 } in.streitz.wolf: { folder: examples/streitz, status: "failed, mismatched columns in the log files", walltime: 57.0, walltime_norm: 9.5 } in.neb.hop1: { folder: examples/neb, status: "failed, ERROR: Cannot use NEB with a single replica (src/REPLICA/neb.cpp:141).", walltime: -1 } in.neb.hop1.end: { folder: examples/neb, status: "failed, ERROR: Cannot use NEB with a single replica (src/REPLICA/neb.cpp:141).", walltime: -1 } @@ -722,17 +837,17 @@ in.fix_python_invoke: { folder: examples/python, status: "failed, ERROR: Could n in.fix_python_invoke_neighlist: { folder: examples/python, status: "failed, ERROR: Could not process Python string: .", walltime: -1 } in.fix_python_move_nve_melt: { folder: examples/python, status: "failed, ERROR: Loading python integrator module failure (src/PYTHON/fix_python_move.cpp:64).", walltime: -1 } in.fix_python_move_nve_melt_opt: { folder: examples/python, status: "failed, ERROR: Loading python integrator module failure (src/PYTHON/fix_python_move.cpp:64).", walltime: -1 } -in.pair_python_coulomb: { folder: examples/python, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } -in.pair_python_harmonic: { folder: examples/python, status: "completed, thermo checks passed", walltime: 45.0, walltime_norm: 7.5 } -in.pair_python_hybrid: { folder: examples/python, status: "completed, 3 rel thermo checks failed", walltime: 13.0, walltime_norm: 2.1666666666666665 } -in.pair_python_long: { folder: examples/python, status: "completed, thermo checks passed", walltime: 4.0, walltime_norm: 0.6666666666666666 } -in.pair_python_melt: { folder: examples/python, status: "completed, 3 rel thermo checks failed", walltime: 28.0, walltime_norm: 4.666666666666667 } -in.pair_python_spce: { folder: examples/python, status: "completed, thermo checks passed", walltime: 11.0, walltime_norm: 1.8333333333333333 } -in.pair_python_table: { folder: examples/python, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.pair_python_coulomb: { folder: examples/python, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 3.0, walltime_norm: 0.5 } +in.pair_python_harmonic: { folder: examples/python, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 45.0, walltime_norm: 7.5 } +in.pair_python_hybrid: { folder: examples/python, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 3 }, walltime: 13.0, walltime_norm: 2.1666666666666665 } +in.pair_python_long: { folder: examples/python, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 4.0, walltime_norm: 0.6666666666666666 } +in.pair_python_melt: { folder: examples/python, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 3 }, walltime: 28.0, walltime_norm: 4.666666666666667 } +in.pair_python_spce: { folder: examples/python, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.pair_python_table: { folder: examples/python, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } in.python: { folder: examples/python, status: "failed, ERROR on proc 0: Python evaluation of function loop failed (src/PYTHON/python_impl.cpp:384).", walltime: -1 } -in.bcc: { folder: examples/steinhardt, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.fcc: { folder: examples/steinhardt, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.icos: { folder: examples/steinhardt, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.bcc: { folder: examples/steinhardt, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.fcc: { folder: examples/steinhardt, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.icos: { folder: examples/steinhardt, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } in.kim-ex.melt: { folder: examples/kim, status: "failed, unknown command, package not installed, ERROR: Unknown command: kim init LennardJones_Ar real (src/input.cpp:314)", walltime: -1 } in.kim-pm-property: { folder: examples/kim, status: "failed, unknown command, package not installed, ERROR: Unknown command: kim init LJ_Shifted_Bernardes_1958MedCutoff_Ar__MO_126566794224_004 metal (src/input.cpp:314)", walltime: -1 } in.kim-pm-query.melt: { folder: examples/kim, status: "failed, unknown command, package not installed, ERROR: Unknown command: kim init SW_StillingerWeber_1985_Si__MO_405512056662_005 real (src/input.cpp:314)", walltime: -1 } @@ -740,70 +855,78 @@ in.kim-pm.melt: { folder: examples/kim, status: "failed, unknown command, packag in.kim-query: { folder: examples/kim, status: "failed, unknown command, package not installed, ERROR: Unknown command: kim init EAM_Dynamo_ErcolessiAdams_1994_Al__MO_123629422045_005 metal (src/input.cpp:314)", walltime: -1 } in.kim-sm.melt: { folder: examples/kim, status: "failed, unknown command, package not installed, ERROR: Unknown command: kim init Sim_LAMMPS_ReaxFF_StrachanVanDuinChakraborty_2003_CHNO__SM_107643900657_000 real (src/input.cpp:314)", walltime: -1 } in.lammps.melt: { folder: examples/kim, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 2.0, walltime_norm: 0.3333333333333333 } -in.ellipse.gayberne: { folder: examples/ellipse, status: "completed, 2 rel thermo checks failed", walltime: 1.0, walltime_norm: 0.16666666666666666 } -in.ellipse.resquared: { folder: examples/ellipse, status: "completed, 2 rel thermo checks failed", walltime: 2.0, walltime_norm: 0.3333333333333333 } -in.cos.1000SPCE: { folder: examples/VISCOSITY, status: "completed, 3 rel thermo checks failed", walltime: 36.0, walltime_norm: 6.0 } -in.einstein.2d: { folder: examples/VISCOSITY, status: "completed, thermo checks passed", walltime: 38.0, walltime_norm: 6.333333333333333 } -in.gk.2d: { folder: examples/VISCOSITY, status: "completed, thermo checks passed", walltime: 38.0, walltime_norm: 6.333333333333333 } -in.mp.2d: { folder: examples/VISCOSITY, status: "completed, thermo checks passed", walltime: 5.0, walltime_norm: 0.8333333333333334 } -in.nemd.2d: { folder: examples/VISCOSITY, status: "completed, 8 rel thermo checks failed", walltime: 17.0, walltime_norm: 2.8333333333333335 } -in.wall.2d: { folder: examples/VISCOSITY, status: "completed, thermo checks passed", walltime: 10.0, walltime_norm: 1.6666666666666667 } -in.gcmc.co2: { folder: examples/mc, status: "completed, thermo checks passed", walltime: 11.0, walltime_norm: 1.8333333333333333 } -in.gcmc.h2o: { folder: examples/mc, status: "failed, no Total wall time in the output.", walltime: -1 } -in.gcmc.lj: { folder: examples/mc, status: "completed, thermo checks passed", walltime: 18.0, walltime_norm: 3.0 } -in.mixed: { folder: examples/mc, status: "failed, no Total wall time in the output.", walltime: -1 } -in.pure: { folder: examples/mc, status: "failed, no Total wall time in the output.", walltime: -1 } -in.sgcmc.eam: { folder: examples/mc, status: "completed, thermo checks passed", walltime: 46.0, walltime_norm: 7.666666666666667 } -in.widom.lj: { folder: examples/mc, status: "completed, thermo checks passed", walltime: 8.0, walltime_norm: 1.3333333333333333 } -in.widom.spce: { folder: examples/mc, status: "completed, thermo checks passed", walltime: 45.0, walltime_norm: 7.5 } -in.mc: { folder: examples/MC-LOOP, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.flow.couette: { folder: examples/flow, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.flow.pois: { folder: examples/flow, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } +in.ellipse.gayberne: { folder: examples/ellipse, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 2 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.ellipse.resquared: { folder: examples/ellipse, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 2 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.cos.1000SPCE: { folder: examples/VISCOSITY, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 36.0, walltime_norm: 6.0 } +in.einstein.2d: { folder: examples/VISCOSITY, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 39.0, walltime_norm: 6.5 } +in.gk.2d: { folder: examples/VISCOSITY, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 39.0, walltime_norm: 6.5 } +in.mp.2d: { folder: examples/VISCOSITY, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 5.0, walltime_norm: 0.8333333333333334 } +in.nemd.2d: { folder: examples/VISCOSITY, status: "completed", failed_checks: { abs_diff_failed: 8, rel_diff_failed: 8 }, walltime: 17.0, walltime_norm: 2.8333333333333335 } +in.wall.2d: { folder: examples/VISCOSITY, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 10.0, walltime_norm: 1.6666666666666667 } +in.gcmc.co2: { folder: examples/mc, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.gcmc.h2o: { folder: examples/mc, status: "completed", failed_checks: { abs_diff_failed: 8, rel_diff_failed: 8 }, walltime: 138.0, walltime_norm: 23.0 } +in.gcmc.lj: { folder: examples/mc, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 11.0, walltime_norm: 1.8333333333333333 } +in.mixed: { folder: examples/mc, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 57.0, walltime_norm: 9.5 } +in.pure: { folder: examples/mc, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 57.0, walltime_norm: 9.5 } +in.sgcmc.eam: { folder: examples/mc, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 43.0, walltime_norm: 7.166666666666667 } +in.widom.lj: { folder: examples/mc, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 8.0, walltime_norm: 1.3333333333333333 } +in.widom.spce: { folder: examples/mc, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 44.0, walltime_norm: 7.333333333333333 } +in.mc: { folder: examples/MC-LOOP, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.flow.couette: { folder: examples/flow, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.flow.pois: { folder: examples/flow, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } in.prd: { folder: examples/prd, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 3.0, walltime_norm: 0.5 } -in.C_SNAP: { folder: examples/snap, status: "completed, thermo checks passed", walltime: 34.0, walltime_norm: 5.666666666666667 } -in.grid.snap: { folder: examples/snap, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.grid.tri: { folder: examples/snap, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.snap.InP.JCPA2020: { folder: examples/snap, status: "completed, thermo checks passed", walltime: 15.0, walltime_norm: 2.5 } -in.snap.Mo_Chen: { folder: examples/snap, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } -in.snap.Ta06A: { folder: examples/snap, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.snap.W.2940: { folder: examples/snap, status: "completed, thermo checks passed", walltime: 2.0, walltime_norm: 0.3333333333333333 } -in.snap.WBe.PRB2019: { folder: examples/snap, status: "completed, thermo checks passed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.C_SNAP: { folder: examples/snap, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 34.0, walltime_norm: 5.666666666666667 } +in.grid.snap: { folder: examples/snap, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.grid.tri: { folder: examples/snap, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.snap.InP.JCPA2020: { folder: examples/snap, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 15.0, walltime_norm: 2.5 } +in.snap.Mo_Chen: { folder: examples/snap, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.snap.Ta06A: { folder: examples/snap, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.snap.W.2940: { folder: examples/snap, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.snap.WBe.PRB2019: { folder: examples/snap, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } in.snap.compute: { folder: examples/snap, status: "completed, but no Step nor Loop in the output.", walltime: 0.0, walltime_norm: 0.0 } in.snap.compute.quadratic: { folder: examples/snap, status: "completed, but no Step nor Loop in the output.", walltime: 0.0, walltime_norm: 0.0 } -in.snap.hybrid.WSNAP.HePair: { folder: examples/snap, status: "completed, thermo checks passed", walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.snap.hybrid.WSNAP.HePair: { folder: examples/snap, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } in.snap.scale.Ni_Zuo_JCPA2020: { folder: examples/snap, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 3.0, walltime_norm: 0.5 } in.lammps: { folder: examples/COUPLE/lammps_spparks, status: "failed, ERROR: Cannot open file data.lammps: No such file or directory (src/read_data.cpp:367).", walltime: -1 } in.spparks: { folder: examples/COUPLE/lammps_spparks, status: "failed, unknown command, package not installed, ERROR: Unknown command: seed 56789 (src/input.cpp:314)", walltime: -1 } in.lj: { folder: examples/COUPLE/plugin, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.fix_external: { folder: examples/COUPLE/python, status: "completed, but no Step nor Loop in the output.", walltime: 0.0, walltime_norm: 0.0 } -in.chain: { folder: examples/COUPLE/multiple, status: "failed, no Total wall time in the output.", walltime: -1 } +in.chain: { folder: examples/COUPLE/multiple, status: "failed, no Total wall time in the output, -------------------------------------------------------------------------- +MPI_ABORT was invoked on rank 0 in communicator MPI_COMM_WORLD +with errorcode 1. + +NOTE: invoking MPI_ABORT causes Open MPI to kill all MPI processes. +You may or may not see output from other processes, depending on +exactly when Open MPI kills them. +-------------------------------------------------------------------------- +", walltime: -1 } in.lj: { folder: examples/COUPLE/simple, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } -in.body: { folder: examples/body, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.cubes: { folder: examples/body, status: "completed, 2 rel thermo checks failed", walltime: 36.0, walltime_norm: 6.0 } -in.pour3d: { folder: examples/body, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.squares: { folder: examples/body, status: "completed, 3 rel thermo checks failed", walltime: 3.0, walltime_norm: 0.5 } -in.wall2d: { folder: examples/body, status: "completed, 3 rel thermo checks failed", walltime: 2.0, walltime_norm: 0.3333333333333333 } -in.atm: { folder: examples/atm, status: "completed, thermo checks passed", walltime: 13.0, walltime_norm: 2.1666666666666665 } +in.body: { folder: examples/body, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.cubes: { folder: examples/body, status: "completed", failed_checks: { abs_diff_failed: 2, rel_diff_failed: 2 }, walltime: 36.0, walltime_norm: 6.0 } +in.pour3d: { folder: examples/body, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.squares: { folder: examples/body, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 3.0, walltime_norm: 0.5 } +in.wall2d: { folder: examples/body, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 2.0, walltime_norm: 0.3333333333333333 } +in.atm: { folder: examples/atm, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 13.0, walltime_norm: 2.1666666666666665 } in.ar.lj: { folder: examples/UNITS, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.ar.metal: { folder: examples/UNITS, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.ar.real: { folder: examples/UNITS, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } -in.mos2-bulk: { folder: examples/threebody, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.mos2.rebomos: { folder: examples/threebody, status: "completed, thermo checks passed", walltime: 21.0, walltime_norm: 3.5 } +in.mos2-bulk: { folder: examples/threebody, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.mos2.rebomos: { folder: examples/threebody, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 21.0, walltime_norm: 3.5 } in.mos2.sw.mod: { folder: examples/threebody, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 1.0, walltime_norm: 0.16666666666666666 } -in.threebody: { folder: examples/threebody, status: "completed, thermo checks passed", walltime: 1.0, walltime_norm: 0.16666666666666666 } -in.nemd: { folder: examples/nemd, status: "completed, 3 rel thermo checks failed", walltime: 0.0, walltime_norm: 0.0 } -in.obstacle: { folder: examples/obstacle, status: "completed, thermo checks passed", walltime: 0.0, walltime_norm: 0.0 } -in.crack: { folder: examples/crack, status: "completed, thermo checks passed", walltime: 3.0, walltime_norm: 0.5 } +in.threebody: { folder: examples/threebody, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 1.0, walltime_norm: 0.16666666666666666 } +in.nemd: { folder: examples/nemd, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 0.0, walltime_norm: 0.0 } +in.obstacle: { folder: examples/obstacle, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 0.0, walltime_norm: 0.0 } +in.crack: { folder: examples/crack, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 3.0, walltime_norm: 0.5 } in.elastic: { folder: examples/ELASTIC, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 0.0, walltime_norm: 0.0 } in.peri-pmb: { folder: examples/peri, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 9.0, walltime_norm: 1.5 } -in.peri.eps: { folder: examples/peri, status: "completed, 3 rel thermo checks failed", walltime: 21.0, walltime_norm: 3.5 } -in.peri.lps: { folder: examples/peri, status: "completed, 3 rel thermo checks failed", walltime: 17.0, walltime_norm: 2.8333333333333335 } -in.peri.pmb: { folder: examples/peri, status: "completed, thermo checks passed", walltime: 9.0, walltime_norm: 1.5 } -in.peri.ves: { folder: examples/peri, status: "completed, 3 rel thermo checks failed", walltime: 21.0, walltime_norm: 3.5 } +in.peri.eps: { folder: examples/peri, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 21.0, walltime_norm: 3.5 } +in.peri.lps: { folder: examples/peri, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 17.0, walltime_norm: 2.8333333333333335 } +in.peri.pmb: { folder: examples/peri, status: "completed", failed_checks: { abs_diff_failed: 0, rel_diff_failed: 0 }, walltime: 9.0, walltime_norm: 1.5 } +in.peri.ves: { folder: examples/peri, status: "completed", failed_checks: { abs_diff_failed: 3, rel_diff_failed: 3 }, walltime: 21.0, walltime_norm: 3.5 } in.hyper.global: { folder: examples/hyper, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 53.0, walltime_norm: 8.833333333333334 } -in.hyper.local: { folder: examples/hyper, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 14.0, walltime_norm: 2.3333333333333335 } -in.spce: { folder: examples/rdf-adf, status: "failed, no Total wall time in the output.", walltime: -1 } -in.elastic: { folder: examples/ELASTIC_T/BORN_MATRIX/Silicon, status: "failed, no Total wall time in the output.", walltime: -1 } -in.ljcov: { folder: examples/ELASTIC_T/BORN_MATRIX/Argon/Analytical, status: "failed, no Total wall time in the output.", walltime: -1 } -in.ljcov: { folder: examples/ELASTIC_T/BORN_MATRIX/Argon/Numdiff, status: "failed, no Total wall time in the output.", walltime: -1 } -in.elastic: { folder: examples/ELASTIC_T/DEFORMATION/Silicon, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 9.0, walltime_norm: 1.5 } +in.hyper.local: { folder: examples/hyper, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 13.0, walltime_norm: 2.1666666666666665 } +in.spce: { folder: examples/rdf-adf, status: "completed", failed_checks: { abs_diff_failed: 6, rel_diff_failed: 6 }, walltime: 88.0, walltime_norm: 14.666666666666666 } +in.elastic: { folder: examples/ELASTIC_T/BORN_MATRIX/Silicon, status: "completed, error parsing log.lammps into YAML", walltime: 62.0, walltime_norm: 10.333333333333334 } +in.ljcov: { folder: examples/ELASTIC_T/BORN_MATRIX/Argon/Analytical, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.ljcov: { folder: examples/ELASTIC_T/BORN_MATRIX/Argon/Numdiff, status: "failed, no Total wall time in the output, timeout (180s expired)", walltime: -1 } +in.elastic: { folder: examples/ELASTIC_T/DEFORMATION/Silicon, status: "completed, numerical checks skipped due to missing the reference log file", walltime: 6.0, walltime_norm: 1.0 } From 23bdc5ddc2b8d7116b3822a744d797900ebd43f7 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Tue, 10 Sep 2024 00:18:50 -0500 Subject: [PATCH 260/355] Allowed number of blocks greater than 65,535 for k_transpose --- lib/gpu/lal_neighbor.cpp | 17 +++++++++++++++-- lib/gpu/lal_neighbor_gpu.cu | 10 +++++----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/lib/gpu/lal_neighbor.cpp b/lib/gpu/lal_neighbor.cpp index 10816e2fa6..62ab2b31d0 100644 --- a/lib/gpu/lal_neighbor.cpp +++ b/lib/gpu/lal_neighbor.cpp @@ -586,8 +586,21 @@ void Neighbor::build_nbor_list(double **x, const int inum, const int host_inum, const int b2y=_block_cell_2d; const int g2x=static_cast(ceil(static_cast(_maxspecial)/b2x)); const int g2y=static_cast(ceil(static_cast(nt)/b2y)); - _shared->k_transpose.set_size(g2x,g2y,b2x,b2y); - _shared->k_transpose.run(&dev_special,&dev_special_t,&_maxspecial,&nt); + const int max_num_blocks = 65535; + int shift = 0; + if (g2y < max_num_blocks) { + _shared->k_transpose.set_size(g2x,g2y,b2x,b2y); + _shared->k_transpose.run(&dev_special,&dev_special_t,&_maxspecial,&nt,&shift); + } else { + const int num_rounds = ceil(static_cast(g2y) / max_num_blocks); + int g2y_m = 65534; + for (int i = 0; i < num_rounds; i++) { + _shared->k_transpose.set_size(g2x,g2y_m,b2x,b2y); + _shared->k_transpose.run(&dev_special,&dev_special_t,&_maxspecial,&nt,&shift); + shift += g2y_m; + } + } + time_transpose.stop(); } diff --git a/lib/gpu/lal_neighbor_gpu.cu b/lib/gpu/lal_neighbor_gpu.cu index a7506fc5c3..7d0941ccd5 100644 --- a/lib/gpu/lal_neighbor_gpu.cu +++ b/lib/gpu/lal_neighbor_gpu.cu @@ -147,7 +147,7 @@ __kernel void kernel_calc_cell_counts(const unsigned *restrict cell_id, __kernel void transpose(__global tagint *restrict out, const __global tagint *restrict in, - int columns_in, int rows_in) + int columns_in, int rows_in, int shift) { __local tagint block[BLOCK_CELL_2D][BLOCK_CELL_2D+1]; @@ -158,15 +158,15 @@ __kernel void transpose(__global tagint *restrict out, unsigned i=bi*BLOCK_CELL_2D+ti; unsigned j=bj*BLOCK_CELL_2D+tj; - if ((i Date: Tue, 10 Sep 2024 00:36:13 -0500 Subject: [PATCH 261/355] fixed the value of shift being the number of rows processed in each chunk (g2y_m * b2y) --- lib/gpu/lal_neighbor.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/gpu/lal_neighbor.cpp b/lib/gpu/lal_neighbor.cpp index 62ab2b31d0..ba2a328130 100644 --- a/lib/gpu/lal_neighbor.cpp +++ b/lib/gpu/lal_neighbor.cpp @@ -586,21 +586,24 @@ void Neighbor::build_nbor_list(double **x, const int inum, const int host_inum, const int b2y=_block_cell_2d; const int g2x=static_cast(ceil(static_cast(_maxspecial)/b2x)); const int g2y=static_cast(ceil(static_cast(nt)/b2y)); + // maximum number of blocks on the device const int max_num_blocks = 65535; int shift = 0; if (g2y < max_num_blocks) { _shared->k_transpose.set_size(g2x,g2y,b2x,b2y); _shared->k_transpose.run(&dev_special,&dev_special_t,&_maxspecial,&nt,&shift); } else { - const int num_rounds = ceil(static_cast(g2y) / max_num_blocks); + // using a fixed number of blocks int g2y_m = 65534; - for (int i = 0; i < num_rounds; i++) { + // number of chunks needed for the whole transpose + const int num_chunks = ceil(static_cast(g2y) / g2y_m); + for (int i = 0; i < num_chunks; i++) { _shared->k_transpose.set_size(g2x,g2y_m,b2x,b2y); _shared->k_transpose.run(&dev_special,&dev_special_t,&_maxspecial,&nt,&shift); - shift += g2y_m; + shift += g2y_m*b2y; } } - + time_transpose.stop(); } From c63c88f8b6804f58399429dd3fd52bcf634109ee Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Tue, 10 Sep 2024 08:58:42 -0500 Subject: [PATCH 262/355] reduced the max number of blocks for each transpose --- lib/gpu/lal_neighbor.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/gpu/lal_neighbor.cpp b/lib/gpu/lal_neighbor.cpp index ba2a328130..101e92953c 100644 --- a/lib/gpu/lal_neighbor.cpp +++ b/lib/gpu/lal_neighbor.cpp @@ -586,19 +586,20 @@ void Neighbor::build_nbor_list(double **x, const int inum, const int host_inum, const int b2y=_block_cell_2d; const int g2x=static_cast(ceil(static_cast(_maxspecial)/b2x)); const int g2y=static_cast(ceil(static_cast(nt)/b2y)); - // maximum number of blocks on the device - const int max_num_blocks = 65535; + // the maximum number of blocks on the device is typically 65535 + // we can use a lower number to have more resource per block + const int max_num_blocks = 32768; int shift = 0; if (g2y < max_num_blocks) { _shared->k_transpose.set_size(g2x,g2y,b2x,b2y); _shared->k_transpose.run(&dev_special,&dev_special_t,&_maxspecial,&nt,&shift); } else { // using a fixed number of blocks - int g2y_m = 65534; + int g2y_m = max_num_blocks; + _shared->k_transpose.set_size(g2x,g2y_m,b2x,b2y); // number of chunks needed for the whole transpose const int num_chunks = ceil(static_cast(g2y) / g2y_m); - for (int i = 0; i < num_chunks; i++) { - _shared->k_transpose.set_size(g2x,g2y_m,b2x,b2y); + for (int i = 0; i < num_chunks; i++) { _shared->k_transpose.run(&dev_special,&dev_special_t,&_maxspecial,&nt,&shift); shift += g2y_m*b2y; } From b16bb27184cb873ff9da8136d99f9043b6c19e91 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Tue, 10 Sep 2024 09:47:07 -0500 Subject: [PATCH 263/355] revert to using the max number of blocks on device for each pass, as the number of blocks (32767 or 65535) already saturates the number of SMs anyway --- lib/gpu/lal_neighbor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gpu/lal_neighbor.cpp b/lib/gpu/lal_neighbor.cpp index 101e92953c..051f55f0a3 100644 --- a/lib/gpu/lal_neighbor.cpp +++ b/lib/gpu/lal_neighbor.cpp @@ -587,8 +587,8 @@ void Neighbor::build_nbor_list(double **x, const int inum, const int host_inum, const int g2x=static_cast(ceil(static_cast(_maxspecial)/b2x)); const int g2y=static_cast(ceil(static_cast(nt)/b2y)); // the maximum number of blocks on the device is typically 65535 - // we can use a lower number to have more resource per block - const int max_num_blocks = 32768; + // in principle we can use a lower number to have more resource per block 32768 + const int max_num_blocks = 65535; int shift = 0; if (g2y < max_num_blocks) { _shared->k_transpose.set_size(g2x,g2y,b2x,b2y); From 847ce1e3632e1421d17042b1bbfc57ba89290f2a Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Tue, 10 Sep 2024 15:39:52 -0500 Subject: [PATCH 264/355] enable generating new reference log files if desirable --- tools/regression-tests/run_tests.py | 49 +++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index 5f88f03f64..efbfb2ac25 100755 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -157,6 +157,8 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file using_markers = False EPSILON = np.float64(config['epsilon']) nugget = float(config['nugget']) + genref = config['genref'] + compiler = config['compiler'] use_valgrind = False if 'valgrind' in config['mpiexec']: use_valgrind = True @@ -325,7 +327,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file config['nprocs'] = saved_nprocs # check if the output contains ERROR - # there might not be a log.lammps generated at this point, or only log.lammps contains only the date line + # there might not be a log file generated at this point, or only the log file contains only the date line if "ERROR" in output: error_line = "" for line in output.split('\n'): @@ -357,16 +359,18 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file test_id = test_id + 1 continue - # check if a log.lammps file exists in the current folder - if os.path.isfile("log.lammps") == False: - msg = f" failed, no log.lammps generated with {input_test} with return code {returncode}.\n" + # check if a log file log.{basename}.{nprocs} exists in the current folder + logfilename = f"log.{basename}.{nprocs}" + + if os.path.isfile(logfilename) == False: + msg = f" failed, no log.{basename}.{nprocs} generated with {input_test} with return code {returncode}.\n" print(msg) logger.info(msg) logger.info(f" Output:") logger.info(f" {output}") logger.info(f" Error:\n{error}") - msg = f"{input}: {{ folder: {input_folder}, status: \"failed, no log.lammps\", walltime: {walltime} }}\n" + msg = f"{input}: {{ folder: {input_folder}, status: \"failed, no log file generated\", walltime: {walltime} }}\n" progress.write(msg) progress.close() failure.write(msg) @@ -375,9 +379,14 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file test_id = test_id + 1 continue else: - # save a copy of the log file for further inspection - cmd_str = f"cp log.lammps log.{basename}.{nprocs}" - p = subprocess.run(cmd_str, shell=True, text=True, capture_output=True) + # generate a new log file whose name has the format of log.{date}.{basename}.{compiler}.{nprocs} + if genref == True: + dmy = datetime.datetime.now() + date = dmy.strftime("%d%b%y") + # assume g++ for now, but is be available from running "lmp_binary -h" + compiler = "g++" + cmd_str = f"cp log.{basename}.{nprocs} log.{date}.{basename}.{compiler}.{nprocs}" + p = subprocess.run(cmd_str, shell=True, text=True, capture_output=True) # if skip numerical checks, then skip the rest if skip_numerical_check == True: @@ -795,6 +804,7 @@ def get_lammps_build_configuration(lmp_binary): reading = False operating_system = "" GitInfo = "" + compiler = "g++" row = 0 for line in output: if line != "": @@ -810,7 +820,11 @@ def get_lammps_build_configuration(lmp_binary): operating_system = line if "Git info" in line: GitInfo = line - + if "Compiler" in line: + if "GNU" in line: + compiler = "g++" + if "Intel" in line: + compiler = "icc" row += 1 packages = packages.strip() @@ -824,20 +838,24 @@ def get_lammps_build_configuration(lmp_binary): row += 1 - return packages.split(" "), operating_system, GitInfo, compile_flags + return packages.split(" "), operating_system, GitInfo, compile_flags, compiler ''' launch LAMMPS using the configuration defined in the dictionary config with an input file TODO: - generate new reference values if needed ''' -def execute(lmp_binary, config, input_file_name, generate_ref_yaml=False): +def execute(lmp_binary, config, input_file_name, generate_ref=False): cmd_str = "" # check if mpiexec/mpirun is used if config['mpiexec']: cmd_str += config['mpiexec'] + " " + config['mpiexec_numproc_flag'] + " " + config['nprocs'] + " " - cmd_str += lmp_binary + " -in " + input_file_name + " " + config['args'] + # write to a log file with format log.{basename}.{nprocs} + basename = input_file_name[3:] + logfilename = f"log.{basename}.{config['nprocs']}" + + cmd_str += lmp_binary + " -in " + input_file_name + " " + config['args'] + " -log " + logfilename logger.info(f" Executing: {cmd_str}") # set a timeout (in seconds) for each run @@ -1325,10 +1343,11 @@ if __name__ == "__main__": lmp_binary = os.path.abspath(config['lmp_binary']) # print out the binary info - packages, operating_system, GitInfo, compile_flags = get_lammps_build_configuration(lmp_binary) + packages, operating_system, GitInfo, compile_flags, compiler = get_lammps_build_configuration(lmp_binary) print("\nLAMMPS build info:") print(f" - {operating_system}") print(f" - {GitInfo}") + print(f" - Compiler: {compiler}") print(f" - Active compile flags: {compile_flags}") print(f" - List of {len(packages)} installed packages:") all_pkgs = "" @@ -1336,6 +1355,10 @@ if __name__ == "__main__": all_pkgs += p + " " print(all_pkgs) + # augment config with additional keys + config['compiler'] = compiler + config['genref'] = genref + all_results = [] # save current working dir From 8e2dacd0e3efb5805e07d6d610a00fa1a1e2afcb Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Tue, 10 Sep 2024 15:51:04 -0500 Subject: [PATCH 265/355] put some cosmetic stuffs --- tools/regression-tests/run_tests.py | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index efbfb2ac25..af9d8dd085 100755 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -23,6 +23,7 @@ Some benefits include: + keeping track of the testing progress to resume the testing from the last checkpoint (skipping completed runs) + distributing the input list across multiple processes by splitting the list of input scripts into separate runs (there are ~800 input scripts under the top-level examples) + + generating new reference log files if desirable Input arguments: + the path to a LAMMPS binary (can be relative to the working directory) @@ -794,7 +795,7 @@ def extract_data_to_yaml(inputFileName): return thermo ''' - return a tuple of the list of installed packages, OS, GitInfo and compile_flags + return a dictionary of the list of installed packages, OS, GitInfo, compiler and compile_flags ''' def get_lammps_build_configuration(lmp_binary): cmd_str = lmp_binary + " -h" @@ -805,6 +806,7 @@ def get_lammps_build_configuration(lmp_binary): operating_system = "" GitInfo = "" compiler = "g++" + compiler_full = "" row = 0 for line in output: if line != "": @@ -821,6 +823,7 @@ def get_lammps_build_configuration(lmp_binary): if "Git info" in line: GitInfo = line if "Compiler" in line: + compiler_full = line if "GNU" in line: compiler = "g++" if "Intel" in line: @@ -838,7 +841,17 @@ def get_lammps_build_configuration(lmp_binary): row += 1 - return packages.split(" "), operating_system, GitInfo, compile_flags, compiler + installed_packages = packages.split(" ") + build_config = { + 'installed_packages': installed_packages, + 'operating_system': operating_system, + 'git_info': GitInfo, + 'compiler': compiler, + 'compiler_full': compiler_full, + 'compile_flags': compile_flags, + } + + return build_config ''' launch LAMMPS using the configuration defined in the dictionary config with an input file @@ -1343,11 +1356,18 @@ if __name__ == "__main__": lmp_binary = os.path.abspath(config['lmp_binary']) # print out the binary info - packages, operating_system, GitInfo, compile_flags, compiler = get_lammps_build_configuration(lmp_binary) + build_config = get_lammps_build_configuration(lmp_binary) + packages = build_config['installed_packages'] + operating_system = build_config['installed_packages'] + GitInfo = build_config['git_info'] + compiler = build_config['compiler'] + compiler_full = build_config['compiler_full'] + compile_flags = build_config['compile_flags'] + print("\nLAMMPS build info:") print(f" - {operating_system}") print(f" - {GitInfo}") - print(f" - Compiler: {compiler}") + print(f" - Compiler: {compiler_full}") print(f" - Active compile flags: {compile_flags}") print(f" - List of {len(packages)} installed packages:") all_pkgs = "" From cbbea4771893d21c8a3386b164eb1ade3b68a4ce Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Tue, 10 Sep 2024 16:22:35 -0500 Subject: [PATCH 266/355] fix the incorrect keys --- tools/regression-tests/run_tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index af9d8dd085..37c1cd78a1 100755 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -1358,7 +1358,7 @@ if __name__ == "__main__": # print out the binary info build_config = get_lammps_build_configuration(lmp_binary) packages = build_config['installed_packages'] - operating_system = build_config['installed_packages'] + operating_system = build_config['operating_system'] GitInfo = build_config['git_info'] compiler = build_config['compiler'] compiler_full = build_config['compiler_full'] @@ -1367,7 +1367,7 @@ if __name__ == "__main__": print("\nLAMMPS build info:") print(f" - {operating_system}") print(f" - {GitInfo}") - print(f" - Compiler: {compiler_full}") + print(f" - {compiler_full}") print(f" - Active compile flags: {compile_flags}") print(f" - List of {len(packages)} installed packages:") all_pkgs = "" From b39386afe517737daa1f7cfc96dc6f8f80930f7f Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Tue, 10 Sep 2024 17:00:57 -0500 Subject: [PATCH 267/355] count all the runs with error and failed num checks as failed --- tools/regression-tests/run_tests.py | 37 +++++++++++++++++------------ 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index 37c1cd78a1..d086f28b2c 100755 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -322,7 +322,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file result = TestResult(name=input, output="", time="", status="passed") # run the LAMMPS binary with the input script - cmd_str, output, error, returncode = execute(lmp_binary, config, input_test) + cmd_str, output, error, returncode, logfilename = execute(lmp_binary, config, input_test) # restore the nprocs value in the configuration config['nprocs'] = saved_nprocs @@ -361,8 +361,6 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file continue # check if a log file log.{basename}.{nprocs} exists in the current folder - logfilename = f"log.{basename}.{nprocs}" - if os.path.isfile(logfilename) == False: msg = f" failed, no log.{basename}.{nprocs} generated with {input_test} with return code {returncode}.\n" print(msg) @@ -460,11 +458,11 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file test_id = test_id + 1 continue - # parse thermo output in log.lammps from the run - thermo = extract_data_to_yaml("log.lammps") + # parse thermo output in the log file from the run + thermo = extract_data_to_yaml(logfilename) num_runs = len(thermo) - # the run completed normally but log.lammps may not be friendly for parsing into YAML format + # the run completed normally but the log file may not be friendly for parsing into YAML format if num_runs == 0: logger.info(f" The run terminated with {input_test} gives the following output:") logger.info(f" {output}") @@ -477,7 +475,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file msg += ", memory leaks detected" num_memleak = num_memleak + 1 - result.status = msg + ", error parsing log.lammps into YAML" + result.status = msg + f", error parsing {logfilename} into YAML" results.append(result) progress.write(f"{input}: {{ folder: {input_folder}, status: \"{result.status}\", walltime: {walltime}, walltime_norm: {walltime_norm} }}\n") progress.close() @@ -503,6 +501,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file results.append(result) progress.write(f"{input}: {{ folder: {input_folder}, status: \"completed, numerical checks skipped, unsupported log file format\", walltime: {walltime}, walltime_norm: {walltime_norm} }}\n") progress.close() + num_completed = num_completed + 1 num_error = num_error + 1 test_id = test_id + 1 continue @@ -526,16 +525,17 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file progress.write(msg) progress.close() failure.write(msg) + num_completed = num_completed + 1 num_error = num_error + 1 test_id = test_id + 1 continue - logger.info(f" Comparing thermo output from log.lammps against the reference log file {thermo_ref_file}") + logger.info(f" Comparing thermo output from {logfilename} against the reference log file {thermo_ref_file}") # check if the number of runs matches with that in the reference log file # maybe due to some changes to the input where the ref log file is not updated yet if num_runs != num_runs_ref: - logger.info(f" ERROR: Number of runs in log.lammps ({num_runs}) is different from that in the reference log ({num_runs_ref})." + logger.info(f" ERROR: Number of runs in {logfilename} ({num_runs}) is different from that in the reference log ({num_runs_ref})." " Check README in the folder, possibly due to using mpirun with partitions or parsing the wrong reference log file.") result.status = "failed, incomplete runs" results.append(result) @@ -551,7 +551,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file num_fields = len(thermo[0]['keywords']) num_fields_ref = len(thermo_ref[0]['keywords']) if num_fields != num_fields_ref: - logger.info(f" failed, number of thermo colums in log.lammps ({num_fields}) is different from that in the reference log ({num_fields_ref}) in the first run.") + logger.info(f" failed, number of thermo colums in {logfilename} ({num_fields}) is different from that in the reference log ({num_fields_ref}) in the first run.") logger.info(f" Check both log files for more details.") result.status = "failed, mismatched columns in the log files" results.append(result) @@ -586,7 +586,7 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file num_fields = len(thermo[irun]['keywords']) num_fields_ref = len(thermo_ref[irun]['keywords']) if num_fields != num_fields_ref: - logger.info(f" failed: Number of thermo columns in log.lammps ({num_fields})") + logger.info(f" failed: Number of thermo columns in {logfilename} ({num_fields})") logger.info(f" is different from that in the reference log ({num_fields_ref}) in run {irun}.") mismatched_columns = True continue @@ -676,6 +676,8 @@ def iterate(lmp_binary, input_folder, input_list, config, results, progress_file logger.info(msg) #result.status = f"all {num_checks} checks passed." num_passed = num_passed + 1 + else: + num_error = num_error + 1 result.status = f"abs_diff_failed: {num_abs_failed}, rel_diff_failed: {num_rel_failed}" results.append(result) @@ -855,8 +857,13 @@ def get_lammps_build_configuration(lmp_binary): ''' launch LAMMPS using the configuration defined in the dictionary config with an input file - TODO: - - generate new reference values if needed + return + - cmd_str: the complete command used to launch LAMMPS with the input + - stdout: stdout of the process + - stderr: stderr of the process + - errorcode: error code returned by the process + - logfilename: the log file name for the given input + to avoid duplicate writes to log.lammps if multiple workers execute in the same folder ''' def execute(lmp_binary, config, input_file_name, generate_ref=False): cmd_str = "" @@ -879,7 +886,7 @@ def execute(lmp_binary, config, input_file_name, generate_ref=False): try: p = subprocess.run(cmd_str, shell=True, text=True, capture_output=True, timeout=timeout) - return cmd_str, p.stdout, p.stderr, p.returncode + return cmd_str, p.stdout, p.stderr, p.returncode, logfilename except subprocess.TimeoutExpired: msg = f" Timeout for: {cmd_str} ({timeout}s expired)" @@ -887,7 +894,7 @@ def execute(lmp_binary, config, input_file_name, generate_ref=False): print(msg) error_str = f"timeout ({timeout}s expired)" - return cmd_str, "", error_str, -1 + return cmd_str, "", error_str, -1, logfilename ''' get the reference walltime by running the lmp_binary with config with an input script in the bench/ folder From 12abaf83cca8f6e57144b2624d25339865179282 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Tue, 10 Sep 2024 16:25:06 -0600 Subject: [PATCH 268/355] Changes from @sjplimp --- src/KSPACE/pppm.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/KSPACE/pppm.cpp b/src/KSPACE/pppm.cpp index 06cbf119e3..9665d2392d 100644 --- a/src/KSPACE/pppm.cpp +++ b/src/KSPACE/pppm.cpp @@ -1384,11 +1384,19 @@ void PPPM::set_grid_local() // npey_fft,npez_fft = # of procs in y,z dims // if nprocs is small enough, proc can own 1 or more entire xy planes, // else proc owns 2d sub-blocks of yz plane + // NOTE: commented out lines support this + // need to ensure fft3d.cpp and remap.cpp support 2D planes // me_y,me_z = which proc (0-npe_fft-1) I am in y,z dimensions // nlo_fft,nhi_fft = lower/upper limit of the section // of the global FFT mesh that I own in x-pencil decomposition int npey_fft,npez_fft; + + //if (nz_pppm >= nprocs) { + // npey_fft = 1; + // npez_fft = nprocs; + //} else procs2grid2d(nprocs,ny_pppm,nz_pppm,&npey_fft,&npez_fft); + procs2grid2d(nprocs,ny_pppm,nz_pppm,&npey_fft,&npez_fft); int me_y = me % npey_fft; From 4a11b966352f35f151fc6e376ac991a64c61cf54 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 10 Sep 2024 23:31:37 -0400 Subject: [PATCH 269/355] update with upstream --- lib/gpu/lal_neighbor.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/lib/gpu/lal_neighbor.cpp b/lib/gpu/lal_neighbor.cpp index 10816e2fa6..288415e0e7 100644 --- a/lib/gpu/lal_neighbor.cpp +++ b/lib/gpu/lal_neighbor.cpp @@ -586,8 +586,25 @@ void Neighbor::build_nbor_list(double **x, const int inum, const int host_inum, const int b2y=_block_cell_2d; const int g2x=static_cast(ceil(static_cast(_maxspecial)/b2x)); const int g2y=static_cast(ceil(static_cast(nt)/b2y)); - _shared->k_transpose.set_size(g2x,g2y,b2x,b2y); - _shared->k_transpose.run(&dev_special,&dev_special_t,&_maxspecial,&nt); + // the maximum number of blocks on the device is typically 65535 + // in principle we can use a lower number to have more resource per block 32768 + const int max_num_blocks = 65535; + int shift = 0; + if (g2y < max_num_blocks) { + _shared->k_transpose.set_size(g2x,g2y,b2x,b2y); + _shared->k_transpose.run(&dev_special,&dev_special_t,&_maxspecial,&nt,&shift); + } else { + // using a fixed number of blocks + int g2y_m = max_num_blocks; + _shared->k_transpose.set_size(g2x,g2y_m,b2x,b2y); + // number of chunks needed for the whole transpose + const int num_chunks = ceil(static_cast(g2y) / g2y_m); + for (int i = 0; i < num_chunks; i++) { + _shared->k_transpose.run(&dev_special,&dev_special_t,&_maxspecial,&nt,&shift); + shift += g2y_m*b2y; + } + } + time_transpose.stop(); } From 6b2a47d9c6215a246d7ef5426f7ecc0e5efdf8ac Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 11 Sep 2024 06:34:15 -0400 Subject: [PATCH 270/355] fix typo --- doc/src/group2ndx.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/group2ndx.rst b/doc/src/group2ndx.rst index 19c472e109..e1c4fd23f5 100644 --- a/doc/src/group2ndx.rst +++ b/doc/src/group2ndx.rst @@ -51,7 +51,7 @@ index file. When specifying group IDs, only those groups will be written to the index file. In order to follow the Gromacs conventions, the group *all* will be renamed to *System* in the index file. -The *ndx2group* command will create of update group definitions from +The *ndx2group* command will create or update group definitions from those stored in an index file. Without specifying any group IDs, all groups except *System* will be read from the index file and the corresponding groups recreated. If a group of the same name already From 9f2bc30ba6a4d4a30c5c3955e4ed59037b666a68 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 11 Sep 2024 06:46:51 -0400 Subject: [PATCH 271/355] rename source files to be consistent with naming conventions --- src/.gitignore | 8 ++++---- src/EXTRA-COMMAND/{group_ndx.cpp => group2ndx.cpp} | 6 ++---- src/EXTRA-COMMAND/{group_ndx.h => group2ndx.h} | 10 ++++------ src/EXTRA-COMMAND/{ndx_group.cpp => ndx2group.cpp} | 9 +++------ src/EXTRA-COMMAND/{ndx_group.h => ndx2group.h} | 10 ++++------ src/Purge.list | 5 +++++ 6 files changed, 22 insertions(+), 26 deletions(-) rename src/EXTRA-COMMAND/{group_ndx.cpp => group2ndx.cpp} (97%) rename src/EXTRA-COMMAND/{group_ndx.h => group2ndx.h} (83%) rename src/EXTRA-COMMAND/{ndx_group.cpp => ndx2group.cpp} (97%) rename src/EXTRA-COMMAND/{ndx_group.h => ndx2group.h} (84%) diff --git a/src/.gitignore b/src/.gitignore index c26eaaba30..5f47a58b73 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1078,12 +1078,12 @@ /geturl.cpp /geturl.h /gpu_extra.h -/group_ndx.cpp -/group_ndx.h +/group2ndx.cpp +/group2ndx.h /gz_file_writer.cpp /gz_file_writer.h -/ndx_group.cpp -/ndx_group.h +/ndx2group.cpp +/ndx2group.h /hyper.cpp /hyper.h /improper_class2.cpp diff --git a/src/EXTRA-COMMAND/group_ndx.cpp b/src/EXTRA-COMMAND/group2ndx.cpp similarity index 97% rename from src/EXTRA-COMMAND/group_ndx.cpp rename to src/EXTRA-COMMAND/group2ndx.cpp index 1dc0d3af97..56bf848923 100644 --- a/src/EXTRA-COMMAND/group_ndx.cpp +++ b/src/EXTRA-COMMAND/group2ndx.cpp @@ -1,6 +1,4 @@ -// -*- c++ -*- - -/* ---------------------------------------------------------------------- +/* -*- c++ -*-------------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator https://www.lammps.org/, Sandia National Laboratories LAMMPS development team: developers@lammps.org @@ -16,7 +14,7 @@ Contributing author: Axel Kohlmeyer (Temple U) ------------------------------------------------------------------------- */ -#include "group_ndx.h" +#include "group2ndx.h" #include "atom.h" #include "comm.h" diff --git a/src/EXTRA-COMMAND/group_ndx.h b/src/EXTRA-COMMAND/group2ndx.h similarity index 83% rename from src/EXTRA-COMMAND/group_ndx.h rename to src/EXTRA-COMMAND/group2ndx.h index 685ad82d91..e4926e2d92 100644 --- a/src/EXTRA-COMMAND/group_ndx.h +++ b/src/EXTRA-COMMAND/group2ndx.h @@ -1,6 +1,4 @@ -// -*- c++ -*- - -/* ---------------------------------------------------------- +/* -*- c++ -*----------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator https://www.lammps.org/, Sandia National Laboratories LAMMPS development team: developers@lammps.org @@ -19,8 +17,8 @@ CommandStyle(group2ndx,Group2Ndx); // clang-format on #else -#ifndef LMP_GROUP_NDX_H -#define LMP_GROUP_NDX_H +#ifndef LMP_GROUP2NDX_H +#define LMP_GROUP2NDX_H #include "command.h" @@ -28,7 +26,7 @@ namespace LAMMPS_NS { class Group2Ndx : public Command { public: - Group2Ndx(class LAMMPS *lmp) : Command(lmp){}; + Group2Ndx(class LAMMPS *lmp) : Command(lmp) {}; void command(int, char **) override; private: diff --git a/src/EXTRA-COMMAND/ndx_group.cpp b/src/EXTRA-COMMAND/ndx2group.cpp similarity index 97% rename from src/EXTRA-COMMAND/ndx_group.cpp rename to src/EXTRA-COMMAND/ndx2group.cpp index d006473bb8..ffe159b3b8 100644 --- a/src/EXTRA-COMMAND/ndx_group.cpp +++ b/src/EXTRA-COMMAND/ndx2group.cpp @@ -1,6 +1,4 @@ -// -*- c++ -*- - -/* ---------------------------------------------------------------------- +/* -*- c++ -*--------------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator https://www.lammps.org/, Sandia National Laboratories LAMMPS development team: developers@lammps.org @@ -16,7 +14,7 @@ Contributing author: Axel Kohlmeyer (Temple U) ------------------------------------------------------------------------- */ -#include "ndx_group.h" +#include "ndx2group.h" #include "atom.h" #include "comm.h" @@ -33,8 +31,7 @@ static std::string find_section(FILE *fp, const std::string &name) { char linebuf[BUFLEN]; - if (!fgets(linebuf, BUFLEN, fp)) - throw TokenizerException("Read error", utils::getsyserror()); + if (!fgets(linebuf, BUFLEN, fp)) throw TokenizerException("Read error", utils::getsyserror()); while (!feof(fp)) { if (utils::strmatch(linebuf, "^\\s*\\[.*\\]\\s*$")) { auto words = Tokenizer(linebuf).as_vector(); diff --git a/src/EXTRA-COMMAND/ndx_group.h b/src/EXTRA-COMMAND/ndx2group.h similarity index 84% rename from src/EXTRA-COMMAND/ndx_group.h rename to src/EXTRA-COMMAND/ndx2group.h index 0b35fb62a0..9a2f3c04b5 100644 --- a/src/EXTRA-COMMAND/ndx_group.h +++ b/src/EXTRA-COMMAND/ndx2group.h @@ -1,6 +1,4 @@ -// -*- c++ -*- - -/* ---------------------------------------------------------- +/* -*- c++ -*------------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator https://www.lammps.org/, Sandia National Laboratories LAMMPS development team: developers@lammps.org @@ -19,8 +17,8 @@ CommandStyle(ndx2group,Ndx2Group); // clang-format on #else -#ifndef LMP_NDX_GROUP_H -#define LMP_NDX_GROUP_H +#ifndef LMP_NDX2GROUP_H +#define LMP_NDX2GROUP_H #include "command.h" #include @@ -29,7 +27,7 @@ namespace LAMMPS_NS { class Ndx2Group : public Command { public: - Ndx2Group(class LAMMPS *lmp) : Command(lmp){}; + Ndx2Group(class LAMMPS *lmp) : Command(lmp) {}; void command(int, char **) override; private: diff --git a/src/Purge.list b/src/Purge.list index 14708a111e..7098d39e3a 100644 --- a/src/Purge.list +++ b/src/Purge.list @@ -53,6 +53,11 @@ lmpinstalledpkgs.h lmpgitversion.h mliap_model_python_couple.cpp mliap_model_python_couple.h +# renamed in September 2024 +group_ndx.cpp +group_ndx.h +ndx_group.cpp +ndx_group.h # removed in August 2023 dump_atom_mpiio.cpp dump_atom_mpiio.h From c3a62833c75680e38e36fe5b84eb6dacc0a303a1 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 11 Sep 2024 06:54:06 -0400 Subject: [PATCH 272/355] update --- src/.gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/.gitignore b/src/.gitignore index 5f47a58b73..e557a8cbb2 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -192,6 +192,8 @@ /colvarproxy_lammps_version.h /fix_colvars.cpp /fix_colvars.h +/inthash.cpp +/inthash.h /fix_plumed.cpp /fix_plumed.h /dump_molfile.cpp From e8184e63a567d3ae9a87f6e5b4a6dd1846b794e1 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 11 Sep 2024 09:15:05 -0400 Subject: [PATCH 273/355] create dummy output files to prevent test job failures --- tools/regression-tests/run_tests.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tools/regression-tests/run_tests.py b/tools/regression-tests/run_tests.py index d086f28b2c..63fa8c59c1 100755 --- a/tools/regression-tests/run_tests.py +++ b/tools/regression-tests/run_tests.py @@ -1236,6 +1236,22 @@ if __name__ == "__main__": os.remove(f"input-list-{idx}.txt") except: pass + filename = f"run-{idx}.log" + with open(filename, "w") as f: + f.write('\n') + f.close() + filename = f"progress-{idx}.yaml" + with open(filename, "w") as f: + f.write('\n') + f.close() + filename = f"output-{idx}.xml" + with open(filename, "w") as f: + f.write('\n') + f.close() + filename = f"failure-{idx}.yaml" + with open(filename, "w") as f: + f.write('\n') + f.close() quit() # if the example folders are not specified from the command-line argument --example-folders From a44955dd2ebcc330fc3415e921e5ed02764cbade Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Wed, 11 Sep 2024 09:20:36 -0600 Subject: [PATCH 274/355] Update Kokkos library in LAMMPS to v4.4.0 --- lib/kokkos/CHANGELOG.md | 78 +- lib/kokkos/CITATION.cff | 65 + lib/kokkos/CMakeLists.txt | 4 +- lib/kokkos/Makefile.kokkos | 64 +- lib/kokkos/Makefile.targets | 2 +- lib/kokkos/README.md | 65 +- .../src/sorting/impl/Kokkos_SortByKeyImpl.hpp | 55 +- .../src/std_algorithms/Kokkos_ForEach.hpp | 56 +- .../impl/Kokkos_AdjacentDifference.hpp | 10 + .../impl/Kokkos_Constraints.hpp | 61 +- .../src/std_algorithms/impl/Kokkos_CopyIf.hpp | 5 +- .../impl/Kokkos_ForEachForEachN.hpp | 20 +- .../impl/Kokkos_RandomAccessIterator.hpp | 31 + .../std_algorithms/impl/Kokkos_UniqueCopy.hpp | 5 +- .../unit_tests/TestRandomAccessIterator.cpp | 38 + .../algorithms/unit_tests/TestSortByKey.hpp | 14 +- .../TestStdAlgorithmsConstraints.cpp | 109 + .../TestStdAlgorithmsTeamExclusiveScan.cpp | 6 +- .../TestStdAlgorithmsTeamIsSorted.cpp | 4 +- .../TestStdAlgorithmsTeamIsSortedUntil.cpp | 4 +- .../TestStdAlgorithmsTeamMaxElement.cpp | 4 +- .../TestStdAlgorithmsTeamMinElement.cpp | 4 +- .../TestStdAlgorithmsTeamMinMaxElement.cpp | 4 +- .../TestStdAlgorithmsTeamReduce.cpp | 2 +- ...tdAlgorithmsTeamTransformExclusiveScan.cpp | 2 +- ...tdAlgorithmsTeamTransformInclusiveScan.cpp | 2 +- .../TestStdAlgorithmsTeamTransformReduce.cpp | 2 +- lib/kokkos/appveyor.yml | 2 +- lib/kokkos/benchmarks/CMakeLists.txt | 2 +- .../view_copy_constructor/CMakeLists.txt | 4 + .../benchmarks/view_copy_constructor/Makefile | 46 + .../view_copy_constructor.cpp | 310 +++ lib/kokkos/bin/nvcc_wrapper | 2 +- lib/kokkos/cmake/Dependencies.cmake | 1 - lib/kokkos/cmake/KokkosConfigCommon.cmake.in | 7 +- lib/kokkos/cmake/KokkosCore_config.h.in | 4 +- lib/kokkos/cmake/Modules/FindTPLCUDA.cmake | 57 +- lib/kokkos/cmake/deps/CUDA.cmake | 1 - lib/kokkos/cmake/deps/CUSPARSE.cmake | 26 - lib/kokkos/cmake/fake_tribits.cmake | 8 - lib/kokkos/cmake/kokkos_arch.cmake | 86 +- lib/kokkos/cmake/kokkos_compiler_id.cmake | 83 +- lib/kokkos/cmake/kokkos_enable_options.cmake | 6 +- lib/kokkos/cmake/kokkos_functions.cmake | 7 +- lib/kokkos/cmake/kokkos_test_cxx_std.cmake | 8 +- lib/kokkos/cmake/kokkos_tpls.cmake | 10 +- lib/kokkos/cmake/kokkos_tribits.cmake | 47 +- lib/kokkos/cmake/tpls/FindTPLCUSPARSE.cmake | 26 - lib/kokkos/containers/src/Kokkos_DualView.hpp | 58 +- .../containers/src/Kokkos_DynRankView.hpp | 404 ++-- .../containers/src/Kokkos_DynamicView.hpp | 339 ++-- .../containers/src/Kokkos_OffsetView.hpp | 265 ++- .../containers/src/Kokkos_UnorderedMap.hpp | 110 +- .../containers/unit_tests/TestDualView.hpp | 140 +- .../unit_tests/TestUnorderedMap.hpp | 7 +- .../containers/unit_tests/TestVector.hpp | 4 +- lib/kokkos/core/perf_test/test_atomic.cpp | 3 +- .../perf_test/test_atomic_minmax_simple.cpp | 8 +- lib/kokkos/core/src/Cuda/Kokkos_Cuda.hpp | 15 +- lib/kokkos/core/src/Cuda/Kokkos_CudaSpace.cpp | 67 +- .../core/src/Cuda/Kokkos_Cuda_Error.hpp | 47 - .../core/src/Cuda/Kokkos_Cuda_Graph_Impl.hpp | 9 +- .../core/src/Cuda/Kokkos_Cuda_Instance.cpp | 8 + .../core/src/Cuda/Kokkos_Cuda_Instance.hpp | 30 +- .../src/Cuda/Kokkos_Cuda_Parallel_Team.hpp | 38 +- .../core/src/Cuda/Kokkos_Cuda_abort.hpp | 25 +- lib/kokkos/core/src/HIP/Kokkos_HIP.hpp | 15 +- lib/kokkos/core/src/HIP/Kokkos_HIP_Error.hpp | 37 - .../core/src/HIP/Kokkos_HIP_Graph_Impl.hpp | 4 +- .../core/src/HIP/Kokkos_HIP_Instance.cpp | 16 + .../core/src/HIP/Kokkos_HIP_Instance.hpp | 29 +- .../HIP/Kokkos_HIP_ParallelReduce_MDRange.hpp | 1 + .../HIP/Kokkos_HIP_ParallelReduce_Team.hpp | 3 +- lib/kokkos/core/src/HIP/Kokkos_HIP_Space.cpp | 17 +- lib/kokkos/core/src/HPX/Kokkos_HPX.cpp | 4 +- lib/kokkos/core/src/HPX/Kokkos_HPX.hpp | 18 +- lib/kokkos/core/src/Kokkos_Array.hpp | 102 +- .../Kokkos_Atomics_Desul_Volatile_Wrapper.hpp | 1 - .../core/src/Kokkos_Atomics_Desul_Wrapper.hpp | 2 - lib/kokkos/core/src/Kokkos_Complex.hpp | 76 + lib/kokkos/core/src/Kokkos_CopyViews.hpp | 531 ++--- lib/kokkos/core/src/Kokkos_ExecPolicy.hpp | 81 +- lib/kokkos/core/src/Kokkos_Extents.hpp | 68 +- lib/kokkos/core/src/Kokkos_Graph.hpp | 3 + lib/kokkos/core/src/Kokkos_HostSpace.hpp | 2 - lib/kokkos/core/src/Kokkos_Layout.hpp | 120 +- lib/kokkos/core/src/Kokkos_Macros.hpp | 53 + .../core/src/Kokkos_MathematicalFunctions.hpp | 8 + lib/kokkos/core/src/Kokkos_Pair.hpp | 22 +- lib/kokkos/core/src/Kokkos_Parallel.hpp | 33 +- .../core/src/Kokkos_Parallel_Reduce.hpp | 75 +- lib/kokkos/core/src/Kokkos_View.hpp | 262 +-- .../core/src/OpenACC/Kokkos_OpenACCSpace.cpp | 11 +- .../Kokkos_OpenACC_ParallelFor_Team.hpp | 8 +- lib/kokkos/core/src/OpenMP/Kokkos_OpenMP.cpp | 21 +- lib/kokkos/core/src/OpenMP/Kokkos_OpenMP.hpp | 18 +- .../src/OpenMP/Kokkos_OpenMP_Instance.cpp | 38 +- .../src/OpenMP/Kokkos_OpenMP_Instance.hpp | 22 +- .../src/OpenMP/Kokkos_OpenMP_Parallel_For.hpp | 12 +- .../OpenMP/Kokkos_OpenMP_Parallel_Reduce.hpp | 20 +- .../OpenMP/Kokkos_OpenMP_Parallel_Scan.hpp | 10 +- .../core/src/OpenMP/Kokkos_OpenMP_Task.cpp | 13 +- .../core/src/OpenMP/Kokkos_OpenMP_Task.hpp | 11 +- .../src/OpenMPTarget/Kokkos_OpenMPTarget.hpp | 3 +- .../OpenMPTarget/Kokkos_OpenMPTargetSpace.cpp | 6 +- .../OpenMPTarget/Kokkos_OpenMPTarget_Exec.cpp | 40 - .../Kokkos_OpenMPTarget_Instance.cpp | 1 - .../Kokkos_OpenMPTarget_MDRangePolicy.hpp | 5 + .../Kokkos_OpenMPTarget_Parallel.hpp | 4 - ...okkos_OpenMPTarget_ParallelFor_MDRange.hpp | 383 ++++ ...s_OpenMPTarget_ParallelReduce_MDRange.hpp} | 753 ++++--- ...kkos_OpenMPTarget_ParallelReduce_Range.hpp | 9 +- ...okkos_OpenMPTarget_ParallelReduce_Team.hpp | 10 +- ...Kokkos_OpenMPTarget_ParallelScan_Range.hpp | 10 +- lib/kokkos/core/src/SYCL/Kokkos_SYCL.cpp | 25 + .../src/SYCL/Kokkos_SYCL_GraphNodeKernel.hpp | 157 ++ .../src/SYCL/Kokkos_SYCL_GraphNode_Impl.hpp | 56 + .../core/src/SYCL/Kokkos_SYCL_Graph_Impl.hpp | 174 ++ .../core/src/SYCL/Kokkos_SYCL_Instance.cpp | 34 +- .../core/src/SYCL/Kokkos_SYCL_Instance.hpp | 34 +- .../SYCL/Kokkos_SYCL_ParallelFor_MDRange.hpp | 28 +- .../SYCL/Kokkos_SYCL_ParallelFor_Range.hpp | 63 +- .../src/SYCL/Kokkos_SYCL_ParallelFor_Team.hpp | 75 +- .../Kokkos_SYCL_ParallelReduce_MDRange.hpp | 68 +- .../SYCL/Kokkos_SYCL_ParallelReduce_Range.hpp | 71 +- .../SYCL/Kokkos_SYCL_ParallelReduce_Team.hpp | 138 +- .../SYCL/Kokkos_SYCL_ParallelScan_Range.hpp | 221 ++- .../core/src/SYCL/Kokkos_SYCL_Space.cpp | 89 +- lib/kokkos/core/src/SYCL/Kokkos_SYCL_Team.hpp | 109 +- .../SYCL/Kokkos_SYCL_WorkgroupReduction.hpp | 103 +- lib/kokkos/core/src/Serial/Kokkos_Serial.cpp | 41 +- lib/kokkos/core/src/Serial/Kokkos_Serial.hpp | 29 +- .../Serial/Kokkos_Serial_Parallel_MDRange.hpp | 17 +- .../Serial/Kokkos_Serial_Parallel_Range.hpp | 28 +- .../Serial/Kokkos_Serial_Parallel_Team.hpp | 16 +- .../core/src/Threads/Kokkos_Threads_Team.hpp | 7 +- lib/kokkos/core/src/View/Kokkos_ViewAlloc.hpp | 318 +++ .../View/MDSpan/Kokkos_MDSpan_Accessor.hpp | 220 +++ .../src/View/MDSpan/Kokkos_MDSpan_Extents.hpp | 19 +- .../src/View/MDSpan/Kokkos_MDSpan_Layout.hpp | 156 ++ .../core/src/decl/Kokkos_Declare_SYCL.hpp | 3 + lib/kokkos/core/src/impl/Kokkos_Core.cpp | 37 +- .../src/impl/Kokkos_Default_Graph_Impl.hpp | 7 +- .../Kokkos_DesulAtomicsConfig.hpp} | 12 +- lib/kokkos/core/src/impl/Kokkos_Error.cpp | 121 +- lib/kokkos/core/src/impl/Kokkos_Error.hpp | 107 +- lib/kokkos/core/src/impl/Kokkos_HostSpace.cpp | 23 +- .../core/src/impl/Kokkos_HostThreadTeam.hpp | 23 +- lib/kokkos/core/src/impl/Kokkos_Profiling.cpp | 78 - lib/kokkos/core/src/impl/Kokkos_Profiling.hpp | 59 +- .../src/impl/Kokkos_Profiling_C_Interface.h | 8 + .../core/src/impl/Kokkos_SharedAlloc.cpp | 35 - .../core/src/impl/Kokkos_SharedAlloc.hpp | 86 +- lib/kokkos/core/src/impl/Kokkos_ViewArray.hpp | 622 ------ .../core/src/impl/Kokkos_ViewLayoutTiled.hpp | 1425 ------------- .../core/src/impl/Kokkos_ViewMapping.hpp | 572 ++---- .../core/src/setup/Kokkos_Setup_Cuda.hpp | 2 + .../core/src/setup/Kokkos_Setup_HIP.hpp | 2 + .../core/src/setup/Kokkos_Setup_SYCL.hpp | 17 + lib/kokkos/core/unit_test/CMakeLists.txt | 125 +- lib/kokkos/core/unit_test/Makefile | 22 +- lib/kokkos/core/unit_test/TestAggregate.hpp | 108 - lib/kokkos/core/unit_test/TestArray.cpp | 193 +- lib/kokkos/core/unit_test/TestArrayOps.hpp | 29 + .../core/unit_test/TestAtomicOperations.hpp | 8 +- .../unit_test/TestBitManipulationBuiltins.hpp | 6 - lib/kokkos/core/unit_test/TestComplex.hpp | 166 +- .../unit_test/TestExecSpaceThreadSafety.hpp | 327 +++ .../core/unit_test/TestExecutionSpace.hpp | 56 + lib/kokkos/core/unit_test/TestGraph.hpp | 71 +- .../core/unit_test/TestLocalDeepCopy.hpp | 28 +- lib/kokkos/core/unit_test/TestMDSpan.hpp | 8 +- .../unit_test/TestMDSpanAtomicAccessor.hpp | 112 ++ .../core/unit_test/TestMDSpanConversion.hpp | 507 +++++ .../unit_test/TestMathematicalConstants.hpp | 3 +- .../unit_test/TestMathematicalFunctions.hpp | 80 +- lib/kokkos/core/unit_test/TestMultiGPU.hpp | 184 ++ .../core/unit_test/TestNestedReducerCTAD.cpp | 246 +++ .../core/unit_test/TestNumericTraits.hpp | 105 +- lib/kokkos/core/unit_test/TestOther.hpp | 5 - .../unit_test/TestRangePolicyConstructors.hpp | 40 + lib/kokkos/core/unit_test/TestRealloc.hpp | 13 + lib/kokkos/core/unit_test/TestResize.hpp | 13 + .../core/unit_test/TestSpaceAwareAccessor.hpp | 156 ++ .../TestSpaceAwareAccessorAccessViolation.hpp | 128 ++ .../unit_test/TestTeamMDRangePolicyCTAD.cpp | 199 ++ .../core/unit_test/TestTeamPolicyCTAD.cpp | 135 ++ lib/kokkos/core/unit_test/TestTeamVector.hpp | 7 +- .../core/unit_test/TestTeamVectorRange.hpp | 6 - lib/kokkos/core/unit_test/TestViewAPI.hpp | 98 +- lib/kokkos/core/unit_test/TestViewAPI_c.hpp | 1 + lib/kokkos/core/unit_test/TestViewAPI_d.hpp | 18 - .../core/unit_test/TestViewBadAlloc.hpp | 86 + lib/kokkos/core/unit_test/TestViewCopy_c.hpp | 434 ++++ .../core/unit_test/TestViewLayoutTiled.hpp | 1756 ----------------- lib/kokkos/core/unit_test/TestViewOfViews.hpp | 75 + lib/kokkos/core/unit_test/TestViewSubview.hpp | 5 +- .../core/unit_test/UnitTest_ScopeGuard.cpp | 155 ++ .../category_files/TestHPX_Category.hpp | 1 + .../category_files/TestOpenACC_Category.hpp | 1 + .../TestOpenMPTarget_Category.hpp | 1 + .../category_files/TestSYCL_Category.hpp | 1 + .../category_files/TestThreads_Category.hpp | 1 + .../core/unit_test/cuda/TestCuda_Graph.cpp | 18 - .../cuda/TestCuda_InterOp_StreamsMultiGPU.cpp | 162 +- .../headers_self_contained/CMakeLists.txt | 3 +- .../hip/TestHIP_Memory_Requirements.cpp | 3 - .../incremental/Test01_execspace.hpp | 2 + .../unit_test/openmp/TestOpenMP_Graph.cpp | 18 - .../unit_test/serial/TestSerial_Graph.cpp | 18 - .../sycl/TestSYCL_InterOp_StreamsMultiGPU.cpp | 64 + .../view/TestExtentsDatatypeConversion.cpp | 11 +- lib/kokkos/example/README | 4 +- .../build_cmake_installed/CMakeLists.txt | 1 + .../tutorial/01_hello_world/hello_world.cpp | 19 +- .../hello_world_lambda.cpp | 14 +- .../simple_reduce_lambda.cpp | 5 +- lib/kokkos/generate_makefile.bash | 1 - lib/kokkos/master_history.txt | 1 + lib/kokkos/simd/src/Kokkos_SIMD.hpp | 9 +- lib/kokkos/simd/src/Kokkos_SIMD_AVX2.hpp | 869 +++++++- lib/kokkos/simd/src/Kokkos_SIMD_AVX512.hpp | 1112 ++++++++++- lib/kokkos/simd/src/Kokkos_SIMD_NEON.hpp | 833 +++++++- lib/kokkos/simd/unit_tests/CMakeLists.txt | 12 +- .../unit_tests/include/SIMDTesting_Ops.hpp | 2 + .../include/SIMDTesting_Utilities.hpp | 12 +- .../unit_tests/include/TestSIMD_Condition.hpp | 54 +- .../include/TestSIMD_Conversions.hpp | 142 +- .../include/TestSIMD_GeneratorCtors.hpp | 114 +- .../unit_tests/include/TestSIMD_MaskOps.hpp | 66 +- .../unit_tests/include/TestSIMD_MathOps.hpp | 83 +- .../include/TestSIMD_Reductions.hpp | 36 +- .../unit_tests/include/TestSIMD_ShiftOps.hpp | 82 +- .../include/TestSIMD_WhereExpressions.hpp | 140 +- .../desul/include/desul/atomics/Adapt_HIP.hpp | 77 + .../include/desul/atomics/Atomic_Ref.hpp | 566 +----- .../desul/atomics/Compare_Exchange_HIP.hpp | 145 +- .../include/desul/atomics/Fetch_Op_CUDA.hpp | 54 +- .../desul/atomics/Fetch_Op_Generic.hpp | 88 +- .../include/desul/atomics/Fetch_Op_HIP.hpp | 173 +- .../atomics/Operator_Function_Objects.hpp | 34 +- .../experimental/__p0009_bits/config.hpp | 2 +- .../experimental/__p0009_bits/extents.hpp | 95 +- .../experimental/__p0009_bits/layout_left.hpp | 26 +- .../__p0009_bits/layout_right.hpp | 25 +- .../__p0009_bits/layout_stride.hpp | 185 +- .../experimental/__p0009_bits/macros.hpp | 70 +- .../experimental/__p0009_bits/mdspan.hpp | 4 +- .../experimental/__p0009_bits/utility.hpp | 72 + .../experimental/__p2389_bits/dims.hpp} | 14 +- .../__p2630_bits/submdspan_mapping.hpp | 682 +++++-- .../__p2642_bits/layout_padded.hpp | 540 ++--- .../__p2642_bits/layout_padded_fwd.hpp | 64 +- .../tpls/mdspan/include/mdspan/mdspan.hpp | 1 + 254 files changed, 14227 insertions(+), 9881 deletions(-) create mode 100644 lib/kokkos/CITATION.cff create mode 100644 lib/kokkos/benchmarks/view_copy_constructor/CMakeLists.txt create mode 100644 lib/kokkos/benchmarks/view_copy_constructor/Makefile create mode 100644 lib/kokkos/benchmarks/view_copy_constructor/view_copy_constructor.cpp delete mode 100644 lib/kokkos/cmake/deps/CUSPARSE.cmake delete mode 100644 lib/kokkos/cmake/tpls/FindTPLCUSPARSE.cmake create mode 100644 lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_ParallelFor_MDRange.hpp rename lib/kokkos/core/src/OpenMPTarget/{Kokkos_OpenMPTarget_Parallel_MDRange.hpp => Kokkos_OpenMPTarget_ParallelReduce_MDRange.hpp} (61%) create mode 100644 lib/kokkos/core/src/SYCL/Kokkos_SYCL_GraphNodeKernel.hpp create mode 100644 lib/kokkos/core/src/SYCL/Kokkos_SYCL_GraphNode_Impl.hpp create mode 100644 lib/kokkos/core/src/SYCL/Kokkos_SYCL_Graph_Impl.hpp create mode 100644 lib/kokkos/core/src/View/Kokkos_ViewAlloc.hpp create mode 100644 lib/kokkos/core/src/View/MDSpan/Kokkos_MDSpan_Accessor.hpp create mode 100644 lib/kokkos/core/src/View/MDSpan/Kokkos_MDSpan_Layout.hpp rename lib/kokkos/core/src/{Kokkos_Atomics_Desul_Config.hpp => impl/Kokkos_DesulAtomicsConfig.hpp} (72%) delete mode 100644 lib/kokkos/core/src/impl/Kokkos_ViewArray.hpp delete mode 100644 lib/kokkos/core/src/impl/Kokkos_ViewLayoutTiled.hpp delete mode 100644 lib/kokkos/core/unit_test/TestAggregate.hpp create mode 100644 lib/kokkos/core/unit_test/TestExecSpaceThreadSafety.hpp create mode 100644 lib/kokkos/core/unit_test/TestMDSpanAtomicAccessor.hpp create mode 100644 lib/kokkos/core/unit_test/TestMDSpanConversion.hpp create mode 100644 lib/kokkos/core/unit_test/TestMultiGPU.hpp create mode 100644 lib/kokkos/core/unit_test/TestNestedReducerCTAD.cpp create mode 100644 lib/kokkos/core/unit_test/TestSpaceAwareAccessor.hpp create mode 100644 lib/kokkos/core/unit_test/TestSpaceAwareAccessorAccessViolation.hpp create mode 100644 lib/kokkos/core/unit_test/TestTeamMDRangePolicyCTAD.cpp create mode 100644 lib/kokkos/core/unit_test/TestTeamPolicyCTAD.cpp create mode 100644 lib/kokkos/core/unit_test/TestViewBadAlloc.hpp create mode 100644 lib/kokkos/core/unit_test/TestViewCopy_c.hpp delete mode 100644 lib/kokkos/core/unit_test/TestViewLayoutTiled.hpp create mode 100644 lib/kokkos/core/unit_test/TestViewOfViews.hpp create mode 100644 lib/kokkos/core/unit_test/UnitTest_ScopeGuard.cpp delete mode 100644 lib/kokkos/core/unit_test/cuda/TestCuda_Graph.cpp delete mode 100644 lib/kokkos/core/unit_test/openmp/TestOpenMP_Graph.cpp delete mode 100644 lib/kokkos/core/unit_test/serial/TestSerial_Graph.cpp create mode 100644 lib/kokkos/core/unit_test/sycl/TestSYCL_InterOp_StreamsMultiGPU.cpp create mode 100644 lib/kokkos/tpls/desul/include/desul/atomics/Adapt_HIP.hpp create mode 100644 lib/kokkos/tpls/mdspan/include/experimental/__p0009_bits/utility.hpp rename lib/kokkos/{core/unit_test/hip/TestHIP_Graph.cpp => tpls/mdspan/include/experimental/__p2389_bits/dims.hpp} (59%) diff --git a/lib/kokkos/CHANGELOG.md b/lib/kokkos/CHANGELOG.md index 4fbc900297..78225f9e6c 100644 --- a/lib/kokkos/CHANGELOG.md +++ b/lib/kokkos/CHANGELOG.md @@ -1,12 +1,88 @@ # CHANGELOG +## [4.4.00](https://github.com/kokkos/kokkos/tree/4.4.00) +[Full Changelog](https://github.com/kokkos/kokkos/compare/4.3.01...4.4.00) + +### Features: +* Add `Kokkos::View` conversions from and to [`std::mdspan`](https://en.cppreference.com/w/cpp/container/mdspan) [\#6830](https://github.com/kokkos/kokkos/pull/6830) [\#7069](https://github.com/kokkos/kokkos/pull/7069) + +### Backend and Architecture Enhancements: + +#### CUDA: +* `nvcc_wrapper`: Adding ability to process `--disable-warnings` flag [\#6936](https://github.com/kokkos/kokkos/issues/6936) +* Use recommended/max team size functions in Cuda ParallelFor and Reduce constructors [\#6891](https://github.com/kokkos/kokkos/issues/6891) +* Improve compile-times when building with `Kokkos_ENABLE_DEBUG_BOUNDS_CHECK` in Cuda [\#7013](https://github.com/kokkos/kokkos/pull/7013) + +#### HIP: +* Use HIP builtin atomics [\#6882](https://github.com/kokkos/kokkos/pull/6882) [\#7000](https://github.com/kokkos/kokkos/pull/7000) +* Enable user-specified compiler and linker flags for AMD GPUs [\#7127](https://github.com/kokkos/kokkos/pull/7127) + +#### SYCL: +* Add support for Graphs [\#6912](https://github.com/kokkos/kokkos/pull/6912) +* Fix multi-GPU support [\#6887](https://github.com/kokkos/kokkos/pull/6887) +* Improve performance of reduction and scan operations [\#6562](https://github.com/kokkos/kokkos/pull/6562), [\#6750](https://github.com/kokkos/kokkos/pull/6750) +* Fix lock for guarding scratch space in `TeamPolicy` `parallel_reduce` [\#6988](https://github.com/kokkos/kokkos/pull/6988) +* Include submission command queue property information into `SYCL::print_configuration()` [\#7004](https://github.com/kokkos/kokkos/pull/7004) + +#### OpenACC: +* Make `TeamPolicy` `parallel_for` execute on the correct async queue [\#7012](https://github.com/kokkos/kokkos/pull/7012) + +#### OpenMPTarget: +* Honor user requested loop ordering in `MDRange` policy [\#6925](https://github.com/kokkos/kokkos/pull/6925) +* Prevent data races by guarding the scratch space used in `parallel_scan` [\#6998](https://github.com/kokkos/kokkos/pull/6998) + +#### HPX: +* Workaround issue with template argument deduction to support compilation with NVCC [\#7015](https://github.com/kokkos/kokkos/pull/7015) + +### General Enhancements +* Improve performance of view copies in host parallel regions [\#6730](https://github.com/kokkos/kokkos/pull/6730) +* Harmonize convertibility rules of `Kokkos::RandomAccessIterator` with `View`s [\#6929](https://github.com/kokkos/kokkos/pull/6929) +* Add a check precondition non-overlapping ranges for the `adjacent_difference` algorithm in debug mode [\#6922](https://github.com/kokkos/kokkos/pull/6922) +* Add deduction guides for `TeamPolicy` [\#7030](https://github.com/kokkos/kokkos/pull/7030) +* SIMD: Allow flexible vector width for 32 bit types [\#6802](https://github.com/kokkos/kokkos/pull/6802) +* Updates for `Kokkos::Array`: add `kokkos_swap(Array)` specialization [\#6943](https://github.com/kokkos/kokkos/pull/6943), add `Kokkos::to_array` [\#6375](https://github.com/kokkos/kokkos/pull/6375), make `Kokkos::Array` equality-comparable [\#7148](https://github.com/kokkos/kokkos/pull/7148) +* Structured binding support for `Kokkos::complex` [\#7040](https://github.com/kokkos/kokkos/pull/7040) + +### Build System Changes +* Do not require OpenMP support for languages other than CXX [\#6965](https://github.com/kokkos/kokkos/pull/6965) +* Update Intel GPU architectures in Makefile [\#6895](https://github.com/kokkos/kokkos/pull/6895) +* Fix use of OpenMP with Cuda or HIP as compile language [\#6972](https://github.com/kokkos/kokkos/pull/6972) +* Define and enforce new minimum compiler versions for C++20 support [\#7128](https://github.com/kokkos/kokkos/pull/7128), [\#7123](https://github.com/kokkos/kokkos/pull/7123) +* Add nvidia Grace CPU architecture: `Kokkos_ARCH_ARMV9_GRACE` [\#7158](https://github.com/kokkos/kokkos/pull/7158) +* Fix Makefile.kokkos for Threads [\#6896](https://github.com/kokkos/kokkos/pull/6896) +* Remove support for NVHPC as CUDA device compiler [\#6987](https://github.com/kokkos/kokkos/pull/6987) +* Fix using CUDAToolkit for CMake 3.28.4 and higher [\#7062](https://github.com/kokkos/kokkos/pull/7062) + +### Incompatibilities (i.e. breaking changes) +* Drop `Kokkos::Array` special treatment in `View`s [\#6906](https://github.com/kokkos/kokkos/pull/6906) +* Drop `Experimental::RawMemoryAllocationFailure` [\#7145](https://github.com/kokkos/kokkos/pull/7145) + +### Deprecations +* Remove `Experimental::LayoutTiled` class template and deprecate `is_layouttiled` trait [\#6907](https://github.com/kokkos/kokkos/pull/6907) +* Deprecate `Kokkos::layout_iterate_type_selector` [\#7076](https://github.com/kokkos/kokkos/pull/7076) +* Deprecate specialization of `Kokkos::pair` for a single element [\#6947](https://github.com/kokkos/kokkos/pull/6947) +* Deprecate `deep_copy` of `UnorderedMap` of different size [\#6812](https://github.com/kokkos/kokkos/pull/6812) +* Deprecate trailing `Proxy` template argument of `Kokkos::Array` [\#6934](https://github.com/kokkos/kokkos/pull/6934) +* Deprecate implicit conversions of integers to `ChunkSize` [\#7151](https://github.com/kokkos/kokkos/pull/7151) +* Deprecate implicit conversions to execution spaces [\#7156](https://github.com/kokkos/kokkos/pull/7156) + +### Bug Fixes +* Do not return a copy of the input functor in `Experimental::for_each` [\#6910](https://github.com/kokkos/kokkos/pull/6910) +* Fix `realloc` on views of non-default constructible element types [\#6993](https://github.com/kokkos/kokkos/pull/6993) +* Fix undefined behavior in `View` initialization or fill with zeros [\#7014](https://github.com/kokkos/kokkos/pull/7014) +* Fix `sort_by_key` on host execution spaces when building with NVCC [\#7059](https://github.com/kokkos/kokkos/pull/7059) +* Fix using shared libraries and -fvisibility=hidden [\#7065](https://github.com/kokkos/kokkos/pull/7065) +* Fix view reference counting when functor copy constructor throws in parallel dispatch [\#6289](https://github.com/kokkos/kokkos/pull/6289) +* Fix `initialize(InitializationSetting)` for handling `print_configuration` setting [\#7098](https://github.com/kokkos/kokkos/pull/7098) +* Thread safety fixes for the Serial and OpenMP backend [\#7080](https://github.com/kokkos/kokkos/pull/7080), [\#6151](https://github.com/kokkos/kokkos/pull/6151) + ## [4.3.01](https://github.com/kokkos/kokkos/tree/4.3.01) [Full Changelog](https://github.com/kokkos/kokkos/compare/4.3.00...4.3.01) ### Backend and Architecture Enhancements: #### HIP: -* MI300 support unified memory support [\#6877](https://github.com/kokkos/kokkos/pull/6877) +* MI300 support unified memory [\#6877](https://github.com/kokkos/kokkos/pull/6877) ### Bug Fixes * Serial: Use the provided execution space instance in TeamPolicy [\#6951](https://github.com/kokkos/kokkos/pull/6951) diff --git a/lib/kokkos/CITATION.cff b/lib/kokkos/CITATION.cff new file mode 100644 index 0000000000..28c674c451 --- /dev/null +++ b/lib/kokkos/CITATION.cff @@ -0,0 +1,65 @@ +cff-version: 1.2.0 +title: Kokkos +message: >- + If you use this software, please cite the overview paper +type: software +authors: + - name: The Kokkos authors + website: https://kokkos.org/community/team/ +identifiers: + - type: url + website: https://kokkos.org/kokkos-core-wiki/citation.html +repository-code: 'https://github.com/kokkos/kokkos' +url: 'https://kokkos.org/' +license: Apache-2.0 +preferred-citation: + type: article + authors: + - given-names: Christian R. + family-names: Trott + - given-names: Damien + family-names: Lebrun-Grandié + - given-names: Daniel + family-names: Arndt + - family-names: Ciesko + given-names: Jan + - given-names: Vinh + family-names: Dang + - family-names: Ellingwood + given-names: Nathan + - given-names: Rahulkumar + family-names: Gayatri + - given-names: Evan + family-names: Harvey + - given-names: Daisy S. + family-names: Hollman + - given-names: Dan + family-names: Ibanez + - given-names: Nevin + family-names: Liber + - given-names: Jonathan + family-names: Madsen + - given-names: Jeff + family-names: Miles + - given-names: David + family-names: Poliakoff + - given-names: Amy + family-names: Powell + - given-names: Sivasankaran + family-names: Rajamanickam + - given-names: Mikael + family-names: Simberg + - given-names: Dan + family-names: Sunderland + - given-names: Bruno + family-names: Turcksin + - given-names: Jeremiah + family-names: Wilke + doi: 10.1109/TPDS.2021.3097283 + journal: IEEE Transactions on Parallel and Distributed Systems + start: 805 + end: 817 + title: "Kokkos 3: Programming Model Extensions for the Exascale Era" + volume: 33 + issue: 4 + year: 2022 diff --git a/lib/kokkos/CMakeLists.txt b/lib/kokkos/CMakeLists.txt index 76f2183db8..054de2c1da 100644 --- a/lib/kokkos/CMakeLists.txt +++ b/lib/kokkos/CMakeLists.txt @@ -150,8 +150,8 @@ ENDIF() set(Kokkos_VERSION_MAJOR 4) -set(Kokkos_VERSION_MINOR 3) -set(Kokkos_VERSION_PATCH 1) +set(Kokkos_VERSION_MINOR 4) +set(Kokkos_VERSION_PATCH 0) set(Kokkos_VERSION "${Kokkos_VERSION_MAJOR}.${Kokkos_VERSION_MINOR}.${Kokkos_VERSION_PATCH}") message(STATUS "Kokkos version: ${Kokkos_VERSION}") math(EXPR KOKKOS_VERSION "${Kokkos_VERSION_MAJOR} * 10000 + ${Kokkos_VERSION_MINOR} * 100 + ${Kokkos_VERSION_PATCH}") diff --git a/lib/kokkos/Makefile.kokkos b/lib/kokkos/Makefile.kokkos index 6fdddd9a53..a8e1e803f4 100644 --- a/lib/kokkos/Makefile.kokkos +++ b/lib/kokkos/Makefile.kokkos @@ -11,8 +11,8 @@ CXXFLAGS += $(SHFLAGS) endif KOKKOS_VERSION_MAJOR = 4 -KOKKOS_VERSION_MINOR = 3 -KOKKOS_VERSION_PATCH = 1 +KOKKOS_VERSION_MINOR = 4 +KOKKOS_VERSION_PATCH = 0 KOKKOS_VERSION = $(shell echo $(KOKKOS_VERSION_MAJOR)*10000+$(KOKKOS_VERSION_MINOR)*100+$(KOKKOS_VERSION_PATCH) | bc) # Options: Cuda,HIP,SYCL,OpenMPTarget,OpenMP,Threads,Serial @@ -21,11 +21,11 @@ KOKKOS_DEVICES ?= "OpenMP" # Options: # Intel: KNC,KNL,SNB,HSW,BDW,SKL,SKX,ICL,ICX,SPR # NVIDIA: Kepler,Kepler30,Kepler32,Kepler35,Kepler37,Maxwell,Maxwell50,Maxwell52,Maxwell53,Pascal60,Pascal61,Volta70,Volta72,Turing75,Ampere80,Ampere86,Ada89,Hopper90 -# ARM: ARMv80,ARMv81,ARMv8-ThunderX,ARMv8-TX2,A64FX +# ARM: ARMv80,ARMv81,ARMv8-ThunderX,ARMv8-TX2,A64FX,ARMv9-Grace # IBM: Power8,Power9 -# AMD-GPUS: AMD_GFX906,AMD_GFX908,AMD_GFX90A,AMD_GFX940,AMD_GFX942,AMD_GFX1030,AMD_GFX1100,AMD_GFX1103 +# AMD-GPUS: AMD_GFX906,AMD_GFX908,AMD_GFX90A,AMD_GFX940,AMD_GFX942,AMD_GFX1030,AMD_GFX1100 # AMD-CPUS: AMDAVX,Zen,Zen2,Zen3 -# Intel-GPUs: Gen9,Gen11,Gen12LP,DG1,XeHP,PVC +# Intel-GPUs: Intel_Gen,Intel_Gen9,Intel_Gen11,Intel_Gen12LP,Intel_DG1,Intel_XeHP,Intel_PVC KOKKOS_ARCH ?= "" # Options: yes,no KOKKOS_DEBUG ?= "no" @@ -41,7 +41,7 @@ KOKKOS_STANDALONE_CMAKE ?= "no" # Default settings specific options. # Options: force_uvm,use_ldg,rdc,enable_lambda,enable_constexpr,disable_malloc_async -KOKKOS_CUDA_OPTIONS ?= "enable_lambda" +KOKKOS_CUDA_OPTIONS ?= "enable_lambda,disable_malloc_async" # Options: rdc KOKKOS_HIP_OPTIONS ?= "" @@ -328,12 +328,43 @@ KOKKOS_INTERNAL_USE_ARCH_ICL := $(call kokkos_has_string,$(KOKKOS_ARCH),ICL) KOKKOS_INTERNAL_USE_ARCH_ICX := $(call kokkos_has_string,$(KOKKOS_ARCH),ICX) KOKKOS_INTERNAL_USE_ARCH_SPR := $(call kokkos_has_string,$(KOKKOS_ARCH),SPR) -KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN := $(call kokkos_has_string,$(KOKKOS_ARCH),IntelGen) +# Traditionally, we supported, e.g., IntelGen9 instead of Intel_Gen9. The latter +# matches the CMake option but we also accept the former for backward-compatibility. KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN9 := $(call kokkos_has_string,$(KOKKOS_ARCH),IntelGen9) +ifeq ($(KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN9), 0) + KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN9 := $(call kokkos_has_string,$(KOKKOS_ARCH),Intel_Gen9) +endif KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN11 := $(call kokkos_has_string,$(KOKKOS_ARCH),IntelGen11) +ifeq ($(KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN11), 0) + KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN11 := $(call kokkos_has_string,$(KOKKOS_ARCH),Intel_Gen11) +endif KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN12LP := $(call kokkos_has_string,$(KOKKOS_ARCH),IntelGen12LP) +ifeq ($(KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN12LP), 0) + KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN12LP := $(call kokkos_has_string,$(KOKKOS_ARCH),Intel_Gen12LP) +endif +KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN := $(call kokkos_has_string,$(KOKKOS_ARCH),IntelGen9) +ifeq ($(KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN9), 0) + KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN9 := $(call kokkos_has_string,$(KOKKOS_ARCH),Intel_Gen9) +endif +KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN_SET := $(shell expr $(KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN9) \ + + $(KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN11) \ + + $(KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN12LP)) +ifeq ($(KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN_SET), 0) + KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN := $(call kokkos_has_string,$(KOKKOS_ARCH),IntelGen) + ifeq ($(KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN), 0) + KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN := $(call kokkos_has_string,$(KOKKOS_ARCH),Intel_Gen) + endif +endif KOKKOS_INTERNAL_USE_ARCH_INTEL_DG1 := $(call kokkos_has_string,$(KOKKOS_ARCH),IntelDG1) +ifeq ($(KOKKOS_INTERNAL_USE_ARCH_INTEL_DG1), 0) + KOKKOS_INTERNAL_USE_ARCH_INTEL_DG1 := $(call kokkos_has_string,$(KOKKOS_ARCH),Intel_DG1) +endif KOKKOS_INTERNAL_USE_ARCH_INTEL_XEHP := $(call kokkos_has_string,$(KOKKOS_ARCH),IntelXeHP) +ifeq ($(KOKKOS_INTERNAL_USE_ARCH_INTEL_XEHP), 0) + KOKKOS_INTERNAL_USE_ARCH_INTEL_XEHP := $(call kokkos_has_string,$(KOKKOS_ARCH),Intel_XeHP) +endif +# Traditionally the architecture was called PVC instead of Intel_PVC. This +# version makes us accept IntelPVC and Intel_PVC as well. KOKKOS_INTERNAL_USE_ARCH_INTEL_PVC := $(call kokkos_has_string,$(KOKKOS_ARCH),PVC) # NVIDIA based. @@ -394,7 +425,8 @@ KOKKOS_INTERNAL_USE_ARCH_ARMV81 := $(call kokkos_has_string,$(KOKKOS_ARCH),ARMv8 KOKKOS_INTERNAL_USE_ARCH_ARMV8_THUNDERX := $(call kokkos_has_string,$(KOKKOS_ARCH),ARMv8-ThunderX) KOKKOS_INTERNAL_USE_ARCH_ARMV8_THUNDERX2 := $(call kokkos_has_string,$(KOKKOS_ARCH),ARMv8-TX2) KOKKOS_INTERNAL_USE_ARCH_A64FX := $(call kokkos_has_string,$(KOKKOS_ARCH),A64FX) -KOKKOS_INTERNAL_USE_ARCH_ARM := $(strip $(shell echo $(KOKKOS_INTERNAL_USE_ARCH_ARMV80)+$(KOKKOS_INTERNAL_USE_ARCH_ARMV81)+$(KOKKOS_INTERNAL_USE_ARCH_ARMV8_THUNDERX)+$(KOKKOS_INTERNAL_USE_ARCH_ARMV8_THUNDERX2)+$(KOKKOS_INTERNAL_USE_ARCH_A64FX) | bc)) +KOKKOS_INTERNAL_USE_ARCH_ARMV9_GRACE := $(call kokkos_has_string,$(KOKKOS_ARCH),ARMv9-Grace) +KOKKOS_INTERNAL_USE_ARCH_ARM := $(strip $(shell echo $(KOKKOS_INTERNAL_USE_ARCH_ARMV80)+$(KOKKOS_INTERNAL_USE_ARCH_ARMV81)+$(KOKKOS_INTERNAL_USE_ARCH_ARMV8_THUNDERX)+$(KOKKOS_INTERNAL_USE_ARCH_ARMV8_THUNDERX2)+$(KOKKOS_INTERNAL_USE_ARCH_A64FX)+$(KOKKOS_INTERNAL_USE_ARCH_ARMV9_GRACE) | bc)) # IBM based. KOKKOS_INTERNAL_USE_ARCH_POWER8 := $(call kokkos_has_string,$(KOKKOS_ARCH),Power8) @@ -433,7 +465,6 @@ KOKKOS_INTERNAL_USE_ARCH_AMD_GFX1100 := $(call kokkos_has_string,$(KOKKOS_ARCH), ifeq ($(KOKKOS_INTERNAL_USE_ARCH_AMD_GFX1100), 0) KOKKOS_INTERNAL_USE_ARCH_AMD_GFX1100 := $(call kokkos_has_string,$(KOKKOS_ARCH),NAVI1100) endif -KOKKOS_INTERNAL_USE_ARCH_AMD_GFX1103 := $(call kokkos_has_string,$(KOKKOS_ARCH),AMD_GFX1103) # Any AVX? KOKKOS_INTERNAL_USE_ARCH_AVX := $(shell expr $(KOKKOS_INTERNAL_USE_ARCH_SNB) + $(KOKKOS_INTERNAL_USE_ARCH_AMDAVX)) @@ -758,6 +789,14 @@ ifeq ($(KOKKOS_INTERNAL_USE_ARCH_A64FX), 1) endif endif +ifeq ($(KOKKOS_INTERNAL_USE_ARCH_ARMV9_GRACE), 1) + tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_ARMV9_GRACE") + tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_ARM_NEON") + + KOKKOS_CXXFLAGS += -mcpu=neoverse-v2 -msve-vector-bits=128 + KOKKOS_LDFLAGS += -mcpu=neoverse-v2 -msve-vector-bits=128 +endif + ifeq ($(KOKKOS_INTERNAL_USE_ARCH_ZEN), 1) tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_AMD_ZEN") tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_AVX2") @@ -1119,11 +1158,6 @@ ifeq ($(KOKKOS_INTERNAL_USE_ARCH_AMD_GFX1100), 1) tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_AMD_GPU") KOKKOS_INTERNAL_HIP_ARCH_FLAG := --offload-arch=gfx1100 endif -ifeq ($(KOKKOS_INTERNAL_USE_ARCH_AMD_GFX1103), 1) - tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_AMD_GFX1103") - tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_AMD_GPU") - KOKKOS_INTERNAL_HIP_ARCH_FLAG := --offload-arch=gfx1103 -endif ifeq ($(KOKKOS_INTERNAL_USE_HIP), 1) @@ -1216,6 +1250,8 @@ ifeq ($(KOKKOS_INTERNAL_DISABLE_BUNDLED_MDSPAN), 0) endif tmp := $(call kokkos_append_header,"$H""define KOKKOS_ENABLE_IMPL_MDSPAN") +tmp := $(call kokkos_append_header,"$H""define KOKKOS_ENABLE_IMPL_REF_COUNT_BRANCH_UNLIKELY") + KOKKOS_INTERNAL_LS_CONFIG := $(shell ls KokkosCore_config.h 2>&1) ifeq ($(KOKKOS_INTERNAL_LS_CONFIG), KokkosCore_config.h) diff --git a/lib/kokkos/Makefile.targets b/lib/kokkos/Makefile.targets index e6900a822a..e8e429e027 100644 --- a/lib/kokkos/Makefile.targets +++ b/lib/kokkos/Makefile.targets @@ -81,7 +81,7 @@ ifeq ($(KOKKOS_INTERNAL_USE_THREADS), 1) Kokkos_Threads_Instance.o: $(KOKKOS_CPP_DEPENDS) $(KOKKOS_PATH)/core/src/Threads/Kokkos_Threads_Instance.cpp $(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) -c $(KOKKOS_PATH)/core/src/Threads/Kokkos_Threads_Instance.cpp Kokkos_Threads_Spinwait.o: $(KOKKOS_CPP_DEPENDS) $(KOKKOS_PATH)/core/src/Threads/Kokkos_Threads_Spinwait.cpp - $(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) -c $(KOKKOS_PATH)/core/src/Threads/Kokkos_Spinwait.cpp + $(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) -c $(KOKKOS_PATH)/core/src/Threads/Kokkos_Threads_Spinwait.cpp endif ifeq ($(KOKKOS_INTERNAL_USE_OPENMP), 1) diff --git a/lib/kokkos/README.md b/lib/kokkos/README.md index 19793bb82d..c8c6f8f7cf 100644 --- a/lib/kokkos/README.md +++ b/lib/kokkos/README.md @@ -1,4 +1,4 @@ -![Kokkos](https://avatars2.githubusercontent.com/u/10199860?s=200&v=4) +[![Kokkos](https://avatars2.githubusercontent.com/u/10199860?s=200&v=4)](https://kokkos.org) # Kokkos: Core Libraries @@ -10,43 +10,66 @@ hierarchies and multiple types of execution resources. It currently can use CUDA, HIP, SYCL, HPX, OpenMP and C++ threads as backend programming models with several other backends in development. -**Kokkos Core is part of the Kokkos C++ Performance Portability Programming EcoSystem.** +**Kokkos Core is part of the [Kokkos C++ Performance Portability Programming Ecosystem](https://kokkos.org/about/abstract/).** -For the complete documentation, click below: +Kokkos is a [Linux Foundation](https://linuxfoundation.org) project. -# [kokkos.github.io/kokkos-core-wiki](https://kokkos.github.io/kokkos-core-wiki) - -# Learning about Kokkos +## Learning about Kokkos To start learning about Kokkos: -- [Kokkos Lectures](https://kokkos.github.io/kokkos-core-wiki/videolectures.html): they contain a mix of lecture videos and hands-on exercises covering all the important Kokkos Ecosystem capabilities. +- [Kokkos Lectures](https://kokkos.org/kokkos-core-wiki/videolectures.html): they contain a mix of lecture videos and hands-on exercises covering all the important capabilities. -- [Programming guide](https://kokkos.github.io/kokkos-core-wiki/programmingguide.html): contains in "narrative" form a technical description of the programming model, machine model, and the main building blocks like the Views and parallel dispatch. +- [Programming guide](https://kokkos.org/kokkos-core-wiki/programmingguide.html): contains in "narrative" form a technical description of the programming model, machine model, and the main building blocks like the Views and parallel dispatch. -- [API reference](https://kokkos.github.io/kokkos-core-wiki/): organized by category, i.e., [core](https://kokkos.github.io/kokkos-core-wiki/API/core-index.html), [algorithms](https://kokkos.github.io/kokkos-core-wiki/API/algorithms-index.html) and [containers](https://kokkos.github.io/kokkos-core-wiki/API/containers-index.html) or, if you prefer, in [alphabetical order](https://kokkos.github.io/kokkos-core-wiki/API/alphabetical.html). +- [API reference](https://kokkos.org/kokkos-core-wiki/): organized by category, i.e., [core](https://kokkos.org/kokkos-core-wiki/API/core-index.html), [algorithms](https://kokkos.org/kokkos-core-wiki/API/algorithms-index.html) and [containers](https://kokkos.org/kokkos-core-wiki/API/containers-index.html) or, if you prefer, in [alphabetical order](https://kokkos.org/kokkos-core-wiki/API/alphabetical.html). -- [Use cases and Examples](https://kokkos.github.io/kokkos-core-wiki/usecases.html): a series of examples ranging from how to use Kokkos with MPI to Fortran interoperability. +- [Use cases and Examples](https://kokkos.org/kokkos-core-wiki/usecases.html): a serie of examples ranging from how to use Kokkos with MPI to Fortran interoperability. + +## Obtaining Kokkos + +The latest release of Kokkos can be obtained from the [GitHub releases page](https://github.com/kokkos/kokkos/releases/latest). + +The current release is [4.3.01](https://github.com/kokkos/kokkos/releases/tag/4.3.01). + +```bash +curl -OJ -L https://github.com/kokkos/kokkos/archive/refs/tags/4.3.01.tar.gz +# Or with wget +wget https://github.com/kokkos/kokkos/archive/refs/tags/4.3.01.tar.gz +``` + +To clone the latest development version of Kokkos from GitHub: + +```bash +git clone -b develop https://github.com/kokkos/kokkos.git +``` + +### Building Kokkos + +To build Kokkos, you will need to have a C++ compiler that supports C++17 or later. +All requirements including minimum and primary tested compiler versions can be found [here](https://kokkos.org/kokkos-core-wiki/requirements.html). + +Building and installation instructions are described [here](https://kokkos.org/kokkos-core-wiki/building.html). + +You can also install Kokkos using [Spack](https://spack.io/): `spack install kokkos`. [Available configuration options](https://packages.spack.io/package.html?name=kokkos) can be displayed using `spack info kokkos`. + +## For the complete documentation: [kokkos.org/kokkos-core-wiki/](https://kokkos.org/kokkos-core-wiki/) + +## Support For questions find us on Slack: https://kokkosteam.slack.com or open a GitHub issue. For non-public questions send an email to: *crtrott(at)sandia.gov* -# Contributing to Kokkos +## Contributing -Please see [this page](https://kokkos.github.io/kokkos-core-wiki/contributing.html) for details on how to contribute. +Please see [this page](https://kokkos.org/kokkos-core-wiki/contributing.html) for details on how to contribute. -# Requirements, Building and Installing +## Citing Kokkos -All requirements including minimum and primary tested compiler versions can be found [here](https://kokkos.github.io/kokkos-core-wiki/requirements.html). +Please see the [following page](https://kokkos.org/kokkos-core-wiki/citation.html). -Building and installation instructions are described [here](https://kokkos.github.io/kokkos-core-wiki/building.html). - -# Citing Kokkos - -Please see the [following page](https://kokkos.github.io/kokkos-core-wiki/citation.html). - -# License +## License [![License](https://img.shields.io/badge/License-Apache--2.0_WITH_LLVM--exception-blue)](https://spdx.org/licenses/LLVM-exception.html) diff --git a/lib/kokkos/algorithms/src/sorting/impl/Kokkos_SortByKeyImpl.hpp b/lib/kokkos/algorithms/src/sorting/impl/Kokkos_SortByKeyImpl.hpp index 36deccdfb1..f11f807048 100644 --- a/lib/kokkos/algorithms/src/sorting/impl/Kokkos_SortByKeyImpl.hpp +++ b/lib/kokkos/algorithms/src/sorting/impl/Kokkos_SortByKeyImpl.hpp @@ -189,6 +189,33 @@ void applyPermutation(const ExecutionSpace& space, KOKKOS_LAMBDA(int i) { view(i) = view_copy(permutation(i)); }); } +// FIXME_NVCC: nvcc has trouble compiling lambdas inside a function with +// variadic templates (sort_by_key_via_sort). Switch to using functors instead. +template +struct IotaFunctor { + Permute _permute; + KOKKOS_FUNCTION void operator()(int i) const { _permute(i) = i; } +}; +template +struct LessFunctor { + Keys _keys; + KOKKOS_FUNCTION bool operator()(int i, int j) const { + return _keys(i) < _keys(j); + } +}; + +// FIXME_NVCC+MSVC: We can't use a lambda instead of a functor which gave us +// "For this host platform/dialect, an extended lambda cannot be defined inside +// the 'if' or 'else' block of a constexpr if statement" +template +struct KeyComparisonFunctor { + Keys m_keys; + Comparator m_comparator; + KOKKOS_FUNCTION bool operator()(int i, int j) const { + return m_comparator(m_keys(i), m_keys(j)); + } +}; + template @@ -207,10 +234,9 @@ void sort_by_key_via_sort( n); // iota - Kokkos::parallel_for( - "Kokkos::sort_by_key_via_sort::iota", - Kokkos::RangePolicy(exec, 0, n), - KOKKOS_LAMBDA(int i) { permute(i) = i; }); + Kokkos::parallel_for("Kokkos::sort_by_key_via_sort::iota", + Kokkos::RangePolicy(exec, 0, n), + IotaFunctor{permute}); using Layout = typename Kokkos::View::array_layout; @@ -228,16 +254,15 @@ void sort_by_key_via_sort( Kokkos::DefaultHostExecutionSpace host_exec; if constexpr (sizeof...(MaybeComparator) == 0) { - Kokkos::sort( - host_exec, host_permute, - KOKKOS_LAMBDA(int i, int j) { return host_keys(i) < host_keys(j); }); + Kokkos::sort(host_exec, host_permute, + LessFunctor{host_keys}); } else { auto keys_comparator = std::get<0>(std::tuple(maybeComparator...)); Kokkos::sort( - host_exec, host_permute, KOKKOS_LAMBDA(int i, int j) { - return keys_comparator(host_keys(i), host_keys(j)); - }); + host_exec, host_permute, + KeyComparisonFunctor{ + host_keys, keys_comparator}); } host_exec.fence("Kokkos::Impl::sort_by_key_via_sort: after host sort"); Kokkos::deep_copy(exec, permute, host_permute); @@ -262,16 +287,14 @@ void sort_by_key_via_sort( } #else if constexpr (sizeof...(MaybeComparator) == 0) { - Kokkos::sort( - exec, permute, - KOKKOS_LAMBDA(int i, int j) { return keys(i) < keys(j); }); + Kokkos::sort(exec, permute, LessFunctor{keys}); } else { auto keys_comparator = std::get<0>(std::tuple(maybeComparator...)); Kokkos::sort( - exec, permute, KOKKOS_LAMBDA(int i, int j) { - return keys_comparator(keys(i), keys(j)); - }); + exec, permute, + KeyComparisonFunctor{ + keys, keys_comparator}); } #endif } diff --git a/lib/kokkos/algorithms/src/std_algorithms/Kokkos_ForEach.hpp b/lib/kokkos/algorithms/src/std_algorithms/Kokkos_ForEach.hpp index 6215b325af..05969be463 100644 --- a/lib/kokkos/algorithms/src/std_algorithms/Kokkos_ForEach.hpp +++ b/lib/kokkos/algorithms/src/std_algorithms/Kokkos_ForEach.hpp @@ -29,49 +29,46 @@ namespace Experimental { template < class ExecutionSpace, class IteratorType, class UnaryFunctorType, std::enable_if_t, int> = 0> -UnaryFunctorType for_each(const std::string& label, const ExecutionSpace& ex, - IteratorType first, IteratorType last, - UnaryFunctorType functor) { - return Impl::for_each_exespace_impl(label, ex, first, last, - std::move(functor)); +void for_each(const std::string& label, const ExecutionSpace& ex, + IteratorType first, IteratorType last, UnaryFunctorType functor) { + Impl::for_each_exespace_impl(label, ex, first, last, std::move(functor)); } template < class ExecutionSpace, class IteratorType, class UnaryFunctorType, std::enable_if_t, int> = 0> -UnaryFunctorType for_each(const ExecutionSpace& ex, IteratorType first, - IteratorType last, UnaryFunctorType functor) { - return Impl::for_each_exespace_impl("Kokkos::for_each_iterator_api_default", - ex, first, last, std::move(functor)); +void for_each(const ExecutionSpace& ex, IteratorType first, IteratorType last, + UnaryFunctorType functor) { + Impl::for_each_exespace_impl("Kokkos::for_each_iterator_api_default", ex, + first, last, std::move(functor)); } template < class ExecutionSpace, class DataType, class... Properties, class UnaryFunctorType, std::enable_if_t, int> = 0> -UnaryFunctorType for_each(const std::string& label, const ExecutionSpace& ex, - const ::Kokkos::View& v, - UnaryFunctorType functor) { +void for_each(const std::string& label, const ExecutionSpace& ex, + const ::Kokkos::View& v, + UnaryFunctorType functor) { Impl::static_assert_is_admissible_to_kokkos_std_algorithms(v); namespace KE = ::Kokkos::Experimental; - return Impl::for_each_exespace_impl(label, ex, KE::begin(v), KE::end(v), - std::move(functor)); + Impl::for_each_exespace_impl(label, ex, KE::begin(v), KE::end(v), + std::move(functor)); } template < class ExecutionSpace, class DataType, class... Properties, class UnaryFunctorType, std::enable_if_t, int> = 0> -UnaryFunctorType for_each(const ExecutionSpace& ex, - const ::Kokkos::View& v, - UnaryFunctorType functor) { +void for_each(const ExecutionSpace& ex, + const ::Kokkos::View& v, + UnaryFunctorType functor) { Impl::static_assert_is_admissible_to_kokkos_std_algorithms(v); namespace KE = ::Kokkos::Experimental; - return Impl::for_each_exespace_impl("Kokkos::for_each_view_api_default", ex, - KE::begin(v), KE::end(v), - std::move(functor)); + Impl::for_each_exespace_impl("Kokkos::for_each_view_api_default", ex, + KE::begin(v), KE::end(v), std::move(functor)); } // @@ -82,24 +79,23 @@ UnaryFunctorType for_each(const ExecutionSpace& ex, template , int> = 0> -KOKKOS_FUNCTION UnaryFunctorType for_each(const TeamHandleType& teamHandle, - IteratorType first, IteratorType last, - UnaryFunctorType functor) { - return Impl::for_each_team_impl(teamHandle, first, last, std::move(functor)); +KOKKOS_FUNCTION void for_each(const TeamHandleType& teamHandle, + IteratorType first, IteratorType last, + UnaryFunctorType functor) { + Impl::for_each_team_impl(teamHandle, first, last, std::move(functor)); } template , int> = 0> -KOKKOS_FUNCTION UnaryFunctorType -for_each(const TeamHandleType& teamHandle, - const ::Kokkos::View& v, - UnaryFunctorType functor) { +KOKKOS_FUNCTION void for_each(const TeamHandleType& teamHandle, + const ::Kokkos::View& v, + UnaryFunctorType functor) { Impl::static_assert_is_admissible_to_kokkos_std_algorithms(v); namespace KE = ::Kokkos::Experimental; - return Impl::for_each_team_impl(teamHandle, KE::begin(v), KE::end(v), - std::move(functor)); + Impl::for_each_team_impl(teamHandle, KE::begin(v), KE::end(v), + std::move(functor)); } } // namespace Experimental diff --git a/lib/kokkos/algorithms/src/std_algorithms/impl/Kokkos_AdjacentDifference.hpp b/lib/kokkos/algorithms/src/std_algorithms/impl/Kokkos_AdjacentDifference.hpp index a8171fa068..9f7fcf94fe 100644 --- a/lib/kokkos/algorithms/src/std_algorithms/impl/Kokkos_AdjacentDifference.hpp +++ b/lib/kokkos/algorithms/src/std_algorithms/impl/Kokkos_AdjacentDifference.hpp @@ -82,6 +82,11 @@ OutputIteratorType adjacent_difference_exespace_impl( return first_dest; } +#ifdef KOKKOS_ENABLE_DEBUG + // check for overlapping iterators + Impl::expect_no_overlap(first_from, last_from, first_dest); +#endif + // run const auto num_elements = Kokkos::Experimental::distance(first_from, last_from); @@ -114,6 +119,11 @@ KOKKOS_FUNCTION OutputIteratorType adjacent_difference_team_impl( return first_dest; } +#ifdef KOKKOS_ENABLE_DEBUG + // check for overlapping iterators + Impl::expect_no_overlap(first_from, last_from, first_dest); +#endif + // run const auto num_elements = Kokkos::Experimental::distance(first_from, last_from); diff --git a/lib/kokkos/algorithms/src/std_algorithms/impl/Kokkos_Constraints.hpp b/lib/kokkos/algorithms/src/std_algorithms/impl/Kokkos_Constraints.hpp index 27ce5a6fad..54bb13e25b 100644 --- a/lib/kokkos/algorithms/src/std_algorithms/impl/Kokkos_Constraints.hpp +++ b/lib/kokkos/algorithms/src/std_algorithms/impl/Kokkos_Constraints.hpp @@ -24,18 +24,21 @@ namespace Kokkos { namespace Experimental { namespace Impl { +template +class RandomAccessIterator; + template struct is_admissible_to_kokkos_std_algorithms : std::false_type {}; template struct is_admissible_to_kokkos_std_algorithms< - T, std::enable_if_t< ::Kokkos::is_view::value && T::rank() == 1 && - (std::is_same::value || - std::is_same::value || - std::is_same::value)> > + T, std::enable_if_t<::Kokkos::is_view::value && T::rank() == 1 && + (std::is_same::value || + std::is_same::value || + std::is_same::value)>> : std::true_type {}; template @@ -58,6 +61,18 @@ using is_iterator = Kokkos::is_detected; template inline constexpr bool is_iterator_v = is_iterator::value; +template +struct is_kokkos_iterator : std::false_type {}; + +template +struct is_kokkos_iterator> { + static constexpr bool value = + is_admissible_to_kokkos_std_algorithms::value; +}; + +template +inline constexpr bool is_kokkos_iterator_v = is_kokkos_iterator::value; + // // are_iterators // @@ -215,6 +230,38 @@ KOKKOS_INLINE_FUNCTION void expect_valid_range(IteratorType first, (void)last; } +// +// Check if kokkos iterators are overlapping +// +template +KOKKOS_INLINE_FUNCTION void expect_no_overlap( + [[maybe_unused]] IteratorType1 first, [[maybe_unused]] IteratorType1 last, + [[maybe_unused]] IteratorType2 s_first) { + if constexpr (is_kokkos_iterator_v && + is_kokkos_iterator_v) { + auto const view1 = first.view(); + auto const view2 = s_first.view(); + + std::size_t stride1 = view1.stride(0); + std::size_t stride2 = view2.stride(0); + ptrdiff_t first_diff = view1.data() - view2.data(); + + // FIXME If strides are not identical, checks may not be made + // with the cost of O(1) + // Currently, checks are made only if strides are identical + // If first_diff == 0, there is already an overlap + if (stride1 == stride2 || first_diff == 0) { + [[maybe_unused]] bool is_no_overlap = (first_diff % stride1); + auto* first_pointer1 = view1.data(); + auto* first_pointer2 = view2.data(); + [[maybe_unused]] auto* last_pointer1 = first_pointer1 + (last - first); + [[maybe_unused]] auto* last_pointer2 = first_pointer2 + (last - first); + KOKKOS_EXPECTS(first_pointer1 >= last_pointer2 || + last_pointer1 <= first_pointer2 || is_no_overlap); + } + } +} + } // namespace Impl } // namespace Experimental } // namespace Kokkos diff --git a/lib/kokkos/algorithms/src/std_algorithms/impl/Kokkos_CopyIf.hpp b/lib/kokkos/algorithms/src/std_algorithms/impl/Kokkos_CopyIf.hpp index 3c1e2474bc..ad7b8bb8ca 100644 --- a/lib/kokkos/algorithms/src/std_algorithms/impl/Kokkos_CopyIf.hpp +++ b/lib/kokkos/algorithms/src/std_algorithms/impl/Kokkos_CopyIf.hpp @@ -150,8 +150,9 @@ KOKKOS_FUNCTION OutputIterator copy_if_team_impl( return d_first + count; } -#if defined KOKKOS_COMPILER_INTEL || \ - (defined(KOKKOS_COMPILER_NVCC) && KOKKOS_COMPILER_NVCC >= 1130) +#if defined KOKKOS_COMPILER_INTEL || \ + (defined(KOKKOS_COMPILER_NVCC) && KOKKOS_COMPILER_NVCC >= 1130 && \ + !defined(KOKKOS_COMPILER_MSVC)) __builtin_unreachable(); #endif } diff --git a/lib/kokkos/algorithms/src/std_algorithms/impl/Kokkos_ForEachForEachN.hpp b/lib/kokkos/algorithms/src/std_algorithms/impl/Kokkos_ForEachForEachN.hpp index d3be3b7f66..99cc4a1cf3 100644 --- a/lib/kokkos/algorithms/src/std_algorithms/impl/Kokkos_ForEachForEachN.hpp +++ b/lib/kokkos/algorithms/src/std_algorithms/impl/Kokkos_ForEachForEachN.hpp @@ -42,10 +42,9 @@ struct StdForEachFunctor { }; template -UnaryFunctorType for_each_exespace_impl(const std::string& label, - const HandleType& handle, - IteratorType first, IteratorType last, - UnaryFunctorType functor) { +void for_each_exespace_impl(const std::string& label, const HandleType& handle, + IteratorType first, IteratorType last, + UnaryFunctorType functor) { // checks Impl::static_assert_random_access_and_accessible(handle, first); Impl::expect_valid_range(first, last); @@ -56,8 +55,6 @@ UnaryFunctorType for_each_exespace_impl(const std::string& label, label, RangePolicy(handle, 0, num_elements), StdForEachFunctor(first, functor)); handle.fence("Kokkos::for_each: fence after operation"); - - return functor; } template -KOKKOS_FUNCTION UnaryFunctorType -for_each_team_impl(const TeamHandleType& teamHandle, IteratorType first, - IteratorType last, UnaryFunctorType functor) { +KOKKOS_FUNCTION void for_each_team_impl(const TeamHandleType& teamHandle, + IteratorType first, IteratorType last, + UnaryFunctorType functor) { // checks Impl::static_assert_random_access_and_accessible(teamHandle, first); Impl::expect_valid_range(first, last); @@ -96,7 +93,6 @@ for_each_team_impl(const TeamHandleType& teamHandle, IteratorType first, TeamThreadRange(teamHandle, 0, num_elements), StdForEachFunctor(first, functor)); teamHandle.team_barrier(); - return functor; } template > { ptrdiff_t current_index) : m_view(view), m_current_index(current_index) {} +#ifndef KOKKOS_ENABLE_CXX17 // C++20 and beyond + template + requires(std::is_constructible_v) KOKKOS_FUNCTION + explicit(!std::is_convertible_v) + RandomAccessIterator(const RandomAccessIterator& other) + : m_view(other.m_view), m_current_index(other.m_current_index) {} +#else + template < + class OtherViewType, + std::enable_if_t && + !std::is_convertible_v, + int> = 0> + KOKKOS_FUNCTION explicit RandomAccessIterator( + const RandomAccessIterator& other) + : m_view(other.m_view), m_current_index(other.m_current_index) {} + + template , + int> = 0> + KOKKOS_FUNCTION RandomAccessIterator( + const RandomAccessIterator& other) + : m_view(other.m_view), m_current_index(other.m_current_index) {} +#endif + KOKKOS_FUNCTION iterator_type& operator++() { ++m_current_index; @@ -152,9 +176,16 @@ class RandomAccessIterator< ::Kokkos::View > { KOKKOS_FUNCTION reference operator*() const { return m_view(m_current_index); } + KOKKOS_FUNCTION + view_type view() const { return m_view; } + private: view_type m_view; ptrdiff_t m_current_index = 0; + + // Needed for the converting constructor accepting another iterator + template + friend class RandomAccessIterator; }; } // namespace Impl diff --git a/lib/kokkos/algorithms/src/std_algorithms/impl/Kokkos_UniqueCopy.hpp b/lib/kokkos/algorithms/src/std_algorithms/impl/Kokkos_UniqueCopy.hpp index c7c2930278..710d04805d 100644 --- a/lib/kokkos/algorithms/src/std_algorithms/impl/Kokkos_UniqueCopy.hpp +++ b/lib/kokkos/algorithms/src/std_algorithms/impl/Kokkos_UniqueCopy.hpp @@ -175,8 +175,9 @@ KOKKOS_FUNCTION OutputIterator unique_copy_team_impl( d_first + count); } -#if defined KOKKOS_COMPILER_INTEL || \ - (defined(KOKKOS_COMPILER_NVCC) && KOKKOS_COMPILER_NVCC >= 1130) +#if defined KOKKOS_COMPILER_INTEL || \ + (defined(KOKKOS_COMPILER_NVCC) && KOKKOS_COMPILER_NVCC >= 1130 && \ + !defined(KOKKOS_COMPILER_MSVC)) __builtin_unreachable(); #endif } diff --git a/lib/kokkos/algorithms/unit_tests/TestRandomAccessIterator.cpp b/lib/kokkos/algorithms/unit_tests/TestRandomAccessIterator.cpp index 282d85548c..7d484136b6 100644 --- a/lib/kokkos/algorithms/unit_tests/TestRandomAccessIterator.cpp +++ b/lib/kokkos/algorithms/unit_tests/TestRandomAccessIterator.cpp @@ -46,6 +46,44 @@ TEST_F(random_access_iterator_test, constructor) { EXPECT_TRUE(true); } +TEST_F(random_access_iterator_test, constructiblity) { + auto first_d = KE::begin(m_dynamic_view); + auto cfirst_d = KE::cbegin(m_dynamic_view); + + static_assert(std::is_constructible_v); + static_assert( + !std::is_constructible_v); + [[maybe_unused]] decltype(cfirst_d) tmp_cfirst_d(first_d); + + auto first_s = KE::begin(m_static_view); + auto cfirst_s = KE::cbegin(m_static_view); + + static_assert(std::is_constructible_v); + static_assert( + !std::is_constructible_v); + [[maybe_unused]] decltype(cfirst_s) tmp_cfirst_s(first_s); + + auto first_st = KE::begin(m_strided_view); + auto cfirst_st = KE::cbegin(m_strided_view); + + static_assert( + std::is_constructible_v); + static_assert( + !std::is_constructible_v); + [[maybe_unused]] decltype(cfirst_st) tmp_cfirst_st(first_st); + + // [FIXME] Better to have tests for the explicit specifier with an expression. + // As soon as View converting constructors are re-implemented with a + // conditional explicit, we may add those tests. + static_assert(std::is_constructible_v); + static_assert(std::is_constructible_v); + static_assert(std::is_constructible_v); + static_assert(std::is_constructible_v); + static_assert(std::is_constructible_v); + static_assert(std::is_constructible_v); + EXPECT_TRUE(true); +} + template void test_random_access_it_verify(IteratorType it, ValueType gold_value) { using view_t = Kokkos::View; diff --git a/lib/kokkos/algorithms/unit_tests/TestSortByKey.hpp b/lib/kokkos/algorithms/unit_tests/TestSortByKey.hpp index 16f68eaaf2..9e5bd4a574 100644 --- a/lib/kokkos/algorithms/unit_tests/TestSortByKey.hpp +++ b/lib/kokkos/algorithms/unit_tests/TestSortByKey.hpp @@ -69,7 +69,7 @@ void iota(ExecutionSpace const &space, ViewType const &v, typename ViewType::value_type value = 0) { using ValueType = typename ViewType::value_type; Kokkos::parallel_for( - "ArborX::Algorithms::iota", + "Kokkos::Algorithms::iota", Kokkos::RangePolicy(space, 0, v.extent(0)), KOKKOS_LAMBDA(int i) { v(i) = value + (ValueType)i; }); } @@ -87,6 +87,18 @@ TEST(TEST_CATEGORY, SortByKeyEmptyView) { Kokkos::Experimental::sort_by_key(ExecutionSpace(), keys, values)); } +// Test #7036 +TEST(TEST_CATEGORY, SortByKeyEmptyViewHost) { + using ExecutionSpace = Kokkos::DefaultHostExecutionSpace; + + // does not matter if we use int or something else + Kokkos::View keys("keys", 0); + Kokkos::View values("values", 0); + + ASSERT_NO_THROW( + Kokkos::Experimental::sort_by_key(ExecutionSpace(), keys, values)); +} + TEST(TEST_CATEGORY, SortByKey) { using ExecutionSpace = TEST_EXECSPACE; using MemorySpace = typename ExecutionSpace::memory_space; diff --git a/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsConstraints.cpp b/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsConstraints.cpp index 386d533f7a..2a4525a8c3 100644 --- a/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsConstraints.cpp +++ b/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsConstraints.cpp @@ -81,5 +81,114 @@ TEST(std_algorithms, is_admissible_to_std_algorithms) { strided_view_3d_t>::value); } +TEST(std_algorithms, expect_no_overlap) { + namespace KE = Kokkos::Experimental; + using value_type = double; + + static constexpr size_t extent0 = 13; + + //------------- + // 1d views + //------------- + using static_view_1d_t = Kokkos::View; + [[maybe_unused]] static_view_1d_t static_view_1d{ + "std-algo-test-1d-contiguous-view-static"}; + + using dyn_view_1d_t = Kokkos::View; + [[maybe_unused]] dyn_view_1d_t dynamic_view_1d{ + "std-algo-test-1d-contiguous-view-dynamic", extent0}; + + using strided_view_1d_t = Kokkos::View; + Kokkos::LayoutStride layout1d{extent0, 2}; + strided_view_1d_t strided_view_1d{"std-algo-test-1d-strided-view", layout1d}; + +// Overlapping because iterators are identical +#if defined(KOKKOS_ENABLE_DEBUG) + auto first_s = KE::begin(static_view_1d); + auto last_s = first_s + extent0; + EXPECT_DEATH({ KE::Impl::expect_no_overlap(first_s, last_s, first_s); }, + "Kokkos contract violation:.*"); + + auto first_d = KE::begin(dynamic_view_1d); + auto last_d = first_d + extent0; + EXPECT_DEATH({ KE::Impl::expect_no_overlap(first_d, last_d, first_d); }, + "Kokkos contract violation:.*"); + + auto first_st = KE::begin(strided_view_1d); + auto last_st = first_st + extent0; + EXPECT_DEATH({ KE::Impl::expect_no_overlap(first_st, last_st, first_st); }, + "Kokkos contract violation:.*"); +#endif + + // Ranges are overlapped + static constexpr size_t sub_extent0 = 6, offset0 = 3; + std::pair range0(0, sub_extent0), + range1(offset0, offset0 + sub_extent0); +#if defined(KOKKOS_ENABLE_DEBUG) + auto static_view_1d_0 = Kokkos::subview(static_view_1d, range0); + auto static_view_1d_1 = Kokkos::subview(static_view_1d, range1); + auto first_s0 = KE::begin(static_view_1d_0); // [0, 6) + auto last_s0 = first_s0 + static_view_1d_0.extent(0); + auto first_s1 = KE::begin(static_view_1d_1); // [3, 9) + EXPECT_DEATH({ KE::Impl::expect_no_overlap(first_s0, last_s0, first_s1); }, + "Kokkos contract violation:.*"); + + auto dynamic_view_1d_0 = Kokkos::subview(dynamic_view_1d, range0); + auto dynamic_view_1d_1 = Kokkos::subview(dynamic_view_1d, range1); + auto first_d0 = KE::begin(dynamic_view_1d_0); // [0, 6) + auto last_d0 = first_d0 + dynamic_view_1d_0.extent(0); + auto first_d1 = KE::begin(dynamic_view_1d_1); // [3, 9) + EXPECT_DEATH({ KE::Impl::expect_no_overlap(first_d0, last_d0, first_d1); }, + "Kokkos contract violation:.*"); +#endif + + auto strided_view_1d_0 = Kokkos::subview(strided_view_1d, range0); + auto strided_view_1d_1 = Kokkos::subview(strided_view_1d, range1); + auto first_st0 = KE::begin(strided_view_1d_0); // [0, 12) + auto last_st0 = first_st0 + strided_view_1d_0.extent(0); + auto first_st1 = KE::begin(strided_view_1d_1); // [3, 15) + // Does not overlap since offset (=3) is not divisible by stride (=2) + EXPECT_NO_THROW( + { KE::Impl::expect_no_overlap(first_st0, last_st0, first_st1); }); + + // Iterating over the same range without overlapping + Kokkos::View static_view_2d{ + "std-algo-test-2d-contiguous-view-static"}; + auto sub_static_view_1d_0 = Kokkos::subview(static_view_2d, 0, Kokkos::ALL); + auto sub_static_view_1d_1 = Kokkos::subview(static_view_2d, 1, Kokkos::ALL); + auto sub_first_s0 = KE::begin(sub_static_view_1d_0); // 0, 2, 4, ... + auto sub_last_s0 = sub_first_s0 + sub_static_view_1d_0.extent(0); + auto sub_first_s1 = KE::begin(sub_static_view_1d_1); // 1, 3, 5, ... + + EXPECT_NO_THROW({ + KE::Impl::expect_no_overlap(sub_first_s0, sub_last_s0, sub_first_s1); + }); + + Kokkos::View dynamic_view_2d{ + "std-algo-test-2d-contiguous-view-dynamic", 2, extent0}; + auto sub_dynamic_view_1d_0 = Kokkos::subview(dynamic_view_2d, 0, Kokkos::ALL); + auto sub_dynamic_view_1d_1 = Kokkos::subview(dynamic_view_2d, 1, Kokkos::ALL); + auto sub_first_d0 = KE::begin(sub_dynamic_view_1d_0); // 0, 2, 4, ... + auto sub_last_d0 = sub_first_d0 + sub_dynamic_view_1d_0.extent(0); + auto sub_first_d1 = KE::begin(sub_dynamic_view_1d_1); // 1, 3, 5, ... + + EXPECT_NO_THROW({ + KE::Impl::expect_no_overlap(sub_first_d0, sub_last_d0, sub_first_d1); + }); + + Kokkos::LayoutStride layout2d{2, 3, extent0, 2 * 3}; + Kokkos::View strided_view_2d{ + "std-algo-test-2d-contiguous-view-strided", layout2d}; + auto sub_strided_view_1d_0 = Kokkos::subview(strided_view_2d, 0, Kokkos::ALL); + auto sub_strided_view_1d_1 = Kokkos::subview(strided_view_2d, 1, Kokkos::ALL); + auto sub_first_st0 = KE::begin(sub_strided_view_1d_0); // 0, 6, 12, ... + auto sub_last_st0 = sub_first_st0 + sub_strided_view_1d_0.extent(0); + auto sub_first_st1 = KE::begin(sub_strided_view_1d_1); // 1, 7, 13, ... + + EXPECT_NO_THROW({ + KE::Impl::expect_no_overlap(sub_first_st0, sub_last_st0, sub_first_st1); + }); +} + } // namespace stdalgos } // namespace Test diff --git a/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamExclusiveScan.cpp b/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamExclusiveScan.cpp index 2c8fee02f4..7cb9851087 100644 --- a/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamExclusiveScan.cpp +++ b/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamExclusiveScan.cpp @@ -85,7 +85,7 @@ struct TestFunctorA { break; } -#if not defined KOKKOS_ENABLE_OPENMPTARGET +#ifndef KOKKOS_ENABLE_OPENMPTARGET case 2: { auto it = KE::exclusive_scan( @@ -213,7 +213,7 @@ void test_A(std::size_t numTeams, std::size_t numCols, int apiId) { break; } -#if not defined KOKKOS_ENABLE_OPENMPTARGET +#ifndef KOKKOS_ENABLE_OPENMPTARGET case 2: case 3: { auto it = exclusive_scan(KE::cbegin(rowFrom), KE::cend(rowFrom), @@ -242,7 +242,7 @@ template void run_all_scenarios() { for (int numTeams : teamSizesToTest) { for (const auto& numCols : {0, 1, 2, 13, 101, 1444, 8153}) { -#if not defined KOKKOS_ENABLE_OPENMPTARGET +#ifndef KOKKOS_ENABLE_OPENMPTARGET for (int apiId : {0, 1, 2, 3}) { #else for (int apiId : {0, 1}) { diff --git a/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamIsSorted.cpp b/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamIsSorted.cpp index f9adeb0654..850e80dde1 100644 --- a/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamIsSorted.cpp +++ b/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamIsSorted.cpp @@ -52,7 +52,7 @@ struct TestFunctorA { Kokkos::single(Kokkos::PerTeam(member), [=, *this]() { m_returnsView(myRowIndex) = result; }); } -#if not defined KOKKOS_ENABLE_OPENMPTARGET +#ifndef KOKKOS_ENABLE_OPENMPTARGET else if (m_apiPick == 2) { using value_type = typename ViewType::value_type; result = KE::is_sorted(member, KE::cbegin(myRowView), KE::cend(myRowView), @@ -179,7 +179,7 @@ template void run_all_scenarios(bool makeDataSortedOnPurpose) { for (int numTeams : teamSizesToTest) { for (const auto& numCols : {0, 1, 2, 13, 101, 1444, 5153}) { -#if not defined KOKKOS_ENABLE_OPENMPTARGET +#ifndef KOKKOS_ENABLE_OPENMPTARGET for (int apiId : {0, 1, 2, 3}) { #else for (int apiId : {0, 1}) { diff --git a/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamIsSortedUntil.cpp b/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamIsSortedUntil.cpp index 33af5f99de..e3b95527c7 100644 --- a/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamIsSortedUntil.cpp +++ b/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamIsSortedUntil.cpp @@ -73,7 +73,7 @@ struct TestFunctorA { m_distancesView(myRowIndex) = resultDist; }); } -#if not defined KOKKOS_ENABLE_OPENMPTARGET +#ifndef KOKKOS_ENABLE_OPENMPTARGET else if (m_apiPick == 2) { using value_type = typename ViewType::value_type; auto it = KE::is_sorted_until(member, KE::cbegin(myRowView), @@ -226,7 +226,7 @@ template void run_all_scenarios(const std::string& name, const std::vector& cols) { for (int numTeams : teamSizesToTest) { for (const auto& numCols : cols) { -#if not defined KOKKOS_ENABLE_OPENMPTARGET +#ifndef KOKKOS_ENABLE_OPENMPTARGET for (int apiId : {0, 1, 2, 3}) { #else for (int apiId : {0, 1}) { diff --git a/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamMaxElement.cpp b/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamMaxElement.cpp index fb891a8780..283525dbd1 100644 --- a/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamMaxElement.cpp +++ b/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamMaxElement.cpp @@ -59,7 +59,7 @@ struct TestFunctorA { m_distancesView(myRowIndex) = resultDist; }); } -#if not defined KOKKOS_ENABLE_OPENMPTARGET +#ifndef KOKKOS_ENABLE_OPENMPTARGET else if (m_apiPick == 2) { using value_type = typename ViewType::value_type; auto it = @@ -170,7 +170,7 @@ void run_all_scenarios() { } TEST(std_algorithms_max_element_team_test, test) { -#if not defined KOKKOS_ENABLE_OPENMPTARGET +#ifndef KOKKOS_ENABLE_OPENMPTARGET run_all_scenarios(); run_all_scenarios(); run_all_scenarios(); diff --git a/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamMinElement.cpp b/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamMinElement.cpp index 4ba1b6f968..8579b48315 100644 --- a/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamMinElement.cpp +++ b/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamMinElement.cpp @@ -59,7 +59,7 @@ struct TestFunctorA { m_distancesView(myRowIndex) = resultDist; }); } -#if not defined KOKKOS_ENABLE_OPENMPTARGET +#ifndef KOKKOS_ENABLE_OPENMPTARGET else if (m_apiPick == 2) { using value_type = typename ViewType::value_type; auto it = @@ -169,7 +169,7 @@ void run_all_scenarios() { } TEST(std_algorithms_min_element_team_test, test) { -#if not defined KOKKOS_ENABLE_OPENMPTARGET +#ifndef KOKKOS_ENABLE_OPENMPTARGET run_all_scenarios(); run_all_scenarios(); run_all_scenarios(); diff --git a/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamMinMaxElement.cpp b/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamMinMaxElement.cpp index 17562a5572..51010fdff5 100644 --- a/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamMinMaxElement.cpp +++ b/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamMinMaxElement.cpp @@ -66,7 +66,7 @@ struct TestFunctorA { m_distancesView(myRowIndex, 1) = resultDist2; }); } -#if not defined KOKKOS_ENABLE_OPENMPTARGET +#ifndef KOKKOS_ENABLE_OPENMPTARGET else if (m_apiPick == 2) { using value_type = typename ViewType::value_type; auto itPair = @@ -188,7 +188,7 @@ void run_all_scenarios() { } TEST(std_algorithms_minmax_element_team_test, test) { -#if not defined KOKKOS_ENABLE_OPENMPTARGET +#ifndef KOKKOS_ENABLE_OPENMPTARGET run_all_scenarios(); run_all_scenarios(); run_all_scenarios(); diff --git a/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamReduce.cpp b/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamReduce.cpp index 94c2a8f1f9..eb00d9e083 100644 --- a/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamReduce.cpp +++ b/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamReduce.cpp @@ -16,7 +16,7 @@ #include -#if not defined KOKKOS_ENABLE_OPENMPTARGET +#ifndef KOKKOS_ENABLE_OPENMPTARGET namespace Test { namespace stdalgos { diff --git a/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamTransformExclusiveScan.cpp b/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamTransformExclusiveScan.cpp index 60fa369af1..1c43854381 100644 --- a/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamTransformExclusiveScan.cpp +++ b/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamTransformExclusiveScan.cpp @@ -16,7 +16,7 @@ #include -#if not defined KOKKOS_ENABLE_OPENMPTARGET +#ifndef KOKKOS_ENABLE_OPENMPTARGET namespace Test { namespace stdalgos { diff --git a/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamTransformInclusiveScan.cpp b/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamTransformInclusiveScan.cpp index 10454d6551..0b0d798fd8 100644 --- a/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamTransformInclusiveScan.cpp +++ b/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamTransformInclusiveScan.cpp @@ -16,7 +16,7 @@ #include -#if not defined KOKKOS_ENABLE_OPENMPTARGET +#ifndef KOKKOS_ENABLE_OPENMPTARGET namespace Test { namespace stdalgos { diff --git a/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamTransformReduce.cpp b/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamTransformReduce.cpp index b0a3241ec4..17ded226aa 100644 --- a/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamTransformReduce.cpp +++ b/lib/kokkos/algorithms/unit_tests/TestStdAlgorithmsTeamTransformReduce.cpp @@ -16,7 +16,7 @@ #include -#if not defined KOKKOS_ENABLE_OPENMPTARGET +#ifndef KOKKOS_ENABLE_OPENMPTARGET namespace Test { namespace stdalgos { diff --git a/lib/kokkos/appveyor.yml b/lib/kokkos/appveyor.yml index c0b6e9cab9..d0a5645ef7 100644 --- a/lib/kokkos/appveyor.yml +++ b/lib/kokkos/appveyor.yml @@ -5,6 +5,6 @@ build_script: - cmd: >- mkdir build && cd build && - cmake c:\projects\source -DKokkos_ENABLE_TESTS=ON -DCMAKE_CXX_FLAGS="/W0 /EHsc" -DKokkos_ENABLE_DEPRECATED_CODE_4=ON -DKokkos_ENABLE_DEPRECATION_WARNINGS=OFF && + cmake c:\projects\source -DKokkos_ENABLE_IMPL_MDSPAN=OFF -DKokkos_ENABLE_TESTS=ON -DCMAKE_CXX_FLAGS="/W0 /EHsc" -DKokkos_ENABLE_DEPRECATED_CODE_4=ON -DKokkos_ENABLE_DEPRECATION_WARNINGS=OFF && cmake --build . --target install && ctest -C Debug --output-on-failure diff --git a/lib/kokkos/benchmarks/CMakeLists.txt b/lib/kokkos/benchmarks/CMakeLists.txt index abf5028359..529ef393d9 100644 --- a/lib/kokkos/benchmarks/CMakeLists.txt +++ b/lib/kokkos/benchmarks/CMakeLists.txt @@ -4,7 +4,7 @@ KOKKOS_ADD_BENCHMARK_DIRECTORIES(gather) KOKKOS_ADD_BENCHMARK_DIRECTORIES(gups) KOKKOS_ADD_BENCHMARK_DIRECTORIES(launch_latency) KOKKOS_ADD_BENCHMARK_DIRECTORIES(stream) - +KOKKOS_ADD_BENCHMARK_DIRECTORIES(view_copy_constructor) #FIXME_OPENMPTARGET - These two benchmarks cause ICE. Commenting them for now but a deeper analysis on the cause and a possible fix will follow. IF(NOT Kokkos_ENABLE_OPENMPTARGET) KOKKOS_ADD_BENCHMARK_DIRECTORIES(policy_performance) diff --git a/lib/kokkos/benchmarks/view_copy_constructor/CMakeLists.txt b/lib/kokkos/benchmarks/view_copy_constructor/CMakeLists.txt new file mode 100644 index 0000000000..50a331b2b3 --- /dev/null +++ b/lib/kokkos/benchmarks/view_copy_constructor/CMakeLists.txt @@ -0,0 +1,4 @@ +KOKKOS_ADD_EXECUTABLE( + view_copy_constructor + SOURCES view_copy_constructor.cpp +) diff --git a/lib/kokkos/benchmarks/view_copy_constructor/Makefile b/lib/kokkos/benchmarks/view_copy_constructor/Makefile new file mode 100644 index 0000000000..70c6d517e0 --- /dev/null +++ b/lib/kokkos/benchmarks/view_copy_constructor/Makefile @@ -0,0 +1,46 @@ +KOKKOS_DEVICES=Serial +KOKKOS_ARCH = "" + + +MAKEFILE_PATH := $(subst Makefile,,$(abspath $(lastword $(MAKEFILE_LIST)))) + +ifndef KOKKOS_PATH + KOKKOS_PATH = $(MAKEFILE_PATH)../.. +endif + +SRC = $(wildcard $(MAKEFILE_PATH)*.cpp) +HEADERS = $(wildcard $(MAKEFILE_PATH)*.hpp) + +vpath %.cpp $(sort $(dir $(SRC))) + +default: build + echo "Start Build" + +CXX = clang++ +EXE = view_copy_constructor.exe + +CXXFLAGS ?= -Ofast +override CXXFLAGS += -I$(MAKEFILE_PATH) + +DEPFLAGS = -M +LINK = ${CXX} +LINKFLAGS = -Ofast +KOKKOS_CXX_STANDARD=c++20 + +OBJ = $(notdir $(SRC:.cpp=.o)) +LIB = + +include $(KOKKOS_PATH)/Makefile.kokkos + +build: $(EXE) + +$(EXE): $(OBJ) $(KOKKOS_LINK_DEPENDS) + $(LINK) $(KOKKOS_LDFLAGS) $(LINKFLAGS) $(EXTRA_PATH) $(OBJ) $(KOKKOS_LIBS) $(LIB) -o $(EXE) + +clean: kokkos-clean + rm -f *.o view_copy_constructor.cuda view_copy_constructor.exe + +# Compilation rules + +%.o:%.cpp $(KOKKOS_CPP_DEPENDS) $(HEADERS) + $(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) $(EXTRA_INC) -c $< -o $(notdir $@) diff --git a/lib/kokkos/benchmarks/view_copy_constructor/view_copy_constructor.cpp b/lib/kokkos/benchmarks/view_copy_constructor/view_copy_constructor.cpp new file mode 100644 index 0000000000..63c49f09c0 --- /dev/null +++ b/lib/kokkos/benchmarks/view_copy_constructor/view_copy_constructor.cpp @@ -0,0 +1,310 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +// The function "test_view_collection" exposes the copy constructor +// and destructor overheads in Kokkos View objects +// Please see the lines marked by "NOTE". + +#include +#include +#include +#include +#include +#include +#include + +// NVIEWS is the number of Kokkos View objects in our ViewCollection object +// We have chosen a large value of 40 to make it easier to see performance +// differences when using the likelihood attribute +#define NVIEWS 40 + +class ViewCollection { + public: + Kokkos::View v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, + v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40; + double m_expected_sum; + double m_side_effect; + int m_N; + + ViewCollection(int N) + : v1("v1", N), + v2("v2", N), + v3("v3", N), + v4("v4", N), + v5("v5", N), + v6("v6", N), + v7("v7", N), + v8("v8", N), + v9("v9", N), + v10("v10", N), + v11("v11", N), + v12("v12", N), + v13("v13", N), + v14("v14", N), + v15("v15", N), + v16("v16", N), + v17("v17", N), + v18("v18", N), + v19("v19", N), + v20("v20", N), + v21("v21", N), + v22("v22", N), + v23("v23", N), + v24("v24", N), + v25("v25", N), + v26("v26", N), + v27("v27", N), + v28("v28", N), + v29("v29", N), + v30("v30", N), + v31("v31", N), + v32("v32", N), + v33("v33", N), + v34("v34", N), + v35("v35", N), + v36("v36", N), + v37("v37", N), + v38("v38", N), + v39("v39", N), + v40("v40", N), + m_expected_sum(N * NVIEWS), + m_side_effect(0.0), + m_N(N) { + for (int i = 0; i < N; ++i) { + v1(i) = 1; + v2(i) = 1; + v3(i) = 1; + v4(i) = 1; + v5(i) = 1; + v6(i) = 1; + v7(i) = 1; + v8(i) = 1; + v9(i) = 1; + v10(i) = 1; + v11(i) = 1; + v12(i) = 1; + v13(i) = 1; + v14(i) = 1; + v15(i) = 1; + v16(i) = 1; + v17(i) = 1; + v18(i) = 1; + v19(i) = 1; + v20(i) = 1; + v21(i) = 1; + v22(i) = 1; + v23(i) = 1; + v24(i) = 1; + v25(i) = 1; + v26(i) = 1; + v27(i) = 1; + v28(i) = 1; + v29(i) = 1; + v30(i) = 1; + v31(i) = 1; + v32(i) = 1; + v33(i) = 1; + v34(i) = 1; + v35(i) = 1; + v36(i) = 1; + v37(i) = 1; + v38(i) = 1; + v39(i) = 1; + v40(i) = 1; + } + } + +// The ADD_COPY_CONSTRUCTOR macro is helpful to compare time in the copy +// constructor between compilers. We have found that the GNU compiler +// is sometimes able to inline the default copy constructor. +#ifdef ADD_COPY_CONSTRUCTOR + __attribute__((noinline)) ViewCollection(const ViewCollection& other) + : v1(other.v1), + v2(other.v2), + v3(other.v3), + v4(other.v4), + v5(other.v5), + v6(other.v6), + v7(other.v7), + v8(other.v8), + v9(other.v9), + v10(other.v10), + v11(other.v11), + v12(other.v12), + v13(other.v13), + v14(other.v14), + v15(other.v15), + v16(other.v16), + v17(other.v17), + v18(other.v18), + v19(other.v19), + v20(other.v20), + v21(other.v21), + v22(other.v22), + v23(other.v23), + v24(other.v24), + v25(other.v25), + v26(other.v26), + v27(other.v27), + v28(other.v28), + v29(other.v29), + v30(other.v30), + v31(other.v31), + v32(other.v32), + v33(other.v33), + v34(other.v34), + v35(other.v35), + v36(other.v36), + v37(other.v37), + v38(other.v38), + v39(other.v39), + v40(other.v40), + m_expected_sum(other.m_expected_sum), + m_side_effect(other.m_side_effect), + m_N(other.m_N) {} +#endif + + KOKKOS_INLINE_FUNCTION + double sum_views(int ii, bool execute_kernel) { + double result = 0.0; + if (execute_kernel) { + // This code is only executed when using the command line option -k + // The computation references all Kokkos views. This may help our + // effort to stop compilers from optimizing away the Kokkos views + for (int i = 0; i < m_N; ++i) { + result += v1(i) + v2(i) + v3(i) + v4(i) + v5(i) + v6(i) + v7(i) + + v8(i) + v9(i) + v10(i) + v11(i) + v12(i) + v13(i) + v14(i) + + v15(i) + v16(i) + v17(i) + v18(i) + v19(i) + v20(i) + v21(i) + + v22(i) + v23(i) + v24(i) + v25(i) + v26(i) + v27(i) + v28(i) + + v29(i) + v30(i) + v31(i) + v32(i) + v33(i) + v34(i) + v35(i) + + v36(i) + v37(i) + v38(i) + v39(i) + v40(i); + } + } else { + result = m_expected_sum; + } + // This statement introduces a side effect that may help our effort to + // stop compilers from optimizing away the temporary ViewCollection object + m_side_effect = result * (ii + 1); + return result; + } +}; + +void test_view_collection_kk(int N, int num_iter, bool execute_kernel) { + ViewCollection view_collection(N); + + Kokkos::Timer view_collection_timer; + double max_value = 0.0; + // Max Reduction boilerplate code taken from slide 53 of + // kokkos-tutorials/LectureSeries/KokkosTutorial_02_ViewsAndSpaces.pdf + Kokkos::parallel_reduce( + "collection-reduction", num_iter, + KOKKOS_LAMBDA(int i, double& valueToUpdate) { + // NOTE: The following lines expose the Kokkos View overheads + ViewCollection tmp_view_collection = view_collection; + double my_value = tmp_view_collection.sum_views(i, execute_kernel); + if (my_value > valueToUpdate) valueToUpdate = my_value; + }, + Kokkos::Max(max_value)); + double view_collection_time = view_collection_timer.seconds(); + + bool success = std::fabs(max_value - N * NVIEWS) < 1.E-6; + std::cout << "View Time = " << view_collection_time << " seconds" + << std::endl; + if (success) { + std::cout << "Kokkos run:" << std::endl; + std::cout << "SUCCESS" << std::endl; + } else { + std::cout << "FAILURE" << std::endl; + } +} + +void test_view_collection_serial(int N, int num_iter, bool execute_kernel) { + ViewCollection view_collection(N); + + Kokkos::Timer view_collection_timer; + double max_value = 0.0; + // Max Reduction boilerplate code taken from slide 53 of + // kokkos-tutorials/LectureSeries/KokkosTutorial_02_ViewsAndSpaces.pdf + for (int i = 0; i < num_iter; ++i) { + // NOTE: The following lines expose the Kokkos View overheads + ViewCollection tmp_view_collection = view_collection; + double my_value = tmp_view_collection.sum_views(i, execute_kernel); + if (my_value > max_value) max_value = my_value; + } + double view_collection_time = view_collection_timer.seconds(); + + bool success = std::fabs(max_value - N * NVIEWS) < 1.E-6; + std::cout << "View Time 2 = " << view_collection_time << " seconds" + << std::endl; + if (success) { + std::cout << "Serial run:" << std::endl; + std::cout << "SUCCESS" << std::endl; + } else { + std::cout << "FAILURE" << std::endl; + } +} + +int main(int argc, char* argv[]) { + // The benchmark is only testing reference counting for views on host. +#if defined(KOKKOS_ENABLE_OPENMP) || defined(KOKKOS_ENABLE_SERIAL) || \ + defined(KOKKOS_ENABLE_THREADS) || defined(KOKKOS_ENABLE_HPX) + int N = 1; + int num_iter = 1 << 27; + bool execute_kernel = false; + + for (int i = 0; i < argc; i++) { + if ((strcmp(argv[i], "-N") == 0)) { + N = atoi(argv[++i]); + if (N < 1) { + std::cout << "Array extent must be >= 1" << std::endl; + exit(1); + } + } else if (strcmp(argv[i], "-i") == 0) { + num_iter = atoi(argv[++i]); + if (num_iter < 1) { + std::cout << "Number of iterations must be >= 1" << std::endl; + exit(1); + } + } else if (strcmp(argv[i], "-k") == 0) { + execute_kernel = true; + } else if ((strcmp(argv[i], "-h") == 0)) { + printf(" Options:\n"); + printf(" -N : Array extent\n"); + printf(" -i : Number of iterations\n"); + printf(" -k: Execute the summation kernel\n"); + printf(" -h: Print this message\n\n"); + exit(1); + } + } + + std::cout << "Array extent = " << N << std::endl; + std::cout << "Iterations = " << num_iter << std::endl; + std::cout << "Execute summation kernel = " << std::boolalpha << execute_kernel + << std::noboolalpha << std::endl; + + // Test inside a Kokkos kernel. + Kokkos::initialize(argc, argv); + { test_view_collection_kk(N, num_iter, execute_kernel); } + + // Test outside Kokkos kernel. + test_view_collection_serial(N, num_iter, execute_kernel); + + Kokkos::finalize(); +#endif + + return 0; +} diff --git a/lib/kokkos/bin/nvcc_wrapper b/lib/kokkos/bin/nvcc_wrapper index dbfef2267f..d58645f98a 100755 --- a/lib/kokkos/bin/nvcc_wrapper +++ b/lib/kokkos/bin/nvcc_wrapper @@ -233,7 +233,7 @@ do cuda_args="$cuda_args $1" ;; #Handle more known nvcc args - --extended-lambda|--expt-extended-lambda|--expt-relaxed-constexpr|--Wno-deprecated-gpu-targets|-Wno-deprecated-gpu-targets|-allow-unsupported-compiler|--allow-unsupported-compiler) + --extended-lambda|--expt-extended-lambda|--expt-relaxed-constexpr|--Wno-deprecated-gpu-targets|-Wno-deprecated-gpu-targets|-allow-unsupported-compiler|--allow-unsupported-compiler|--disable-warnings) cuda_args="$cuda_args $1" ;; #Handle known nvcc args that have an argument diff --git a/lib/kokkos/cmake/Dependencies.cmake b/lib/kokkos/cmake/Dependencies.cmake index 611c089b2e..fb1e73b579 100644 --- a/lib/kokkos/cmake/Dependencies.cmake +++ b/lib/kokkos/cmake/Dependencies.cmake @@ -1,6 +1,5 @@ TRIBITS_PACKAGE_DEFINE_DEPENDENCIES( LIB_OPTIONAL_TPLS Pthread CUDA HWLOC DLlib - TEST_OPTIONAL_TPLS CUSPARSE ) TRIBITS_TPL_TENTATIVELY_ENABLE(DLlib) diff --git a/lib/kokkos/cmake/KokkosConfigCommon.cmake.in b/lib/kokkos/cmake/KokkosConfigCommon.cmake.in index 8d5ef0de42..d3ac39ffa3 100644 --- a/lib/kokkos/cmake/KokkosConfigCommon.cmake.in +++ b/lib/kokkos/cmake/KokkosConfigCommon.cmake.in @@ -225,8 +225,13 @@ FUNCTION(kokkos_compilation) # if built w/o CUDA support, we want to basically make this a no-op SET(_Kokkos_ENABLE_CUDA @Kokkos_ENABLE_CUDA@) + + IF(CMAKE_VERSION VERSION_GREATER_EQUAL 3.17) + SET(MAYBE_CURRENT_INSTALLATION_ROOT "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../../..") + ENDIF() + # search relative first and then absolute - SET(_HINTS "${CMAKE_CURRENT_LIST_DIR}/../.." "@CMAKE_INSTALL_PREFIX@") + SET(_HINTS "${MAYBE_CURRENT_INSTALLATION_ROOT}" "@CMAKE_INSTALL_PREFIX@") # find kokkos_launch_compiler FIND_PROGRAM(Kokkos_COMPILE_LAUNCHER diff --git a/lib/kokkos/cmake/KokkosCore_config.h.in b/lib/kokkos/cmake/KokkosCore_config.h.in index 94f8fc4214..7997aa3707 100644 --- a/lib/kokkos/cmake/KokkosCore_config.h.in +++ b/lib/kokkos/cmake/KokkosCore_config.h.in @@ -52,6 +52,8 @@ #cmakedefine KOKKOS_OPT_RANGE_AGGRESSIVE_VECTORIZATION // deprecated #cmakedefine KOKKOS_ENABLE_AGGRESSIVE_VECTORIZATION #cmakedefine KOKKOS_ENABLE_IMPL_MDSPAN +#cmakedefine KOKKOS_ENABLE_IMPL_REF_COUNT_BRANCH_UNLIKELY +#cmakedefine KOKKOS_ENABLE_IMPL_VIEW_OF_VIEWS_DESTRUCTOR_PRECONDITION_VIOLATION_WORKAROUND #cmakedefine KOKKOS_ENABLE_ATOMICS_BYPASS /* TPL Settings */ @@ -65,6 +67,7 @@ #cmakedefine KOKKOS_ARCH_ARMV8_THUNDERX #cmakedefine KOKKOS_ARCH_ARMV81 #cmakedefine KOKKOS_ARCH_ARMV8_THUNDERX2 +#cmakedefine KOKKOS_ARCH_ARMV9_GRACE #cmakedefine KOKKOS_ARCH_A64FX #cmakedefine KOKKOS_ARCH_AVX #cmakedefine KOKKOS_ARCH_AVX2 @@ -116,7 +119,6 @@ #cmakedefine KOKKOS_ARCH_AMD_GFX942 #cmakedefine KOKKOS_ARCH_AMD_GFX1030 #cmakedefine KOKKOS_ARCH_AMD_GFX1100 -#cmakedefine KOKKOS_ARCH_AMD_GFX1103 #cmakedefine KOKKOS_ARCH_AMD_GPU #cmakedefine KOKKOS_ARCH_VEGA // deprecated #cmakedefine KOKKOS_ARCH_VEGA906 // deprecated diff --git a/lib/kokkos/cmake/Modules/FindTPLCUDA.cmake b/lib/kokkos/cmake/Modules/FindTPLCUDA.cmake index 5a62c530fc..445f4e93a5 100644 --- a/lib/kokkos/cmake/Modules/FindTPLCUDA.cmake +++ b/lib/kokkos/cmake/Modules/FindTPLCUDA.cmake @@ -7,37 +7,38 @@ IF (NOT CUDAToolkit_ROOT) ENDIF() ENDIF() -# FIXME CMake 3.28.4 creates more targets than we export -IF(CMAKE_VERSION VERSION_GREATER_EQUAL "3.17.0" AND CMAKE_VERSION VERSION_LESS "3.28.4") - find_package(CUDAToolkit) -ELSE() - include(${CMAKE_CURRENT_LIST_DIR}/CudaToolkit.cmake) +IF(KOKKOS_CXX_HOST_COMPILER_ID STREQUAL NVHPC AND CMAKE_VERSION VERSION_LESS "3.20.1") + MESSAGE(FATAL_ERROR "Using NVHPC as host compiler requires at least CMake 3.20.1") ENDIF() - -IF (TARGET CUDA::cudart) - SET(FOUND_CUDART TRUE) - KOKKOS_EXPORT_IMPORTED_TPL(CUDA::cudart) -ELSE() - SET(FOUND_CUDART FALSE) -ENDIF() - -IF (TARGET CUDA::cuda_driver) - SET(FOUND_CUDA_DRIVER TRUE) - KOKKOS_EXPORT_IMPORTED_TPL(CUDA::cuda_driver) -ELSE() - SET(FOUND_CUDA_DRIVER FALSE) -ENDIF() - -include(FindPackageHandleStandardArgs) -IF(KOKKOS_CXX_HOST_COMPILER_ID STREQUAL NVHPC) - SET(KOKKOS_CUDA_ERROR "Using NVHPC as host compiler requires at least CMake 3.20.1") -ELSE() - SET(KOKKOS_CUDA_ERROR DEFAULT_MSG) -ENDIF() -FIND_PACKAGE_HANDLE_STANDARD_ARGS(TPLCUDA ${KOKKOS_CUDA_ERROR} FOUND_CUDART FOUND_CUDA_DRIVER) -IF (FOUND_CUDA_DRIVER AND FOUND_CUDART) +IF(CMAKE_VERSION VERSION_GREATER_EQUAL "3.17.0") + find_package(CUDAToolkit REQUIRED) KOKKOS_CREATE_IMPORTED_TPL(CUDA INTERFACE LINK_LIBRARIES CUDA::cuda_driver CUDA::cudart ) + KOKKOS_EXPORT_CMAKE_TPL(CUDAToolkit REQUIRED) +ELSE() + include(${CMAKE_CURRENT_LIST_DIR}/CudaToolkit.cmake) + + IF (TARGET CUDA::cudart) + SET(FOUND_CUDART TRUE) + KOKKOS_EXPORT_IMPORTED_TPL(CUDA::cudart) + ELSE() + SET(FOUND_CUDART FALSE) + ENDIF() + + IF (TARGET CUDA::cuda_driver) + SET(FOUND_CUDA_DRIVER TRUE) + KOKKOS_EXPORT_IMPORTED_TPL(CUDA::cuda_driver) + ELSE() + SET(FOUND_CUDA_DRIVER FALSE) + ENDIF() + + include(FindPackageHandleStandardArgs) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(TPLCUDA ${DEFAULT_MSG} FOUND_CUDART FOUND_CUDA_DRIVER) + IF (FOUND_CUDA_DRIVER AND FOUND_CUDART) + KOKKOS_CREATE_IMPORTED_TPL(CUDA INTERFACE + LINK_LIBRARIES CUDA::cuda_driver CUDA::cudart + ) + ENDIF() ENDIF() diff --git a/lib/kokkos/cmake/deps/CUDA.cmake b/lib/kokkos/cmake/deps/CUDA.cmake index 68bf5b3d57..5b6afd6151 100644 --- a/lib/kokkos/cmake/deps/CUDA.cmake +++ b/lib/kokkos/cmake/deps/CUDA.cmake @@ -35,7 +35,6 @@ IF(NOT _CUDA_FAILURE) GLOBAL_SET(TPL_CUDA_LIBRARY_DIRS) GLOBAL_SET(TPL_CUDA_INCLUDE_DIRS ${CUDA_TOOLKIT_INCLUDE}) GLOBAL_SET(TPL_CUDA_LIBRARIES ${CUDA_CUDART_LIBRARY} ${CUDA_cublas_LIBRARY} ${CUDA_cufft_LIBRARY}) - KOKKOS_CREATE_IMPORTED_TPL_LIBRARY(CUSPARSE) ELSE() SET(TPL_ENABLE_CUDA OFF) ENDIF() diff --git a/lib/kokkos/cmake/deps/CUSPARSE.cmake b/lib/kokkos/cmake/deps/CUSPARSE.cmake deleted file mode 100644 index b016971ab9..0000000000 --- a/lib/kokkos/cmake/deps/CUSPARSE.cmake +++ /dev/null @@ -1,26 +0,0 @@ -#@HEADER -# ************************************************************************ -# -# Kokkos v. 4.0 -# Copyright (2022) National Technology & Engineering -# Solutions of Sandia, LLC (NTESS). -# -# Under the terms of Contract DE-NA0003525 with NTESS, -# the U.S. Government retains certain rights in this software. -# -# Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. -# -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -# -# ************************************************************************ -# @HEADER - -#include(${TRIBITS_DEPS_DIR}/CUDA.cmake) - -#IF (TPL_ENABLE_CUDA) -# GLOBAL_SET(TPL_CUSPARSE_LIBRARY_DIRS) -# GLOBAL_SET(TPL_CUSPARSE_INCLUDE_DIRS ${TPL_CUDA_INCLUDE_DIRS}) -# GLOBAL_SET(TPL_CUSPARSE_LIBRARIES ${CUDA_cusparse_LIBRARY}) -# KOKKOS_CREATE_IMPORTED_TPL_LIBRARY(CUSPARSE) -#ENDIF() - diff --git a/lib/kokkos/cmake/fake_tribits.cmake b/lib/kokkos/cmake/fake_tribits.cmake index 4c5331ec79..a18d2ac518 100644 --- a/lib/kokkos/cmake/fake_tribits.cmake +++ b/lib/kokkos/cmake/fake_tribits.cmake @@ -118,14 +118,6 @@ FUNCTION(KOKKOS_ADD_TEST) ENDIF() ENDFUNCTION() -FUNCTION(KOKKOS_ADD_ADVANCED_TEST) - if (KOKKOS_HAS_TRILINOS) - TRIBITS_ADD_ADVANCED_TEST(${ARGN}) - else() - # TODO Write this - endif() -ENDFUNCTION() - MACRO(KOKKOS_CREATE_IMPORTED_TPL_LIBRARY TPL_NAME) ADD_INTERFACE_LIBRARY(TPL_LIB_${TPL_NAME}) TARGET_LINK_LIBRARIES(TPL_LIB_${TPL_NAME} LINK_PUBLIC ${TPL_${TPL_NAME}_LIBRARIES}) diff --git a/lib/kokkos/cmake/kokkos_arch.cmake b/lib/kokkos/cmake/kokkos_arch.cmake index df11c76cc3..a581d9f945 100644 --- a/lib/kokkos/cmake/kokkos_arch.cmake +++ b/lib/kokkos/cmake/kokkos_arch.cmake @@ -28,6 +28,7 @@ KOKKOS_CHECK_DEPRECATED_OPTIONS( #------------------------------------------------------------------------------- SET(KOKKOS_ARCH_LIST) +include(CheckCXXCompilerFlag) KOKKOS_DEPRECATED_LIST(ARCH ARCH) @@ -49,6 +50,7 @@ DECLARE_AND_CHECK_HOST_ARCH(ARMV81 "ARMv8.1 Compatible CPU") DECLARE_AND_CHECK_HOST_ARCH(ARMV8_THUNDERX "ARMv8 Cavium ThunderX CPU") DECLARE_AND_CHECK_HOST_ARCH(ARMV8_THUNDERX2 "ARMv8 Cavium ThunderX2 CPU") DECLARE_AND_CHECK_HOST_ARCH(A64FX "ARMv8.2 with SVE Support") +DECLARE_AND_CHECK_HOST_ARCH(ARMV9_GRACE "ARMv9 NVIDIA Grace CPU") DECLARE_AND_CHECK_HOST_ARCH(SNB "Intel Sandy/Ivy Bridge CPUs") DECLARE_AND_CHECK_HOST_ARCH(HSW "Intel Haswell CPUs") DECLARE_AND_CHECK_HOST_ARCH(BDW "Intel Broadwell Xeon E-class CPUs") @@ -101,9 +103,9 @@ LIST(APPEND CORRESPONDING_AMD_FLAGS gfx90a gfx90a gfx908 gfx908) LIST(APPEND SUPPORTED_AMD_GPUS MI50/60 MI50/60) LIST(APPEND SUPPORTED_AMD_ARCHS VEGA906 AMD_GFX906) LIST(APPEND CORRESPONDING_AMD_FLAGS gfx906 gfx906) -LIST(APPEND SUPPORTED_AMD_GPUS PHOENIX RX7900XTX V620/W6800 V620/W6800) -LIST(APPEND SUPPORTED_AMD_ARCHS AMD_GFX1103 AMD_GFX1100 NAVI1030 AMD_GFX1030) -LIST(APPEND CORRESPONDING_AMD_FLAGS gfx1103 gfx1100 gfx1030 gfx1030) +LIST(APPEND SUPPORTED_AMD_GPUS RX7900XTX RX7900XTX V620/W6800 V620/W6800) +LIST(APPEND SUPPORTED_AMD_ARCHS NAVI1100 AMD_GFX1100 NAVI1030 AMD_GFX1030) +LIST(APPEND CORRESPONDING_AMD_FLAGS gfx1100 gfx1100 gfx1030 gfx1030) #FIXME CAN BE REPLACED WITH LIST_ZIP IN CMAKE 3.17 FOREACH(ARCH IN LISTS SUPPORTED_AMD_ARCHS) @@ -189,12 +191,6 @@ IF (KOKKOS_CXX_COMPILER_ID STREQUAL Clang) ELSEIF(CUDAToolkit_BIN_DIR) GLOBAL_APPEND(KOKKOS_CUDA_OPTIONS --cuda-path=${CUDAToolkit_BIN_DIR}/..) ENDIF() -ELSEIF (KOKKOS_CXX_COMPILER_ID STREQUAL NVHPC) - SET(CUDA_ARCH_FLAG "-gpu") - GLOBAL_APPEND(KOKKOS_CUDA_OPTIONS -cuda) - IF (KOKKOS_ENABLE_CUDA) # FIXME ideally unreachable when CUDA not enabled - GLOBAL_APPEND(KOKKOS_LINK_OPTIONS -cuda) - ENDIF() ELSEIF(KOKKOS_CXX_COMPILER_ID STREQUAL NVIDIA) SET(CUDA_ARCH_FLAG "-arch") ENDIF() @@ -209,6 +205,11 @@ ENDIF() #------------------------------- KOKKOS_HIP_OPTIONS --------------------------- +KOKKOS_OPTION(IMPL_AMDGPU_FLAGS "" STRING "Set compiler flags for AMD GPUs") +KOKKOS_OPTION(IMPL_AMDGPU_LINK "" STRING "Set linker flags for AMD GPUs") +MARK_AS_ADVANCED(Kokkos_IMPL_AMDGPU_FLAGS) +MARK_AS_ADVANCED(Kokkos_IMPL_AMDGPU_LINK) + #clear anything that might be in the cache GLOBAL_SET(KOKKOS_AMDGPU_OPTIONS) IF(KOKKOS_ENABLE_HIP) @@ -301,6 +302,20 @@ IF (KOKKOS_ARCH_A64FX) ) ENDIF() +IF (KOKKOS_ARCH_ARMV9_GRACE) + SET(KOKKOS_ARCH_ARM_NEON ON) + check_cxx_compiler_flag("-mcpu=neoverse-n2" COMPILER_SUPPORTS_NEOVERSE_N2) + check_cxx_compiler_flag("-msve-vector-bits=128" COMPILER_SUPPORTS_SVE_VECTOR_BITS) + IF (COMPILER_SUPPORTS_NEOVERSE_N2 AND COMPILER_SUPPORTS_SVE_VECTOR_BITS) + COMPILER_SPECIFIC_FLAGS( + COMPILER_ID KOKKOS_CXX_HOST_COMPILER_ID + DEFAULT -mcpu=neoverse-n2 -msve-vector-bits=128 + ) + ELSE() + MESSAGE(WARNING "Compiler does not support ARMv9 Grace architecture") + ENDIF() +ENDIF() + IF (KOKKOS_ARCH_ZEN) COMPILER_SPECIFIC_FLAGS( COMPILER_ID KOKKOS_CXX_HOST_COMPILER_ID @@ -535,17 +550,17 @@ IF (KOKKOS_CXX_HOST_COMPILER_ID STREQUAL NVHPC) SET(KOKKOS_ARCH_AVX512XEON OFF) ENDIF() +# FIXME_NVCC nvcc doesn't seem to support Arm Neon. +IF(KOKKOS_ARCH_ARM_NEON AND KOKKOS_CXX_COMPILER_ID STREQUAL NVIDIA) + UNSET(KOKKOS_ARCH_ARM_NEON) +ENDIF() + IF (NOT KOKKOS_COMPILE_LANGUAGE STREQUAL CUDA) IF (KOKKOS_ENABLE_CUDA_RELOCATABLE_DEVICE_CODE) COMPILER_SPECIFIC_FLAGS( Clang -fcuda-rdc NVIDIA --relocatable-device-code=true - NVHPC -gpu=rdc ) - ELSEIF(KOKKOS_ENABLE_CUDA) - COMPILER_SPECIFIC_FLAGS( - NVHPC -gpu=nordc - ) ENDIF() ENDIF() @@ -571,7 +586,7 @@ IF (KOKKOS_ENABLE_HIP) COMPILER_SPECIFIC_FLAGS( DEFAULT -fgpu-rdc ) - IF (NOT KOKKOS_CXX_COMPILER_ID STREQUAL HIPCC) + IF (NOT KOKKOS_CXX_COMPILER_ID STREQUAL HIPCC AND NOT KOKKOS_IMPL_AMDGPU_FLAGS) COMPILER_SPECIFIC_LINK_OPTIONS( DEFAULT --hip-link ) @@ -654,15 +669,9 @@ FUNCTION(CHECK_CUDA_ARCH ARCH FLAG) IF(KOKKOS_ENABLE_COMPILE_AS_CMAKE_LANGUAGE) SET(CMAKE_CUDA_ARCHITECTURES ${KOKKOS_CUDA_ARCHITECTURES} PARENT_SCOPE) ELSE() - IF(KOKKOS_CXX_COMPILER_ID STREQUAL NVHPC) - STRING(REPLACE "sm_" "cc" NVHPC_CUDA_ARCH ${FLAG}) - GLOBAL_APPEND(KOKKOS_CUDA_OPTIONS "${CUDA_ARCH_FLAG}=${NVHPC_CUDA_ARCH}") - GLOBAL_APPEND(KOKKOS_LINK_OPTIONS "${CUDA_ARCH_FLAG}=${NVHPC_CUDA_ARCH}") - ELSE() - GLOBAL_APPEND(KOKKOS_CUDA_OPTIONS "${CUDA_ARCH_FLAG}=${FLAG}") - IF(KOKKOS_ENABLE_CUDA_RELOCATABLE_DEVICE_CODE OR KOKKOS_CXX_COMPILER_ID STREQUAL NVIDIA) - GLOBAL_APPEND(KOKKOS_LINK_OPTIONS "${CUDA_ARCH_FLAG}=${FLAG}") - ENDIF() + GLOBAL_APPEND(KOKKOS_CUDA_OPTIONS "${CUDA_ARCH_FLAG}=${FLAG}") + IF(KOKKOS_ENABLE_CUDA_RELOCATABLE_DEVICE_CODE OR KOKKOS_CXX_COMPILER_ID STREQUAL NVIDIA) + GLOBAL_APPEND(KOKKOS_LINK_OPTIONS "${CUDA_ARCH_FLAG}=${FLAG}") ENDIF() ENDIF() ENDIF() @@ -704,14 +713,16 @@ FUNCTION(CHECK_AMDGPU_ARCH ARCH FLAG) MESSAGE(WARNING "Given AMD GPU architecture ${ARCH}, but Kokkos_ENABLE_HIP, Kokkos_ENABLE_SYCL, Kokkos_ENABLE_OPENACC, and Kokkos_ENABLE_OPENMPTARGET are OFF. Option will be ignored.") UNSET(KOKKOS_ARCH_${ARCH} PARENT_SCOPE) ELSE() - IF(KOKKOS_ENABLE_HIP) - SET(KOKKOS_HIP_ARCHITECTURES ${FLAG} PARENT_SCOPE) - ENDIF() - SET(KOKKOS_AMDGPU_ARCH_FLAG ${FLAG} PARENT_SCOPE) - GLOBAL_APPEND(KOKKOS_AMDGPU_OPTIONS "${AMDGPU_ARCH_FLAG}=${FLAG}") - IF(KOKKOS_ENABLE_HIP_RELOCATABLE_DEVICE_CODE) - GLOBAL_APPEND(KOKKOS_LINK_OPTIONS "${AMDGPU_ARCH_FLAG}=${FLAG}") - ENDIF() + IF(KOKKOS_ENABLE_HIP) + SET(KOKKOS_HIP_ARCHITECTURES ${FLAG} PARENT_SCOPE) + ENDIF() + IF(NOT KOKKOS_IMPL_AMDGPU_FLAGS) + SET(KOKKOS_AMDGPU_ARCH_FLAG ${FLAG} PARENT_SCOPE) + GLOBAL_APPEND(KOKKOS_AMDGPU_OPTIONS "${AMDGPU_ARCH_FLAG}=${FLAG}") + ENDIF() + IF(KOKKOS_ENABLE_HIP_RELOCATABLE_DEVICE_CODE) + GLOBAL_APPEND(KOKKOS_LINK_OPTIONS "${AMDGPU_ARCH_FLAG}=${FLAG}") + ENDIF() ENDIF() ENDIF() ENDFUNCTION() @@ -724,6 +735,15 @@ FOREACH(ARCH IN LISTS SUPPORTED_AMD_ARCHS) CHECK_AMDGPU_ARCH(${ARCH} ${FLAG}) ENDFOREACH() +IF(KOKKOS_IMPL_AMDGPU_FLAGS) + IF (NOT AMDGPU_ARCH_ALREADY_SPECIFIED) + MESSAGE(FATAL_ERROR "When IMPL_AMDGPU_FLAGS is set the architecture autodectection is disabled. " + "Please explicitly set the GPU architecture.") + ENDIF() + GLOBAL_APPEND(KOKKOS_AMDGPU_OPTIONS "${KOKKOS_IMPL_AMDGPU_FLAGS}") + GLOBAL_APPEND(KOKKOS_LINK_OPTIONS "${KOKKOS_IMPL_AMDGPU_LINK}") +ENDIF() + MACRO(SET_AND_CHECK_AMD_ARCH ARCH FLAG) KOKKOS_SET_OPTION(ARCH_${ARCH} ON) CHECK_AMDGPU_ARCH(${ARCH} ${FLAG}) @@ -984,7 +1004,7 @@ IF (KOKKOS_ARCH_HOPPER90) ENDIF() #HIP detection of gpu arch -IF(KOKKOS_ENABLE_HIP AND NOT AMDGPU_ARCH_ALREADY_SPECIFIED) +IF(KOKKOS_ENABLE_HIP AND NOT AMDGPU_ARCH_ALREADY_SPECIFIED AND NOT KOKKOS_IMPL_AMDGPU_FLAGS) FIND_PROGRAM(ROCM_ENUMERATOR rocm_agent_enumerator) IF(NOT ROCM_ENUMERATOR) MESSAGE(FATAL_ERROR "Autodetection of AMD GPU architecture not possible as " diff --git a/lib/kokkos/cmake/kokkos_compiler_id.cmake b/lib/kokkos/cmake/kokkos_compiler_id.cmake index 9135ca2b41..e8bfadb64e 100644 --- a/lib/kokkos/cmake/kokkos_compiler_id.cmake +++ b/lib/kokkos/cmake/kokkos_compiler_id.cmake @@ -42,12 +42,8 @@ IF(Kokkos_ENABLE_CUDA) # If launcher was found and nvcc_wrapper was not specified as # compiler and `CMAKE_CXX_COMPILIER_LAUNCHER` is not set, set to use launcher. # Will ensure CMAKE_CXX_COMPILER is replaced by nvcc_wrapper - IF(Kokkos_COMPILE_LAUNCHER AND NOT INTERNAL_HAVE_COMPILER_NVCC AND NOT KOKKOS_CXX_COMPILER_ID STREQUAL Clang - AND NOT (Kokkos_ENABLE_IMPL_NVHPC_AS_DEVICE_COMPILER AND KOKKOS_CXX_COMPILER_ID STREQUAL NVHPC)) + IF(Kokkos_COMPILE_LAUNCHER AND NOT INTERNAL_HAVE_COMPILER_NVCC AND NOT KOKKOS_CXX_COMPILER_ID STREQUAL Clang) IF(CMAKE_CXX_COMPILER_LAUNCHER) - IF(KOKKOS_CXX_COMPILER_ID STREQUAL NVHPC) - MESSAGE(STATUS "Using nvc++ as device compiler requires Kokkos_ENABLE_IMPL_NVHPC_AS_DEVICE_COMPILER=ON!") - ENDIF() MESSAGE(FATAL_ERROR "Cannot use CMAKE_CXX_COMPILER_LAUNCHER if the CMAKE_CXX_COMPILER is not able to compile CUDA code, i.e. nvcc_wrapper or clang++!") ENDIF() # the first argument to launcher is always the C++ compiler defined by cmake @@ -149,56 +145,85 @@ IF(KOKKOS_CXX_COMPILER_ID STREQUAL Fujitsu) ENDIF() # Enforce the minimum compilers supported by Kokkos. -SET(KOKKOS_MESSAGE_TEXT "Compiler not supported by Kokkos. Required compiler versions:") -SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n Clang(CPU) 8.0.0 or higher") -SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n Clang(CUDA) 10.0.0 or higher") -SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n Clang(OpenMPTarget) 15.0.0 or higher") -SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n GCC 8.2.0 or higher") -SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n Intel 19.0.5 or higher") -SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n IntelLLVM(CPU) 2021.1.1 or higher") -SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n IntelLLVM(SYCL) 2023.0.0 or higher") -SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n NVCC 11.0.0 or higher") -SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n HIPCC 5.2.0 or higher") -SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n NVHPC/PGI 22.3 or higher") -SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n MSVC 19.29 or higher") -SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n XL/XLClang not supported") +IF(NOT CMAKE_CXX_STANDARD) + SET(CMAKE_CXX_STANDARD 17) +ENDIF() +IF(CMAKE_CXX_STANDARD EQUAL 17) + SET(KOKKOS_CLANG_CPU_MINIMUM 8.0.0) + SET(KOKKOS_CLANG_CUDA_MINIMUM 10.0.0) + SET(KOKKOS_CLANG_OPENMPTARGET_MINIMUM 15.0.0) + SET(KOKKOS_GCC_MINIMUM 8.2.0) + SET(KOKKOS_INTEL_MINIMUM 19.0.5) + SET(KOKKOS_INTEL_LLVM_CPU_MINIMUM 2021.1.1) + SET(KOKKOS_INTEL_LLVM_SYCL_MINIMUM 2023.0.0) + SET(KOKKOS_NVCC_MINIMUM 11.0.0) + SET(KOKKOS_HIPCC_MINIMUM 5.2.0) + SET(KOKKOS_NVHPC_MINIMUM 22.3) + SET(KOKKOS_MSVC_MINIMUM 19.29) +ELSE() + SET(KOKKOS_CLANG_CPU_MINIMUM 14.0.0) + SET(KOKKOS_CLANG_CUDA_MINIMUM 14.0.0) + SET(KOKKOS_CLANG_OPENMPTARGET_MINIMUM 15.0.0) + SET(KOKKOS_GCC_MINIMUM 10.1.0) + SET(KOKKOS_INTEL_MINIMUM "not supported") + SET(KOKKOS_INTEL_LLVM_CPU_MINIMUM 2022.0.0) + SET(KOKKOS_INTEL_LLVM_SYCL_MINIMUM 2023.0.0) + SET(KOKKOS_NVCC_MINIMUM 12.0.0) + SET(KOKKOS_HIPCC_MINIMUM 5.2.0) + SET(KOKKOS_NVHPC_MINIMUM 22.3) + SET(KOKKOS_MSVC_MINIMUM 19.30) +ENDIF() + +SET(KOKKOS_MESSAGE_TEXT "Compiler not supported by Kokkos for C++${CMAKE_CXX_STANDARD}. Required minimum compiler versions:") +SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n Clang(CPU) ${KOKKOS_CLANG_CPU_MINIMUM}") +SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n Clang(CUDA) ${KOKKOS_CLANG_CUDA_MINIMUM}") +SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n Clang(OpenMPTarget) ${KOKKOS_CLANG_OPENMPTARGET_MINIMUM}") +SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n GCC ${KOKKOS_GCC_MINIMUM}") +SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n Intel ${KOKKOS_INTEL_MINIMUM}") +SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n IntelLLVM(CPU) ${KOKKOS_INTEL_LLVM_CPU_MINIMUM}") +SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n IntelLLVM(SYCL) ${KOKKOS_INTEL_LLVM_SYCL_MINIMUM}") +SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n NVCC ${KOKKOS_NVCC_MINIMUM}") +SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n HIPCC ${KOKKOS_HIPCC_MINIMUM}") +SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n NVHPC/PGI ${KOKKOS_NVHPC_MINIMUM}") +SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n MSVC ${KOKKOS_MSVC_MINIMUM}") +SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n XL/XLClang not supported") SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\nCompiler: ${KOKKOS_CXX_COMPILER_ID} ${KOKKOS_CXX_COMPILER_VERSION}\n") IF(KOKKOS_CXX_COMPILER_ID STREQUAL Clang AND NOT Kokkos_ENABLE_CUDA) - IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS 8.0.0) + IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS ${KOKKOS_CLANG_CPU_MINIMUM}) MESSAGE(FATAL_ERROR "${KOKKOS_MESSAGE_TEXT}") ENDIF() ELSEIF(KOKKOS_CXX_COMPILER_ID STREQUAL Clang AND Kokkos_ENABLE_CUDA) - IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS 10.0.0) + IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS ${KOKKOS_CLANG_CUDA_MINIMUM}) MESSAGE(FATAL_ERROR "${KOKKOS_MESSAGE_TEXT}") ENDIF() ELSEIF(KOKKOS_CXX_COMPILER_ID STREQUAL GNU) - IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS 8.2.0) + IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS ${KOKKOS_GCC_MINIMUM}) MESSAGE(FATAL_ERROR "${KOKKOS_MESSAGE_TEXT}") ENDIF() ELSEIF(KOKKOS_CXX_COMPILER_ID STREQUAL Intel) - IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS 19.0.5) + IF((NOT CMAKE_CXX_STANDARD EQUAL 17) OR (KOKKOS_CXX_COMPILER_VERSION VERSION_LESS ${KOKKOS_INTEL_MINIMUM})) MESSAGE(FATAL_ERROR "${KOKKOS_MESSAGE_TEXT}") ENDIF() ELSEIF(KOKKOS_CXX_COMPILER_ID STREQUAL IntelLLVM AND NOT Kokkos_ENABLE_SYCL) - IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS 2021.1.1) + IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS ${KOKKOS_INTEL_LLVM_CPU_MINIMUM}) MESSAGE(FATAL_ERROR "${KOKKOS_MESSAGE_TEXT}") ENDIF() ELSEIF(KOKKOS_CXX_COMPILER_ID STREQUAL IntelLLVM AND Kokkos_ENABLE_SYCL) - IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS 2023.0.0) + IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS ${KOKKOS_INTEL_LLVM_SYCL_MINIMUM}) MESSAGE(FATAL_ERROR "${KOKKOS_MESSAGE_TEXT}") ENDIF() ELSEIF(KOKKOS_CXX_COMPILER_ID STREQUAL NVIDIA) - IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS 11.0.0) + IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS ${KOKKOS_NVCC_MINIMUM}) MESSAGE(FATAL_ERROR "${KOKKOS_MESSAGE_TEXT}") ENDIF() SET(CMAKE_CXX_EXTENSIONS OFF CACHE BOOL "Kokkos turns off CXX extensions" FORCE) ELSEIF(KOKKOS_CXX_COMPILER_ID STREQUAL HIPCC) - IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS 5.2.0) + IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS ${KOKKOS_HIPCC_MINIMUM}) MESSAGE(FATAL_ERROR "${KOKKOS_MESSAGE_TEXT}") ENDIF() ELSEIF(KOKKOS_CXX_COMPILER_ID STREQUAL PGI OR KOKKOS_CXX_COMPILER_ID STREQUAL NVHPC) - IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS 22.3) + IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS ${KOKKOS_NVHPC_MINIMUM}) MESSAGE(FATAL_ERROR "${KOKKOS_MESSAGE_TEXT}") ENDIF() # Treat PGI internally as NVHPC to simplify handling both compilers. @@ -206,13 +231,13 @@ ELSEIF(KOKKOS_CXX_COMPILER_ID STREQUAL PGI OR KOKKOS_CXX_COMPILER_ID STREQUAL NV # backward-compatible to pgc++. SET(KOKKOS_CXX_COMPILER_ID NVHPC CACHE STRING INTERNAL FORCE) ELSEIF(KOKKOS_CXX_COMPILER_ID STREQUAL "MSVC") - IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS 19.29) + IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS ${KOKKOS_MSVC_MINIMUM}) MESSAGE(FATAL_ERROR "${KOKKOS_MESSAGE_TEXT}") ENDIF() ELSEIF(KOKKOS_CXX_COMPILER_ID STREQUAL XL OR KOKKOS_CXX_COMPILER_ID STREQUAL XLClang) MESSAGE(FATAL_ERROR "${KOKKOS_MESSAGE_TEXT}") ELSEIF(KOKKOS_CXX_COMPILER_ID STREQUAL Clang AND Kokkos_ENABLE_OPENMPTARGET) - IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS 15.0.0) + IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS KOKKOS_CLANG_OPENMPTARGET_MINIMUM) MESSAGE(FATAL_ERROR "${KOKKOS_MESSAGE_TEXT}") ENDIF() ENDIF() diff --git a/lib/kokkos/cmake/kokkos_enable_options.cmake b/lib/kokkos/cmake/kokkos_enable_options.cmake index 32788e7aa0..b900c4a232 100644 --- a/lib/kokkos/cmake/kokkos_enable_options.cmake +++ b/lib/kokkos/cmake/kokkos_enable_options.cmake @@ -75,8 +75,12 @@ KOKKOS_ENABLE_OPTION(IMPL_HIP_UNIFIED_MEMORY OFF "Whether to leverage unified me # This option will go away eventually, but allows fallback to old implementation when needed. KOKKOS_ENABLE_OPTION(DESUL_ATOMICS_EXTERNAL OFF "Whether to use an external desul installation") KOKKOS_ENABLE_OPTION(ATOMICS_BYPASS OFF "**NOT RECOMMENDED** Whether to make atomics non-atomic for non-threaded MPI-only use cases") +KOKKOS_ENABLE_OPTION(IMPL_REF_COUNT_BRANCH_UNLIKELY ON "Whether to use the C++20 `[[unlikely]]` attribute in the view reference counting") +mark_as_advanced(Kokkos_ENABLE_IMPL_REF_COUNT_BRANCH_UNLIKELY) +KOKKOS_ENABLE_OPTION(IMPL_VIEW_OF_VIEWS_DESTRUCTOR_PRECONDITION_VIOLATION_WORKAROUND OFF "Whether to enable a workaround for invalid use of View of Views that causes program hang on destruction.") +mark_as_advanced(Kokkos_ENABLE_IMPL_VIEW_OF_VIEWS_DESTRUCTOR_PRECONDITION_VIOLATION_WORKAROUND) -KOKKOS_ENABLE_OPTION(IMPL_MDSPAN OFF "Whether to enable experimental mdspan support") +KOKKOS_ENABLE_OPTION(IMPL_MDSPAN ON "Whether to enable experimental mdspan support") KOKKOS_ENABLE_OPTION(MDSPAN_EXTERNAL OFF BOOL "Whether to use an external version of mdspan") KOKKOS_ENABLE_OPTION(IMPL_SKIP_COMPILER_MDSPAN ON BOOL "Whether to use an internal version of mdspan even if the compiler supports mdspan") mark_as_advanced(Kokkos_ENABLE_IMPL_MDSPAN) diff --git a/lib/kokkos/cmake/kokkos_functions.cmake b/lib/kokkos/cmake/kokkos_functions.cmake index 9dab1ca00e..d1f1e0d7a7 100644 --- a/lib/kokkos/cmake/kokkos_functions.cmake +++ b/lib/kokkos/cmake/kokkos_functions.cmake @@ -709,7 +709,12 @@ MACRO(kokkos_find_imported NAME) ENDIF() IF (NOT TPL_LIBRARY_SUFFIXES) - SET(TPL_LIBRARY_SUFFIXES lib lib64) + SET(TPL_LIBRARY_SUFFIXES lib) + IF(KOKKOS_IMPL_32BIT) + LIST(APPEND TPL_LIBRARY_SUFFIXES lib32) + ELSE() + LIST(APPEND TPL_LIBRARY_SUFFIXES lib64) + ENDIF() ENDIF() SET(${NAME}_INCLUDE_DIRS) diff --git a/lib/kokkos/cmake/kokkos_test_cxx_std.cmake b/lib/kokkos/cmake/kokkos_test_cxx_std.cmake index b075a3e36b..5b45674e05 100644 --- a/lib/kokkos/cmake/kokkos_test_cxx_std.cmake +++ b/lib/kokkos/cmake/kokkos_test_cxx_std.cmake @@ -124,12 +124,8 @@ IF(KOKKOS_ENABLE_CUDA) ELSEIF(CMAKE_CXX_EXTENSIONS) MESSAGE(FATAL_ERROR "Compiling CUDA code with clang doesn't support C++ extensions. Set -DCMAKE_CXX_EXTENSIONS=OFF") ENDIF() - ELSEIF(NOT KOKKOS_CXX_COMPILER_ID STREQUAL NVIDIA AND NOT (Kokkos_ENABLE_IMPL_NVHPC_AS_DEVICE_COMPILER AND KOKKOS_CXX_COMPILER_ID STREQUAL NVHPC)) - IF(KOKKOS_CXX_COMPILER_ID STREQUAL NVHPC) - MESSAGE(FATAL_ERROR "Invalid compiler for CUDA. To allow nvc++ as Cuda compiler, Kokkos_ENABLE_IMPL_NVHPC_AS_DEVICE_COMPILER=ON must be set!") - ELSE() - MESSAGE(FATAL_ERROR "Invalid compiler for CUDA. The compiler must be nvcc_wrapper or Clang or NVC++ or use kokkos_launch_compiler, but compiler ID was ${KOKKOS_CXX_COMPILER_ID}") - ENDIF() + ELSEIF(NOT KOKKOS_CXX_COMPILER_ID STREQUAL NVIDIA) + MESSAGE(FATAL_ERROR "Invalid compiler for CUDA. The compiler must be nvcc_wrapper or Clang or use kokkos_launch_compiler, but compiler ID was ${KOKKOS_CXX_COMPILER_ID}") ENDIF() ENDIF() diff --git a/lib/kokkos/cmake/kokkos_tpls.cmake b/lib/kokkos/cmake/kokkos_tpls.cmake index 6ef3b79bde..cda9e0d600 100644 --- a/lib/kokkos/cmake/kokkos_tpls.cmake +++ b/lib/kokkos/cmake/kokkos_tpls.cmake @@ -103,13 +103,19 @@ if (Kokkos_ENABLE_IMPL_MDSPAN AND Kokkos_ENABLE_MDSPAN_EXTERNAL) endif() IF (Kokkos_ENABLE_OPENMP) - find_package(OpenMP REQUIRED) + find_package(OpenMP REQUIRED COMPONENTS CXX) # FIXME_TRILINOS Trilinos doesn't allow for Kokkos to use find_dependency # so we just append the flags here instead of linking with the OpenMP target. IF(KOKKOS_HAS_TRILINOS) COMPILER_SPECIFIC_FLAGS(DEFAULT ${OpenMP_CXX_FLAGS}) ELSE() - KOKKOS_EXPORT_CMAKE_TPL(OpenMP REQUIRED) + KOKKOS_EXPORT_CMAKE_TPL(OpenMP REQUIRED COMPONENTS CXX) + ENDIF() + IF(Kokkos_ENABLE_HIP AND KOKKOS_COMPILE_LANGUAGE STREQUAL HIP) + GLOBAL_APPEND(KOKKOS_AMDGPU_OPTIONS ${OpenMP_CXX_FLAGS}) + ENDIF() + IF(Kokkos_ENABLE_CUDA AND KOKKOS_COMPILE_LANGUAGE STREQUAL CUDA) + GLOBAL_APPEND(KOKKOS_CUDA_OPTIONS -Xcompiler ${OpenMP_CXX_FLAGS}) ENDIF() ENDIF() diff --git a/lib/kokkos/cmake/kokkos_tribits.cmake b/lib/kokkos/cmake/kokkos_tribits.cmake index 060a7a8472..6da543a2c8 100644 --- a/lib/kokkos/cmake/kokkos_tribits.cmake +++ b/lib/kokkos/cmake/kokkos_tribits.cmake @@ -160,6 +160,12 @@ FUNCTION(KOKKOS_ADD_EXECUTABLE_AND_TEST ROOT_NAME) ) ENDIF() ENDIF() + # We noticed problems with -fvisibility=hidden for inline static variables + # if Kokkos was built as shared library. + IF(BUILD_SHARED_LIBS) + SET_PROPERTY(TARGET ${PACKAGE_NAME}_${ROOT_NAME} PROPERTY VISIBILITY_INLINES_HIDDEN ON) + SET_PROPERTY(TARGET ${PACKAGE_NAME}_${ROOT_NAME} PROPERTY CXX_VISIBILITY_PRESET hidden) + ENDIF() ENDFUNCTION() FUNCTION(KOKKOS_SET_EXE_PROPERTY ROOT_NAME) @@ -241,34 +247,6 @@ MACRO(KOKKOS_CONFIGURE_CORE) KOKKOS_CONFIG_HEADER( KokkosCore_Config_HeaderSet.in KokkosCore_Config_FwdBackend.hpp "KOKKOS_FWD" "fwd/Kokkos_Fwd" "${KOKKOS_ENABLED_DEVICES}") KOKKOS_CONFIG_HEADER( KokkosCore_Config_HeaderSet.in KokkosCore_Config_SetupBackend.hpp "KOKKOS_SETUP" "setup/Kokkos_Setup" "${DEVICE_SETUP_LIST}") KOKKOS_CONFIG_HEADER( KokkosCore_Config_HeaderSet.in KokkosCore_Config_DeclareBackend.hpp "KOKKOS_DECLARE" "decl/Kokkos_Declare" "${KOKKOS_ENABLED_DEVICES}") - SET(_DEFAULT_HOST_MEMSPACE "::Kokkos::HostSpace") - KOKKOS_OPTION(DEFAULT_DEVICE_MEMORY_SPACE "" STRING "Override default device memory space") - KOKKOS_OPTION(DEFAULT_HOST_MEMORY_SPACE "" STRING "Override default host memory space") - KOKKOS_OPTION(DEFAULT_DEVICE_EXECUTION_SPACE "" STRING "Override default device execution space") - KOKKOS_OPTION(DEFAULT_HOST_PARALLEL_EXECUTION_SPACE "" STRING "Override default host parallel execution space") - IF (NOT Kokkos_DEFAULT_DEVICE_EXECUTION_SPACE STREQUAL "") - SET(_DEVICE_PARALLEL ${Kokkos_DEFAULT_DEVICE_EXECUTION_SPACE}) - MESSAGE(STATUS "Override default device execution space: ${_DEVICE_PARALLEL}") - SET(KOKKOS_DEVICE_SPACE_ACTIVE ON) - ELSE() - IF (_DEVICE_PARALLEL STREQUAL "NoTypeDefined") - SET(KOKKOS_DEVICE_SPACE_ACTIVE OFF) - ELSE() - SET(KOKKOS_DEVICE_SPACE_ACTIVE ON) - ENDIF() - ENDIF() - IF (NOT Kokkos_DEFAULT_HOST_PARALLEL_EXECUTION_SPACE STREQUAL "") - SET(_HOST_PARALLEL ${Kokkos_DEFAULT_HOST_PARALLEL_EXECUTION_SPACE}) - MESSAGE(STATUS "Override default host parallel execution space: ${_HOST_PARALLEL}") - SET(KOKKOS_HOSTPARALLEL_SPACE_ACTIVE ON) - ELSE() - IF (_HOST_PARALLEL STREQUAL "NoTypeDefined") - SET(KOKKOS_HOSTPARALLEL_SPACE_ACTIVE OFF) - ELSE() - SET(KOKKOS_HOSTPARALLEL_SPACE_ACTIVE ON) - ENDIF() - ENDIF() - #We are ready to configure the header CONFIGURE_FILE(cmake/KokkosCore_config.h.in KokkosCore_config.h @ONLY) ENDMACRO() @@ -484,15 +462,10 @@ ENDFUNCTION() FUNCTION(KOKKOS_LIB_INCLUDE_DIRECTORIES TARGET) - IF(KOKKOS_HAS_TRILINOS) - #ignore the target, tribits doesn't do anything directly with targets - TRIBITS_INCLUDE_DIRECTORIES(${ARGN}) - ELSE() #append to a list for later - KOKKOS_LIB_TYPE(${TARGET} INCTYPE) - FOREACH(DIR ${ARGN}) - TARGET_INCLUDE_DIRECTORIES(${TARGET} ${INCTYPE} $) - ENDFOREACH() - ENDIF() + KOKKOS_LIB_TYPE(${TARGET} INCTYPE) + FOREACH(DIR ${ARGN}) + TARGET_INCLUDE_DIRECTORIES(${TARGET} ${INCTYPE} $) + ENDFOREACH() ENDFUNCTION() FUNCTION(KOKKOS_LIB_COMPILE_OPTIONS TARGET) diff --git a/lib/kokkos/cmake/tpls/FindTPLCUSPARSE.cmake b/lib/kokkos/cmake/tpls/FindTPLCUSPARSE.cmake deleted file mode 100644 index 4709f8002b..0000000000 --- a/lib/kokkos/cmake/tpls/FindTPLCUSPARSE.cmake +++ /dev/null @@ -1,26 +0,0 @@ -#@HEADER -# ************************************************************************ -# -# Kokkos v. 4.0 -# Copyright (2022) National Technology & Engineering -# Solutions of Sandia, LLC (NTESS). -# -# Under the terms of Contract DE-NA0003525 with NTESS, -# the U.S. Government retains certain rights in this software. -# -# Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. -# -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -# -#@HEADER - -# Check for CUDA support - -IF (NOT TPL_ENABLE_CUDA) - MESSAGE(FATAL_ERROR "\nCUSPARSE requires CUDA") -ELSE() - GLOBAL_SET(TPL_CUSPARSE_LIBRARY_DIRS) - GLOBAL_SET(TPL_CUSPARSE_INCLUDE_DIRS ${TPL_CUDA_INCLUDE_DIRS}) - GLOBAL_SET(TPL_CUSPARSE_LIBRARIES ${CUDA_cusparse_LIBRARY}) -ENDIF() - diff --git a/lib/kokkos/containers/src/Kokkos_DualView.hpp b/lib/kokkos/containers/src/Kokkos_DualView.hpp index e821570a8d..a37a2bdceb 100644 --- a/lib/kokkos/containers/src/Kokkos_DualView.hpp +++ b/lib/kokkos/containers/src/Kokkos_DualView.hpp @@ -944,13 +944,13 @@ class DualView : public ViewTraits { if (sizeMismatch) { ::Kokkos::realloc(arg_prop, d_view, n0, n1, n2, n3, n4, n5, n6, n7); - if (alloc_prop_input::initialize) { + if constexpr (alloc_prop_input::initialize) { h_view = create_mirror_view(typename t_host::memory_space(), d_view); } else { h_view = create_mirror_view(Kokkos::WithoutInitializing, typename t_host::memory_space(), d_view); } - } else if (alloc_prop_input::initialize) { + } else if constexpr (alloc_prop_input::initialize) { if constexpr (alloc_prop_input::has_execution_space) { const auto& exec_space = Impl::get_property(arg_prop); @@ -1038,12 +1038,10 @@ class DualView : public ViewTraits { /* Resize on Device */ if (sizeMismatch) { ::Kokkos::resize(properties, d_view, n0, n1, n2, n3, n4, n5, n6, n7); - if (alloc_prop_input::initialize) { - h_view = create_mirror_view(typename t_host::memory_space(), d_view); - } else { - h_view = create_mirror_view(Kokkos::WithoutInitializing, - typename t_host::memory_space(), d_view); - } + // this part of the lambda was relocated in a method as it contains a + // `if constexpr`. In some cases, both branches were evaluated + // leading to a compile error + resync_host(properties); /* Mark Device copy as modified */ ++modified_flags(1); @@ -1054,13 +1052,10 @@ class DualView : public ViewTraits { /* Resize on Host */ if (sizeMismatch) { ::Kokkos::resize(properties, h_view, n0, n1, n2, n3, n4, n5, n6, n7); - if (alloc_prop_input::initialize) { - d_view = create_mirror_view(typename t_dev::memory_space(), h_view); - - } else { - d_view = create_mirror_view(Kokkos::WithoutInitializing, - typename t_dev::memory_space(), h_view); - } + // this part of the lambda was relocated in a method as it contains a + // `if constexpr`. In some cases, both branches were evaluated + // leading to a compile error + resync_device(properties); /* Mark Host copy as modified */ ++modified_flags(0); @@ -1099,6 +1094,39 @@ class DualView : public ViewTraits { } } + private: + // resync host mirror from device + // this code was relocated from a lambda as it contains a `if constexpr`. + // In some cases, both branches were evaluated, leading to a compile error + template + inline void resync_host(Impl::ViewCtorProp const&) { + using alloc_prop_input = Impl::ViewCtorProp; + + if constexpr (alloc_prop_input::initialize) { + h_view = create_mirror_view(typename t_host::memory_space(), d_view); + } else { + h_view = create_mirror_view(Kokkos::WithoutInitializing, + typename t_host::memory_space(), d_view); + } + } + + // resync device mirror from host + // this code was relocated from a lambda as it contains a `if constexpr` + // In some cases, both branches were evaluated leading to a compile error + template + inline void resync_device(Impl::ViewCtorProp const&) { + using alloc_prop_input = Impl::ViewCtorProp; + + if constexpr (alloc_prop_input::initialize) { + d_view = create_mirror_view(typename t_dev::memory_space(), h_view); + + } else { + d_view = create_mirror_view(Kokkos::WithoutInitializing, + typename t_dev::memory_space(), h_view); + } + } + + public: void resize(const size_t n0 = KOKKOS_IMPL_CTOR_DEFAULT_ARG, const size_t n1 = KOKKOS_IMPL_CTOR_DEFAULT_ARG, const size_t n2 = KOKKOS_IMPL_CTOR_DEFAULT_ARG, diff --git a/lib/kokkos/containers/src/Kokkos_DynRankView.hpp b/lib/kokkos/containers/src/Kokkos_DynRankView.hpp index 5fa59f1b7c..5f7fcaf69e 100644 --- a/lib/kokkos/containers/src/Kokkos_DynRankView.hpp +++ b/lib/kokkos/containers/src/Kokkos_DynRankView.hpp @@ -1657,8 +1657,7 @@ KOKKOS_FUNCTION auto as_view_of_rank_n( if constexpr (std::is_same_v || std::is_same_v || - std::is_same_v || - is_layouttiled::value) { + std::is_same_v) { for (int i = N; i < 7; ++i) layout.dimension[i] = KOKKOS_IMPL_CTOR_DEFAULT_ARG; } @@ -1933,254 +1932,155 @@ struct MirrorDRVType { } // namespace Impl namespace Impl { + +// create a mirror +// private interface that accepts arbitrary view constructor args passed by a +// view_alloc template -inline typename DynRankView::HostMirror create_mirror( - const DynRankView& src, - const Impl::ViewCtorProp& arg_prop, - std::enable_if_t::has_memory_space>* = - nullptr) { - using src_type = DynRankView; - using dst_type = typename src_type::HostMirror; - - using alloc_prop_input = Impl::ViewCtorProp; - - static_assert( - !alloc_prop_input::has_label, - "The view constructor arguments passed to Kokkos::create_mirror " - "must not include a label!"); - static_assert( - !alloc_prop_input::has_pointer, - "The view constructor arguments passed to Kokkos::create_mirror must " - "not include a pointer!"); - static_assert( - !alloc_prop_input::allow_padding, - "The view constructor arguments passed to Kokkos::create_mirror must " - "not explicitly allow padding!"); +inline auto create_mirror(const DynRankView& src, + const Impl::ViewCtorProp& arg_prop) { + check_view_ctor_args_create_mirror(); auto prop_copy = Impl::with_properties_if_unset( arg_prop, std::string(src.label()).append("_mirror")); - return dst_type(prop_copy, Impl::reconstructLayout(src.layout(), src.rank())); -} + if constexpr (Impl::ViewCtorProp::has_memory_space) { + using dst_type = typename Impl::MirrorDRVType< + typename Impl::ViewCtorProp::memory_space, T, + P...>::view_type; -template -inline auto create_mirror( - const DynRankView& src, - const Impl::ViewCtorProp& arg_prop, - std::enable_if_t::has_memory_space>* = - nullptr) { - using dst_type = typename Impl::MirrorDRVType< - typename Impl::ViewCtorProp::memory_space, T, - P...>::view_type; + return dst_type(prop_copy, + Impl::reconstructLayout(src.layout(), src.rank())); + } else { + using src_type = DynRankView; + using dst_type = typename src_type::HostMirror; - using alloc_prop_input = Impl::ViewCtorProp; - - static_assert( - !alloc_prop_input::has_label, - "The view constructor arguments passed to Kokkos::create_mirror " - "must not include a label!"); - static_assert( - !alloc_prop_input::has_pointer, - "The view constructor arguments passed to Kokkos::create_mirror must " - "not include a pointer!"); - static_assert( - !alloc_prop_input::allow_padding, - "The view constructor arguments passed to Kokkos::create_mirror must " - "not explicitly allow padding!"); - - auto prop_copy = Impl::with_properties_if_unset( - arg_prop, std::string(src.label()).append("_mirror")); - - return dst_type(prop_copy, Impl::reconstructLayout(src.layout(), src.rank())); + return dst_type(prop_copy, + Impl::reconstructLayout(src.layout(), src.rank())); + } +#if defined(KOKKOS_COMPILER_INTEL) || \ + (defined(KOKKOS_COMPILER_NVCC) && KOKKOS_COMPILER_NVCC >= 1130 && \ + !defined(KOKKOS_COMPILER_MSVC)) + __builtin_unreachable(); +#endif } } // namespace Impl -// Create a mirror in host space -template -inline typename DynRankView::HostMirror create_mirror( - const DynRankView& src, - std::enable_if_t::specialize, - void>::value>* = nullptr) { - return Impl::create_mirror(src, Kokkos::Impl::ViewCtorProp<>{}); +// public interface +template ::specialize>>> +inline auto create_mirror(const DynRankView& src) { + return Impl::create_mirror(src, Kokkos::view_alloc()); } -template -inline typename DynRankView::HostMirror create_mirror( - Kokkos::Impl::WithoutInitializing_t wi, const DynRankView& src, - std::enable_if_t::specialize, - void>::value>* = nullptr) { +// public interface that accepts a without initializing flag +template ::specialize>>> +inline auto create_mirror(Kokkos::Impl::WithoutInitializing_t wi, + const DynRankView& src) { return Impl::create_mirror(src, Kokkos::view_alloc(wi)); } -template -inline typename DynRankView::HostMirror create_mirror( - const Impl::ViewCtorProp& arg_prop, - const DynRankView& src, - std::enable_if_t< - std::is_void::specialize>::value && - !Impl::ViewCtorProp::has_memory_space>* = nullptr) { - return Impl::create_mirror(src, arg_prop); -} - -// Create a mirror in a new space +// public interface that accepts a space template ::value && - std::is_void::specialize>::value>> -typename Impl::MirrorDRVType::view_type create_mirror( - const Space&, const Kokkos::DynRankView& src) { + std::is_void_v::specialize>>> +auto create_mirror(const Space&, const Kokkos::DynRankView& src) { return Impl::create_mirror( src, Kokkos::view_alloc(typename Space::memory_space{})); } -template -typename Impl::MirrorDRVType::view_type create_mirror( - Kokkos::Impl::WithoutInitializing_t wi, const Space&, - const Kokkos::DynRankView& src, - std::enable_if_t::specialize, - void>::value>* = nullptr) { +// public interface that accepts a space and a without initializing flag +template ::value && + std::is_void_v::specialize>>> +auto create_mirror(Kokkos::Impl::WithoutInitializing_t wi, const Space&, + const Kokkos::DynRankView& src) { return Impl::create_mirror( src, Kokkos::view_alloc(wi, typename Space::memory_space{})); } -template -inline auto create_mirror( - const Impl::ViewCtorProp& arg_prop, - const DynRankView& src, - std::enable_if_t< - std::is_void::specialize>::value && - Impl::ViewCtorProp::has_memory_space>* = nullptr) { - using ReturnType = typename Impl::MirrorDRVType< - typename Impl::ViewCtorProp::memory_space, T, - P...>::view_type; - return ReturnType{Impl::create_mirror(src, arg_prop)}; +// public interface that accepts arbitrary view constructor args passed by a +// view_alloc +template ::specialize>>> +inline auto create_mirror(const Impl::ViewCtorProp& arg_prop, + const DynRankView& src) { + return Impl::create_mirror(src, arg_prop); } namespace Impl { -template -inline std::enable_if_t< - !Impl::ViewCtorProp::has_memory_space && - std::is_same< - typename DynRankView::memory_space, - typename DynRankView::HostMirror::memory_space>::value && - std::is_same< - typename DynRankView::data_type, - typename DynRankView::HostMirror::data_type>::value, - typename DynRankView::HostMirror> -create_mirror_view(const DynRankView& src, - const typename Impl::ViewCtorProp&) { - return src; -} +// create a mirror view +// private interface that accepts arbitrary view constructor args passed by a +// view_alloc template -inline std::enable_if_t< - !Impl::ViewCtorProp::has_memory_space && - !(std::is_same< - typename DynRankView::memory_space, - typename DynRankView::HostMirror::memory_space>::value && - std::is_same< - typename DynRankView::data_type, - typename DynRankView::HostMirror::data_type>::value), - typename DynRankView::HostMirror> -create_mirror_view( +inline auto create_mirror_view( const DynRankView& src, - const typename Impl::ViewCtorProp& arg_prop) { - return Kokkos::Impl::create_mirror(src, arg_prop); + [[maybe_unused]] const typename Impl::ViewCtorProp& + arg_prop) { + if constexpr (!Impl::ViewCtorProp::has_memory_space) { + if constexpr (std::is_same::memory_space, + typename DynRankView< + T, P...>::HostMirror::memory_space>::value && + std::is_same::data_type, + typename DynRankView< + T, P...>::HostMirror::data_type>::value) { + return typename DynRankView::HostMirror(src); + } else { + return Kokkos::Impl::choose_create_mirror(src, arg_prop); + } + } else { + if constexpr (Impl::MirrorDRViewType::memory_space, + T, P...>::is_same_memspace) { + return typename Impl::MirrorDRViewType< + typename Impl::ViewCtorProp::memory_space, T, + P...>::view_type(src); + } else { + return Kokkos::Impl::choose_create_mirror(src, arg_prop); + } + } +#if defined(KOKKOS_COMPILER_INTEL) || \ + (defined(KOKKOS_COMPILER_NVCC) && KOKKOS_COMPILER_NVCC >= 1130 && \ + !defined(KOKKOS_COMPILER_MSVC)) + __builtin_unreachable(); +#endif } -template ::has_memory_space>> -inline std::enable_if_t< - Kokkos::is_space< - typename Impl::ViewCtorProp::memory_space>::value && - Impl::MirrorDRViewType< - typename Impl::ViewCtorProp::memory_space, T, - P...>::is_same_memspace, - typename Impl::MirrorDRViewType< - typename Impl::ViewCtorProp::memory_space, T, - P...>::view_type> -create_mirror_view(const Kokkos::DynRankView& src, - const typename Impl::ViewCtorProp&) { - return src; -} - -template ::has_memory_space>> -inline std::enable_if_t< - Kokkos::is_space< - typename Impl::ViewCtorProp::memory_space>::value && - !Impl::MirrorDRViewType< - typename Impl::ViewCtorProp::memory_space, T, - P...>::is_same_memspace, - typename Impl::MirrorDRViewType< - typename Impl::ViewCtorProp::memory_space, T, - P...>::view_type> -create_mirror_view( - const Kokkos::DynRankView& src, - const typename Impl::ViewCtorProp& arg_prop) { - return Kokkos::Impl::create_mirror(src, arg_prop); -} } // namespace Impl -// Create a mirror view in host space +// public interface template -inline std::enable_if_t< - (std::is_same< - typename DynRankView::memory_space, - typename DynRankView::HostMirror::memory_space>::value && - std::is_same::data_type, - typename DynRankView::HostMirror::data_type>::value), - typename DynRankView::HostMirror> -create_mirror_view(const Kokkos::DynRankView& src) { - return src; -} - -template -inline std::enable_if_t< - !(std::is_same< - typename DynRankView::memory_space, - typename DynRankView::HostMirror::memory_space>::value && - std::is_same< - typename DynRankView::data_type, - typename DynRankView::HostMirror::data_type>::value), - typename DynRankView::HostMirror> -create_mirror_view(const Kokkos::DynRankView& src) { - return Kokkos::create_mirror(src); +inline auto create_mirror_view(const Kokkos::DynRankView& src) { + return Impl::create_mirror_view(src, Kokkos::view_alloc()); } +// public interface that accepts a without initializing flag template inline auto create_mirror_view(Kokkos::Impl::WithoutInitializing_t wi, const DynRankView& src) { return Impl::create_mirror_view(src, Kokkos::view_alloc(wi)); } -// Create a mirror view in a new space -// FIXME_C++17 Improve SFINAE here. +// public interface that accepts a space template ::value>> -inline typename Impl::MirrorDRViewType::view_type -create_mirror_view( - const Space&, const Kokkos::DynRankView& src, - std::enable_if_t< - Impl::MirrorDRViewType::is_same_memspace>* = nullptr) { - return src; +inline auto create_mirror_view(const Space&, + const Kokkos::DynRankView& src) { + return Impl::create_mirror_view( + src, Kokkos::view_alloc(typename Space::memory_space())); } -// FIXME_C++17 Improve SFINAE here. +// public interface that accepts a space and a without initializing flag template ::value>> -inline typename Impl::MirrorDRViewType::view_type -create_mirror_view( - const Space& space, const Kokkos::DynRankView& src, - std::enable_if_t< - !Impl::MirrorDRViewType::is_same_memspace>* = nullptr) { - return Kokkos::create_mirror(space, src); -} - -template + typename Enable = std::enable_if_t::value>> inline auto create_mirror_view(Kokkos::Impl::WithoutInitializing_t wi, const Space&, const Kokkos::DynRankView& src) { @@ -2188,6 +2088,8 @@ inline auto create_mirror_view(Kokkos::Impl::WithoutInitializing_t wi, src, Kokkos::view_alloc(typename Space::memory_space{}, wi)); } +// public interface that accepts arbitrary view constructor args passed by a +// view_alloc template inline auto create_mirror_view( const typename Impl::ViewCtorProp& arg_prop, @@ -2195,75 +2097,51 @@ inline auto create_mirror_view( return Impl::create_mirror_view(src, arg_prop); } -template +// create a mirror view and deep copy it +// public interface that accepts arbitrary view constructor args passed by a +// view_alloc +template ::specialize>::value>> auto create_mirror_view_and_copy( - const Impl::ViewCtorProp&, - const Kokkos::DynRankView& src, - std::enable_if_t< - std::is_void::specialize>::value && - Impl::MirrorDRViewType< - typename Impl::ViewCtorProp::memory_space, T, - P...>::is_same_memspace>* = nullptr) { + [[maybe_unused]] const Impl::ViewCtorProp& arg_prop, + const Kokkos::DynRankView& src) { using alloc_prop_input = Impl::ViewCtorProp; - static_assert( - alloc_prop_input::has_memory_space, - "The view constructor arguments passed to " - "Kokkos::create_mirror_view_and_copy must include a memory space!"); - static_assert(!alloc_prop_input::has_pointer, - "The view constructor arguments passed to " - "Kokkos::create_mirror_view_and_copy must " - "not include a pointer!"); - static_assert(!alloc_prop_input::allow_padding, - "The view constructor arguments passed to " - "Kokkos::create_mirror_view_and_copy must " - "not explicitly allow padding!"); - // same behavior as deep_copy(src, src) - if (!alloc_prop_input::has_execution_space) - fence( - "Kokkos::create_mirror_view_and_copy: fence before returning src view"); - return src; -} + Impl::check_view_ctor_args_create_mirror_view_and_copy(); -template -auto create_mirror_view_and_copy( - const Impl::ViewCtorProp& arg_prop, - const Kokkos::DynRankView& src, - std::enable_if_t< - std::is_void::specialize>::value && - !Impl::MirrorDRViewType< - typename Impl::ViewCtorProp::memory_space, T, - P...>::is_same_memspace>* = nullptr) { - using alloc_prop_input = Impl::ViewCtorProp; - static_assert( - alloc_prop_input::has_memory_space, - "The view constructor arguments passed to " - "Kokkos::create_mirror_view_and_copy must include a memory space!"); - static_assert(!alloc_prop_input::has_pointer, - "The view constructor arguments passed to " - "Kokkos::create_mirror_view_and_copy must " - "not include a pointer!"); - static_assert(!alloc_prop_input::allow_padding, - "The view constructor arguments passed to " - "Kokkos::create_mirror_view_and_copy must " - "not explicitly allow padding!"); - using Space = typename alloc_prop_input::memory_space; - using Mirror = typename Impl::MirrorDRViewType::view_type; + if constexpr (Impl::MirrorDRViewType< + typename Impl::ViewCtorProp::memory_space, + T, P...>::is_same_memspace) { + // same behavior as deep_copy(src, src) + if constexpr (!alloc_prop_input::has_execution_space) + fence( + "Kokkos::create_mirror_view_and_copy: fence before returning src " + "view"); + return src; + } else { + using Space = typename alloc_prop_input::memory_space; + using Mirror = typename Impl::MirrorDRViewType::view_type; - auto arg_prop_copy = Impl::with_properties_if_unset( - arg_prop, std::string{}, WithoutInitializing, - typename Space::execution_space{}); + auto arg_prop_copy = Impl::with_properties_if_unset( + arg_prop, std::string{}, WithoutInitializing, + typename Space::execution_space{}); - std::string& label = Impl::get_property(arg_prop_copy); - if (label.empty()) label = src.label(); - auto mirror = typename Mirror::non_const_type{ - arg_prop_copy, Impl::reconstructLayout(src.layout(), src.rank())}; - if constexpr (alloc_prop_input::has_execution_space) { - deep_copy(Impl::get_property(arg_prop_copy), - mirror, src); - } else - deep_copy(mirror, src); - return mirror; + std::string& label = Impl::get_property(arg_prop_copy); + if (label.empty()) label = src.label(); + auto mirror = typename Mirror::non_const_type{ + arg_prop_copy, Impl::reconstructLayout(src.layout(), src.rank())}; + if constexpr (alloc_prop_input::has_execution_space) { + deep_copy(Impl::get_property(arg_prop_copy), + mirror, src); + } else + deep_copy(mirror, src); + return mirror; + } +#if defined(KOKKOS_COMPILER_NVCC) && KOKKOS_COMPILER_NVCC >= 1130 && \ + !defined(KOKKOS_COMPILER_MSVC) + __builtin_unreachable(); +#endif } template diff --git a/lib/kokkos/containers/src/Kokkos_DynamicView.hpp b/lib/kokkos/containers/src/Kokkos_DynamicView.hpp index 12885edbae..a4b74e246e 100644 --- a/lib/kokkos/containers/src/Kokkos_DynamicView.hpp +++ b/lib/kokkos/containers/src/Kokkos_DynamicView.hpp @@ -590,96 +590,81 @@ struct MirrorDynamicViewType { } // namespace Impl namespace Impl { -template -inline auto create_mirror( - const Kokkos::Experimental::DynamicView& src, - const Impl::ViewCtorProp& arg_prop, - std::enable_if_t::has_memory_space>* = - nullptr) { - using alloc_prop_input = Impl::ViewCtorProp; - static_assert( - !alloc_prop_input::has_label, - "The view constructor arguments passed to Kokkos::create_mirror " - "must not include a label!"); - static_assert( - !alloc_prop_input::has_pointer, - "The view constructor arguments passed to Kokkos::create_mirror must " - "not include a pointer!"); - static_assert( - !alloc_prop_input::allow_padding, - "The view constructor arguments passed to Kokkos::create_mirror must " - "not explicitly allow padding!"); +// create a mirror +// private interface that accepts arbitrary view constructor args passed by a +// view_alloc +template +inline auto create_mirror(const Kokkos::Experimental::DynamicView& src, + const Impl::ViewCtorProp& arg_prop) { + using alloc_prop_input = Impl::ViewCtorProp; + check_view_ctor_args_create_mirror(); auto prop_copy = Impl::with_properties_if_unset( arg_prop, std::string(src.label()).append("_mirror")); - auto ret = typename Kokkos::Experimental::DynamicView::HostMirror( - prop_copy, src.chunk_size(), src.chunk_max() * src.chunk_size()); + if constexpr (Impl::ViewCtorProp::has_memory_space) { + using MemorySpace = typename alloc_prop_input::memory_space; - ret.resize_serial(src.extent(0)); + auto ret = typename Kokkos::Impl::MirrorDynamicViewType< + MemorySpace, T, P...>::view_type(prop_copy, src.chunk_size(), + src.chunk_max() * src.chunk_size()); - return ret; + ret.resize_serial(src.extent(0)); + + return ret; + } else { + auto ret = typename Kokkos::Experimental::DynamicView::HostMirror( + prop_copy, src.chunk_size(), src.chunk_max() * src.chunk_size()); + + ret.resize_serial(src.extent(0)); + + return ret; + } +#if defined(KOKKOS_COMPILER_INTEL) || \ + (defined(KOKKOS_COMPILER_NVCC) && KOKKOS_COMPILER_NVCC >= 1130 && \ + !defined(KOKKOS_COMPILER_MSVC)) + __builtin_unreachable(); +#endif } -template -inline auto create_mirror( - const Kokkos::Experimental::DynamicView& src, - const Impl::ViewCtorProp& arg_prop, - std::enable_if_t::has_memory_space>* = - nullptr) { - using alloc_prop_input = Impl::ViewCtorProp; - - static_assert( - !alloc_prop_input::has_label, - "The view constructor arguments passed to Kokkos::create_mirror " - "must not include a label!"); - static_assert( - !alloc_prop_input::has_pointer, - "The view constructor arguments passed to Kokkos::create_mirror must " - "not include a pointer!"); - static_assert( - !alloc_prop_input::allow_padding, - "The view constructor arguments passed to Kokkos::create_mirror must " - "not explicitly allow padding!"); - - using MemorySpace = typename alloc_prop_input::memory_space; - auto prop_copy = Impl::with_properties_if_unset( - arg_prop, std::string(src.label()).append("_mirror")); - - auto ret = typename Kokkos::Impl::MirrorDynamicViewType< - MemorySpace, T, P...>::view_type(prop_copy, src.chunk_size(), - src.chunk_max() * src.chunk_size()); - - ret.resize_serial(src.extent(0)); - - return ret; -} } // namespace Impl -// Create a mirror in host space -template +// public interface +template ::specialize>>> inline auto create_mirror( const Kokkos::Experimental::DynamicView& src) { return Impl::create_mirror(src, Impl::ViewCtorProp<>{}); } -template +// public interface that accepts a without initializing flag +template ::specialize>>> inline auto create_mirror( Kokkos::Impl::WithoutInitializing_t wi, const Kokkos::Experimental::DynamicView& src) { return Impl::create_mirror(src, Kokkos::view_alloc(wi)); } -// Create a mirror in a new space -template +// public interface that accepts a space +template ::value && + std::is_void_v::specialize>>> inline auto create_mirror( const Space&, const Kokkos::Experimental::DynamicView& src) { return Impl::create_mirror( src, Kokkos::view_alloc(typename Space::memory_space{})); } -template +// public interface that accepts a space and a without initializing flag +template ::value && + std::is_void_v::specialize>>> typename Kokkos::Impl::MirrorDynamicViewType::view_type create_mirror(Kokkos::Impl::WithoutInitializing_t wi, const Space&, const Kokkos::Experimental::DynamicView& src) { @@ -687,7 +672,11 @@ create_mirror(Kokkos::Impl::WithoutInitializing_t wi, const Space&, src, Kokkos::view_alloc(wi, typename Space::memory_space{})); } -template +// public interface that accepts arbitrary view constructor args passed by a +// view_alloc +template ::specialize>>> inline auto create_mirror( const Impl::ViewCtorProp& arg_prop, const Kokkos::Experimental::DynamicView& src) { @@ -696,76 +685,56 @@ inline auto create_mirror( namespace Impl { +// create a mirror view +// private interface that accepts arbitrary view constructor args passed by a +// view_alloc template -inline std::enable_if_t< - !Impl::ViewCtorProp::has_memory_space && - (std::is_same< - typename Kokkos::Experimental::DynamicView::memory_space, - typename Kokkos::Experimental::DynamicView< - T, P...>::HostMirror::memory_space>::value && - std::is_same< - typename Kokkos::Experimental::DynamicView::data_type, - typename Kokkos::Experimental::DynamicView< - T, P...>::HostMirror::data_type>::value), - typename Kokkos::Experimental::DynamicView::HostMirror> -create_mirror_view(const Kokkos::Experimental::DynamicView& src, - const Impl::ViewCtorProp&) { - return src; +inline auto create_mirror_view( + const Kokkos::Experimental::DynamicView& src, + [[maybe_unused]] const Impl::ViewCtorProp& arg_prop) { + if constexpr (!Impl::ViewCtorProp::has_memory_space) { + if constexpr (std::is_same::memory_space, + typename Kokkos::Experimental::DynamicView< + T, P...>::HostMirror::memory_space>::value && + std::is_same::data_type, + typename Kokkos::Experimental::DynamicView< + T, P...>::HostMirror::data_type>::value) { + return + typename Kokkos::Experimental::DynamicView::HostMirror(src); + } else { + return Kokkos::Impl::choose_create_mirror(src, arg_prop); + } + } else { + if constexpr (Impl::MirrorDynamicViewType< + typename Impl::ViewCtorProp< + ViewCtorArgs...>::memory_space, + T, P...>::is_same_memspace) { + return typename Impl::MirrorDynamicViewType< + typename Impl::ViewCtorProp::memory_space, T, + P...>::view_type(src); + } else { + return Kokkos::Impl::choose_create_mirror(src, arg_prop); + } + } +#if defined(KOKKOS_COMPILER_INTEL) || \ + (defined(KOKKOS_COMPILER_NVCC) && KOKKOS_COMPILER_NVCC >= 1130 && \ + !defined(KOKKOS_COMPILER_MSVC)) + __builtin_unreachable(); +#endif } -template -inline std::enable_if_t< - !Impl::ViewCtorProp::has_memory_space && - !(std::is_same< - typename Kokkos::Experimental::DynamicView::memory_space, - typename Kokkos::Experimental::DynamicView< - T, P...>::HostMirror::memory_space>::value && - std::is_same< - typename Kokkos::Experimental::DynamicView::data_type, - typename Kokkos::Experimental::DynamicView< - T, P...>::HostMirror::data_type>::value), - typename Kokkos::Experimental::DynamicView::HostMirror> -create_mirror_view(const Kokkos::Experimental::DynamicView& src, - const Impl::ViewCtorProp& arg_prop) { - return Kokkos::create_mirror(arg_prop, src); -} - -template ::has_memory_space>> -std::enable_if_t::memory_space, - T, P...>::is_same_memspace, - typename Impl::MirrorDynamicViewType< - typename Impl::ViewCtorProp::memory_space, - T, P...>::view_type> -create_mirror_view(const Kokkos::Experimental::DynamicView& src, - const Impl::ViewCtorProp&) { - return src; -} - -template ::has_memory_space>> -std::enable_if_t::memory_space, - T, P...>::is_same_memspace, - typename Impl::MirrorDynamicViewType< - typename Impl::ViewCtorProp::memory_space, - T, P...>::view_type> -create_mirror_view(const Kokkos::Experimental::DynamicView& src, - const Impl::ViewCtorProp& arg_prop) { - return Kokkos::Impl::create_mirror(src, arg_prop); -} } // namespace Impl -// Create a mirror view in host space +// public interface template inline auto create_mirror_view( const typename Kokkos::Experimental::DynamicView& src) { return Impl::create_mirror_view(src, Impl::ViewCtorProp<>{}); } +// public interface that accepts a without initializing flag template inline auto create_mirror_view( Kokkos::Impl::WithoutInitializing_t wi, @@ -773,15 +742,18 @@ inline auto create_mirror_view( return Impl::create_mirror_view(src, Kokkos::view_alloc(wi)); } -// Create a mirror in a new space -template +// public interface that accepts a space +template ::value>> inline auto create_mirror_view( const Space&, const Kokkos::Experimental::DynamicView& src) { return Impl::create_mirror_view(src, view_alloc(typename Space::memory_space{})); } -template +// public interface that accepts a space and a without initializing flag +template ::value>> inline auto create_mirror_view( Kokkos::Impl::WithoutInitializing_t wi, const Space&, const Kokkos::Experimental::DynamicView& src) { @@ -789,6 +761,8 @@ inline auto create_mirror_view( src, Kokkos::view_alloc(wi, typename Space::memory_space{})); } +// public interface that accepts arbitrary view constructor args passed by a +// view_alloc template inline auto create_mirror_view( const Impl::ViewCtorProp& arg_prop, @@ -985,80 +959,57 @@ struct ViewCopy, } // namespace Impl -template +// create a mirror view and deep copy it +// public interface that accepts arbitrary view constructor args passed by a +// view_alloc +template ::specialize>::value>> auto create_mirror_view_and_copy( - const Impl::ViewCtorProp&, - const Kokkos::Experimental::DynamicView& src, - std::enable_if_t< - std::is_void::specialize>::value && - Impl::MirrorDynamicViewType< - typename Impl::ViewCtorProp::memory_space, T, - P...>::is_same_memspace>* = nullptr) { + [[maybe_unused]] const Impl::ViewCtorProp& arg_prop, + const Kokkos::Experimental::DynamicView& src) { using alloc_prop_input = Impl::ViewCtorProp; - static_assert( - alloc_prop_input::has_memory_space, - "The view constructor arguments passed to " - "Kokkos::create_mirror_view_and_copy must include a memory space!"); - static_assert(!alloc_prop_input::has_pointer, - "The view constructor arguments passed to " - "Kokkos::create_mirror_view_and_copy must " - "not include a pointer!"); - static_assert(!alloc_prop_input::allow_padding, - "The view constructor arguments passed to " - "Kokkos::create_mirror_view_and_copy must " - "not explicitly allow padding!"); - // same behavior as deep_copy(src, src) - if (!alloc_prop_input::has_execution_space) - fence( - "Kokkos::create_mirror_view_and_copy: fence before returning src view"); - return src; + Impl::check_view_ctor_args_create_mirror_view_and_copy(); + + if constexpr (Impl::MirrorDynamicViewType< + typename Impl::ViewCtorProp::memory_space, + T, P...>::is_same_memspace) { + // same behavior as deep_copy(src, src) + if constexpr (!alloc_prop_input::has_execution_space) + fence( + "Kokkos::create_mirror_view_and_copy: fence before returning src " + "view"); + return src; + } else { + using Space = typename alloc_prop_input::memory_space; + using Mirror = + typename Impl::MirrorDynamicViewType::view_type; + + auto arg_prop_copy = Impl::with_properties_if_unset( + arg_prop, std::string{}, WithoutInitializing, + typename Space::execution_space{}); + + std::string& label = Impl::get_property(arg_prop_copy); + if (label.empty()) label = src.label(); + auto mirror = typename Mirror::non_const_type( + arg_prop_copy, src.chunk_size(), src.chunk_max() * src.chunk_size()); + mirror.resize_serial(src.extent(0)); + if constexpr (alloc_prop_input::has_execution_space) { + deep_copy(Impl::get_property(arg_prop_copy), + mirror, src); + } else + deep_copy(mirror, src); + return mirror; + } +#if defined(KOKKOS_COMPILER_NVCC) && KOKKOS_COMPILER_NVCC >= 1130 && \ + !defined(KOKKOS_COMPILER_MSVC) + __builtin_unreachable(); +#endif } -template -auto create_mirror_view_and_copy( - const Impl::ViewCtorProp& arg_prop, - const Kokkos::Experimental::DynamicView& src, - std::enable_if_t< - std::is_void::specialize>::value && - !Impl::MirrorDynamicViewType< - typename Impl::ViewCtorProp::memory_space, T, - P...>::is_same_memspace>* = nullptr) { - using alloc_prop_input = Impl::ViewCtorProp; - static_assert( - alloc_prop_input::has_memory_space, - "The view constructor arguments passed to " - "Kokkos::create_mirror_view_and_copy must include a memory space!"); - static_assert(!alloc_prop_input::has_pointer, - "The view constructor arguments passed to " - "Kokkos::create_mirror_view_and_copy must " - "not include a pointer!"); - static_assert(!alloc_prop_input::allow_padding, - "The view constructor arguments passed to " - "Kokkos::create_mirror_view_and_copy must " - "not explicitly allow padding!"); - using Space = typename alloc_prop_input::memory_space; - using Mirror = - typename Impl::MirrorDynamicViewType::view_type; - - auto arg_prop_copy = Impl::with_properties_if_unset( - arg_prop, std::string{}, WithoutInitializing, - typename Space::execution_space{}); - - std::string& label = Impl::get_property(arg_prop_copy); - if (label.empty()) label = src.label(); - auto mirror = typename Mirror::non_const_type( - arg_prop_copy, src.chunk_size(), src.chunk_max() * src.chunk_size()); - mirror.resize_serial(src.extent(0)); - if constexpr (alloc_prop_input::has_execution_space) { - deep_copy(Impl::get_property(arg_prop_copy), - mirror, src); - } else - deep_copy(mirror, src); - return mirror; -} - -template +template ::value>> auto create_mirror_view_and_copy( const Space&, const Kokkos::Experimental::DynamicView& src, std::string const& name = "") { diff --git a/lib/kokkos/containers/src/Kokkos_OffsetView.hpp b/lib/kokkos/containers/src/Kokkos_OffsetView.hpp index 91a7e4a927..3adc70b190 100644 --- a/lib/kokkos/containers/src/Kokkos_OffsetView.hpp +++ b/lib/kokkos/containers/src/Kokkos_OffsetView.hpp @@ -471,62 +471,31 @@ class OffsetView : public ViewTraits { template KOKKOS_FORCEINLINE_FUNCTION std::enable_if_t< (Kokkos::Impl::are_integral::value && (2 == Rank) && - is_default_map && is_layout_left && (traits::rank_dynamic == 0)), + is_default_map && + (is_layout_left || is_layout_right || is_layout_stride)), reference_type> operator()(const I0& i0, const I1& i1) const { KOKKOS_IMPL_OFFSETVIEW_OPERATOR_VERIFY((m_track, m_map, m_begins, i0, i1)) const size_t j0 = i0 - m_begins[0]; const size_t j1 = i1 - m_begins[1]; - return m_map.m_impl_handle[j0 + m_map.m_impl_offset.m_dim.N0 * j1]; - } - - template - KOKKOS_FORCEINLINE_FUNCTION std::enable_if_t< - (Kokkos::Impl::are_integral::value && (2 == Rank) && - is_default_map && is_layout_left && (traits::rank_dynamic != 0)), - reference_type> - operator()(const I0& i0, const I1& i1) const { - KOKKOS_IMPL_OFFSETVIEW_OPERATOR_VERIFY((m_track, m_map, m_begins, i0, i1)) - const size_t j0 = i0 - m_begins[0]; - const size_t j1 = i1 - m_begins[1]; - return m_map.m_impl_handle[j0 + m_map.m_impl_offset.m_stride * j1]; - } - - template - KOKKOS_FORCEINLINE_FUNCTION std::enable_if_t< - (Kokkos::Impl::are_integral::value && (2 == Rank) && - is_default_map && is_layout_right && (traits::rank_dynamic == 0)), - reference_type> - operator()(const I0& i0, const I1& i1) const { - KOKKOS_IMPL_OFFSETVIEW_OPERATOR_VERIFY((m_track, m_map, m_begins, i0, i1)) - const size_t j0 = i0 - m_begins[0]; - const size_t j1 = i1 - m_begins[1]; - return m_map.m_impl_handle[j1 + m_map.m_impl_offset.m_dim.N1 * j0]; - } - - template - KOKKOS_FORCEINLINE_FUNCTION std::enable_if_t< - (Kokkos::Impl::are_integral::value && (2 == Rank) && - is_default_map && is_layout_right && (traits::rank_dynamic != 0)), - reference_type> - operator()(const I0& i0, const I1& i1) const { - KOKKOS_IMPL_OFFSETVIEW_OPERATOR_VERIFY((m_track, m_map, m_begins, i0, i1)) - const size_t j0 = i0 - m_begins[0]; - const size_t j1 = i1 - m_begins[1]; - return m_map.m_impl_handle[j1 + m_map.m_impl_offset.m_stride * j0]; - } - - template - KOKKOS_FORCEINLINE_FUNCTION - std::enable_if_t<(Kokkos::Impl::are_integral::value && - (2 == Rank) && is_default_map && is_layout_stride), - reference_type> - operator()(const I0& i0, const I1& i1) const { - KOKKOS_IMPL_OFFSETVIEW_OPERATOR_VERIFY((m_track, m_map, m_begins, i0, i1)) - const size_t j0 = i0 - m_begins[0]; - const size_t j1 = i1 - m_begins[1]; - return m_map.m_impl_handle[j0 * m_map.m_impl_offset.m_stride.S0 + - j1 * m_map.m_impl_offset.m_stride.S1]; + if constexpr (is_layout_left) { + if constexpr (traits::rank_dynamic == 0) + return m_map.m_impl_handle[j0 + m_map.m_impl_offset.m_dim.N0 * j1]; + else + return m_map.m_impl_handle[j0 + m_map.m_impl_offset.m_stride * j1]; + } else if constexpr (is_layout_right) { + if constexpr (traits::rank_dynamic == 0) + return m_map.m_impl_handle[j1 + m_map.m_impl_offset.m_dim.N1 * j0]; + else + return m_map.m_impl_handle[j1 + m_map.m_impl_offset.m_stride * j0]; + } else { + static_assert(is_layout_stride); + return m_map.m_impl_handle[j0 * m_map.m_impl_offset.m_stride.S0 + + j1 * m_map.m_impl_offset.m_stride.S1]; + } +#if defined(KOKKOS_COMPILER_INTEL) + __builtin_unreachable(); +#endif } //------------------------------ @@ -1841,71 +1810,73 @@ struct MirrorOffsetType { } // namespace Impl namespace Impl { -template -inline std::enable_if_t< - !Impl::ViewCtorProp::has_memory_space, - typename Kokkos::Experimental::OffsetView::HostMirror> -create_mirror(const Kokkos::Experimental::OffsetView& src, - const Impl::ViewCtorProp& arg_prop) { - return typename Kokkos::Experimental::OffsetView::HostMirror( - Kokkos::create_mirror(arg_prop, src.view()), src.begins()); -} -template ::has_memory_space>> +// create a mirror +// private interface that accepts arbitrary view constructor args passed by a +// view_alloc +template inline auto create_mirror(const Kokkos::Experimental::OffsetView& src, const Impl::ViewCtorProp& arg_prop) { - using alloc_prop_input = Impl::ViewCtorProp; - using Space = typename Impl::ViewCtorProp::memory_space; + check_view_ctor_args_create_mirror(); - static_assert( - !alloc_prop_input::has_label, - "The view constructor arguments passed to Kokkos::create_mirror " - "must not include a label!"); - static_assert( - !alloc_prop_input::has_pointer, - "The view constructor arguments passed to Kokkos::create_mirror must " - "not include a pointer!"); - static_assert( - !alloc_prop_input::allow_padding, - "The view constructor arguments passed to Kokkos::create_mirror must " - "not explicitly allow padding!"); + if constexpr (Impl::ViewCtorProp::has_memory_space) { + using Space = typename Impl::ViewCtorProp::memory_space; - auto prop_copy = Impl::with_properties_if_unset( - arg_prop, std::string(src.label()).append("_mirror")); + auto prop_copy = Impl::with_properties_if_unset( + arg_prop, std::string(src.label()).append("_mirror")); - return typename Kokkos::Impl::MirrorOffsetType::view_type( - prop_copy, src.layout(), - {src.begin(0), src.begin(1), src.begin(2), src.begin(3), src.begin(4), - src.begin(5), src.begin(6), src.begin(7)}); + return typename Kokkos::Impl::MirrorOffsetType::view_type( + prop_copy, src.layout(), + {src.begin(0), src.begin(1), src.begin(2), src.begin(3), src.begin(4), + src.begin(5), src.begin(6), src.begin(7)}); + } else { + return typename Kokkos::Experimental::OffsetView::HostMirror( + Kokkos::create_mirror(arg_prop, src.view()), src.begins()); + } +#if defined(KOKKOS_COMPILER_INTEL) || \ + (defined(KOKKOS_COMPILER_NVCC) && KOKKOS_COMPILER_NVCC >= 1130 && \ + !defined(KOKKOS_COMPILER_MSVC)) + __builtin_unreachable(); +#endif } + } // namespace Impl -// Create a mirror in host space -template +// public interface +template ::specialize>>> inline auto create_mirror( const Kokkos::Experimental::OffsetView& src) { return Impl::create_mirror(src, Impl::ViewCtorProp<>{}); } -template +// public interface that accepts a without initializing flag +template ::specialize>>> inline auto create_mirror( Kokkos::Impl::WithoutInitializing_t wi, const Kokkos::Experimental::OffsetView& src) { return Impl::create_mirror(src, Kokkos::view_alloc(wi)); } -// Create a mirror in a new space +// public interface that accepts a space template ::value>> + typename Enable = std::enable_if_t< + Kokkos::is_space::value && + std::is_void_v::specialize>>> inline auto create_mirror( const Space&, const Kokkos::Experimental::OffsetView& src) { return Impl::create_mirror( src, Kokkos::view_alloc(typename Space::memory_space{})); } -template +// public interface that accepts a space and a without initializing flag +template ::value && + std::is_void_v::specialize>>> typename Kokkos::Impl::MirrorOffsetType::view_type create_mirror(Kokkos::Impl::WithoutInitializing_t wi, const Space&, const Kokkos::Experimental::OffsetView& src) { @@ -1913,7 +1884,11 @@ create_mirror(Kokkos::Impl::WithoutInitializing_t wi, const Space&, src, Kokkos::view_alloc(typename Space::memory_space{}, wi)); } -template +// public interface that accepts arbitrary view constructor args passed by a +// view_alloc +template ::specialize>>> inline auto create_mirror( const Impl::ViewCtorProp& arg_prop, const Kokkos::Experimental::OffsetView& src) { @@ -1921,76 +1896,56 @@ inline auto create_mirror( } namespace Impl { + +// create a mirror view +// private interface that accepts arbitrary view constructor args passed by a +// view_alloc template -inline std::enable_if_t< - !Impl::ViewCtorProp::has_memory_space && - (std::is_same< - typename Kokkos::Experimental::OffsetView::memory_space, - typename Kokkos::Experimental::OffsetView< - T, P...>::HostMirror::memory_space>::value && - std::is_same< - typename Kokkos::Experimental::OffsetView::data_type, - typename Kokkos::Experimental::OffsetView< - T, P...>::HostMirror::data_type>::value), - typename Kokkos::Experimental::OffsetView::HostMirror> -create_mirror_view(const Kokkos::Experimental::OffsetView& src, - const Impl::ViewCtorProp&) { - return src; +inline auto create_mirror_view( + const Kokkos::Experimental::OffsetView& src, + [[maybe_unused]] const Impl::ViewCtorProp& arg_prop) { + if constexpr (!Impl::ViewCtorProp::has_memory_space) { + if constexpr (std::is_same::memory_space, + typename Kokkos::Experimental::OffsetView< + T, P...>::HostMirror::memory_space>::value && + std::is_same::data_type, + typename Kokkos::Experimental::OffsetView< + T, P...>::HostMirror::data_type>::value) { + return + typename Kokkos::Experimental::OffsetView::HostMirror(src); + } else { + return Kokkos::Impl::choose_create_mirror(src, arg_prop); + } + } else { + if constexpr (Impl::MirrorOffsetViewType::memory_space, + T, P...>::is_same_memspace) { + return typename Impl::MirrorOffsetViewType< + typename Impl::ViewCtorProp::memory_space, T, + P...>::view_type(src); + } else { + return Kokkos::Impl::choose_create_mirror(src, arg_prop); + } + } +#if defined(KOKKOS_COMPILER_INTEL) || \ + (defined(KOKKOS_COMPILER_NVCC) && KOKKOS_COMPILER_NVCC >= 1130 && \ + !defined(KOKKOS_COMPILER_MSVC)) + __builtin_unreachable(); +#endif } -template -inline std::enable_if_t< - !Impl::ViewCtorProp::has_memory_space && - !(std::is_same< - typename Kokkos::Experimental::OffsetView::memory_space, - typename Kokkos::Experimental::OffsetView< - T, P...>::HostMirror::memory_space>::value && - std::is_same< - typename Kokkos::Experimental::OffsetView::data_type, - typename Kokkos::Experimental::OffsetView< - T, P...>::HostMirror::data_type>::value), - typename Kokkos::Experimental::OffsetView::HostMirror> -create_mirror_view(const Kokkos::Experimental::OffsetView& src, - const Impl::ViewCtorProp& arg_prop) { - return Kokkos::create_mirror(arg_prop, src); -} - -template ::has_memory_space>> -std::enable_if_t::memory_space, - T, P...>::is_same_memspace, - typename Impl::MirrorOffsetViewType< - typename Impl::ViewCtorProp::memory_space, - T, P...>::view_type> -create_mirror_view(const Kokkos::Experimental::OffsetView& src, - const Impl::ViewCtorProp&) { - return src; -} - -template ::has_memory_space>> -std::enable_if_t::memory_space, - T, P...>::is_same_memspace, - typename Impl::MirrorOffsetViewType< - typename Impl::ViewCtorProp::memory_space, - T, P...>::view_type> -create_mirror_view(const Kokkos::Experimental::OffsetView& src, - const Impl::ViewCtorProp& arg_prop) { - return Kokkos::Impl::create_mirror(src, arg_prop); -} } // namespace Impl -// Create a mirror view in host space +// public interface template inline auto create_mirror_view( const typename Kokkos::Experimental::OffsetView& src) { return Impl::create_mirror_view(src, Impl::ViewCtorProp<>{}); } +// public interface that accepts a without initializing flag template inline auto create_mirror_view( Kokkos::Impl::WithoutInitializing_t wi, @@ -1998,7 +1953,7 @@ inline auto create_mirror_view( return Impl::create_mirror_view(src, Kokkos::view_alloc(wi)); } -// Create a mirror view in a new space +// public interface that accepts a space template ::value>> inline auto create_mirror_view( @@ -2007,7 +1962,9 @@ inline auto create_mirror_view( src, Kokkos::view_alloc(typename Space::memory_space{})); } -template +// public interface that accepts a space and a without initializing flag +template ::value>> inline auto create_mirror_view( Kokkos::Impl::WithoutInitializing_t wi, const Space&, const Kokkos::Experimental::OffsetView& src) { @@ -2015,6 +1972,8 @@ inline auto create_mirror_view( src, Kokkos::view_alloc(typename Space::memory_space{}, wi)); } +// public interface that accepts arbitrary view constructor args passed by a +// view_alloc template inline auto create_mirror_view( const Impl::ViewCtorProp& arg_prop, @@ -2022,7 +1981,9 @@ inline auto create_mirror_view( return Impl::create_mirror_view(src, arg_prop); } -// Create a mirror view and deep_copy in a new space +// create a mirror view and deep copy it +// public interface that accepts arbitrary view constructor args passed by a +// view_alloc template typename Kokkos::Impl::MirrorOffsetViewType< typename Impl::ViewCtorProp::memory_space, T, diff --git a/lib/kokkos/containers/src/Kokkos_UnorderedMap.hpp b/lib/kokkos/containers/src/Kokkos_UnorderedMap.hpp index 78a6a238ec..c3a8b67df8 100644 --- a/lib/kokkos/containers/src/Kokkos_UnorderedMap.hpp +++ b/lib/kokkos/containers/src/Kokkos_UnorderedMap.hpp @@ -805,56 +805,94 @@ class UnorderedMap { return *this; } + // Re-allocate the views of the calling UnorderedMap according to src + // capacity, and deep copy the src data. template std::enable_if_t, key_type>::value && std::is_same, value_type>::value> create_copy_view( UnorderedMap const &src) { if (m_hash_lists.data() != src.m_hash_lists.data()) { - insertable_map_type tmp; + allocate_view(src); + deep_copy_view(src); + } + } - tmp.m_bounded_insert = src.m_bounded_insert; - tmp.m_hasher = src.m_hasher; - tmp.m_equal_to = src.m_equal_to; - tmp.m_size() = src.m_size(); - tmp.m_available_indexes = bitset_type(src.capacity()); - tmp.m_hash_lists = size_type_view( - view_alloc(WithoutInitializing, "UnorderedMap hash list"), - src.m_hash_lists.extent(0)); - tmp.m_next_index = size_type_view( - view_alloc(WithoutInitializing, "UnorderedMap next index"), - src.m_next_index.extent(0)); - tmp.m_keys = - key_type_view(view_alloc(WithoutInitializing, "UnorderedMap keys"), - src.m_keys.extent(0)); - tmp.m_values = value_type_view( - view_alloc(WithoutInitializing, "UnorderedMap values"), - src.m_values.extent(0)); - tmp.m_scalars = scalars_view("UnorderedMap scalars"); + // Allocate views of the calling UnorderedMap with the same capacity as the + // src. + template + std::enable_if_t, key_type>::value && + std::is_same, value_type>::value> + allocate_view( + UnorderedMap const &src) { + insertable_map_type tmp; - Kokkos::deep_copy(tmp.m_available_indexes, src.m_available_indexes); + tmp.m_bounded_insert = src.m_bounded_insert; + tmp.m_hasher = src.m_hasher; + tmp.m_equal_to = src.m_equal_to; + tmp.m_size() = src.m_size(); + tmp.m_available_indexes = bitset_type(src.capacity()); + tmp.m_hash_lists = size_type_view( + view_alloc(WithoutInitializing, "UnorderedMap hash list"), + src.m_hash_lists.extent(0)); + tmp.m_next_index = size_type_view( + view_alloc(WithoutInitializing, "UnorderedMap next index"), + src.m_next_index.extent(0)); + tmp.m_keys = + key_type_view(view_alloc(WithoutInitializing, "UnorderedMap keys"), + src.m_keys.extent(0)); + tmp.m_values = + value_type_view(view_alloc(WithoutInitializing, "UnorderedMap values"), + src.m_values.extent(0)); + tmp.m_scalars = scalars_view("UnorderedMap scalars"); + + *this = tmp; + } + + // Deep copy view data from src. This requires that the src capacity is + // identical to the capacity of the calling UnorderedMap. + template + std::enable_if_t, key_type>::value && + std::is_same, value_type>::value> + deep_copy_view( + UnorderedMap const &src) { +#ifndef KOKKOS_ENABLE_DEPRECATED_CODE_4 + // To deep copy UnorderedMap, capacity must be identical + KOKKOS_EXPECTS(capacity() == src.capacity()); +#else + if (capacity() != src.capacity()) { + allocate_view(src); +#ifdef KOKKOS_ENABLE_DEPRECATION_WARNINGS + Kokkos::Impl::log_warning( + "Warning: deep_copy_view() allocating views is deprecated. Must call " + "with UnorderedMaps of identical capacity, or use " + "create_copy_view().\n"); +#endif + } +#endif + + if (m_hash_lists.data() != src.m_hash_lists.data()) { + Kokkos::deep_copy(m_available_indexes, src.m_available_indexes); using raw_deep_copy = Kokkos::Impl::DeepCopy; - raw_deep_copy(tmp.m_hash_lists.data(), src.m_hash_lists.data(), + raw_deep_copy(m_hash_lists.data(), src.m_hash_lists.data(), sizeof(size_type) * src.m_hash_lists.extent(0)); - raw_deep_copy(tmp.m_next_index.data(), src.m_next_index.data(), + raw_deep_copy(m_next_index.data(), src.m_next_index.data(), sizeof(size_type) * src.m_next_index.extent(0)); - raw_deep_copy(tmp.m_keys.data(), src.m_keys.data(), + raw_deep_copy(m_keys.data(), src.m_keys.data(), sizeof(key_type) * src.m_keys.extent(0)); if (!is_set) { - raw_deep_copy(tmp.m_values.data(), src.m_values.data(), + raw_deep_copy(m_values.data(), src.m_values.data(), sizeof(impl_value_type) * src.m_values.extent(0)); } - raw_deep_copy(tmp.m_scalars.data(), src.m_scalars.data(), + raw_deep_copy(m_scalars.data(), src.m_scalars.data(), sizeof(int) * num_scalars); Kokkos::fence( - "Kokkos::UnorderedMap::create_copy_view: fence after copy to tmp"); - - *this = tmp; + "Kokkos::UnorderedMap::deep_copy_view: fence after copy to dst."); } } @@ -932,13 +970,25 @@ class UnorderedMap { friend struct Impl::UnorderedMapPrint; }; -// Specialization of deep_copy for two UnorderedMap objects. +// Specialization of deep_copy() for two UnorderedMap objects. template inline void deep_copy( UnorderedMap &dst, const UnorderedMap &src) { - dst.create_copy_view(src); + dst.deep_copy_view(src); +} + +// Specialization of create_mirror() for an UnorderedMap object. +template +typename UnorderedMap::HostMirror +create_mirror( + const UnorderedMap &src) { + typename UnorderedMap::HostMirror + dst; + dst.allocate_view(src); + return dst; } } // namespace Kokkos diff --git a/lib/kokkos/containers/unit_tests/TestDualView.hpp b/lib/kokkos/containers/unit_tests/TestDualView.hpp index a15e5fa299..2512cb5c49 100644 --- a/lib/kokkos/containers/unit_tests/TestDualView.hpp +++ b/lib/kokkos/containers/unit_tests/TestDualView.hpp @@ -55,8 +55,8 @@ struct test_dualview_alloc { bool result = false; test_dualview_alloc(unsigned int size) { - result = run_me >( - size, 3); + result = + run_me>(size, 3); } }; @@ -154,7 +154,7 @@ struct test_dualview_combinations { } test_dualview_combinations(unsigned int size, bool with_init) { - result = run_me >( + result = run_me>( size, 3, with_init); } }; @@ -253,21 +253,18 @@ struct test_dual_view_deep_copy { } // end run_me test_dual_view_deep_copy() { - run_me >(10, 5, - true); - run_me >(10, 5, - false); + run_me>(10, 5, true); + run_me>(10, 5, + false); // Test zero length but allocated (a.d_view.data!=nullptr but // a.d_view.span()==0) - run_me >(0, 5, true); - run_me >(0, 5, - false); + run_me>(0, 5, true); + run_me>(0, 5, false); // Test default constructed view - run_me >(-1, 5, - true); - run_me >(-1, 5, - false); + run_me>(-1, 5, true); + run_me>(-1, 5, + false); } }; @@ -282,15 +279,20 @@ struct test_dualview_resize { const unsigned int m = 5; const unsigned int factor = 2; - ViewType a("A", n, m); + ViewType a; + if constexpr (Initialize) + a = ViewType("A", n, m); + else + a = ViewType(Kokkos::view_alloc(Kokkos::WithoutInitializing, "A"), n, m); + Kokkos::deep_copy(a.d_view, 1); /* Covers case "Resize on Device" */ a.modify_device(); - if (Initialize) - Kokkos::resize(Kokkos::WithoutInitializing, a, factor * n, factor * m); - else + if constexpr (Initialize) Kokkos::resize(a, factor * n, factor * m); + else + Kokkos::resize(Kokkos::WithoutInitializing, a, factor * n, factor * m); ASSERT_EQ(a.extent(0), n * factor); ASSERT_EQ(a.extent(1), m * factor); @@ -298,33 +300,38 @@ struct test_dualview_resize { a.sync_host(); // Check device view is initialized as expected - scalar_type a_d_sum = 0; // Execute on the execution_space associated with t_dev's memory space using t_dev_exec_space = typename ViewType::t_dev::memory_space::execution_space; - Kokkos::parallel_reduce( - Kokkos::RangePolicy(0, a.d_view.extent(0)), - SumViewEntriesFunctor(a.d_view), - a_d_sum); + Kokkos::View errors_d( + "errors"); + Kokkos::parallel_for( + Kokkos::MDRangePolicy>( + {0, 0}, {a.d_view.extent(0), a.d_view.extent(1)}), + KOKKOS_LAMBDA(int i, int j) { + if (a.d_view(i, j) != 1) Kokkos::atomic_inc(errors_d.data()); + }); + int errors_d_scalar; + Kokkos::deep_copy(errors_d_scalar, errors_d); // Check host view is synced as expected - scalar_type a_h_sum = 0; + int errors_h_scalar = 0; for (size_t i = 0; i < a.h_view.extent(0); ++i) for (size_t j = 0; j < a.h_view.extent(1); ++j) { - a_h_sum += a.h_view(i, j); + if (a.h_view(i, j) != 1) ++errors_h_scalar; } // Check - ASSERT_EQ(a_h_sum, a_d_sum); - ASSERT_EQ(a_h_sum, scalar_type(a.extent(0) * a.extent(1))); + ASSERT_EQ(errors_d_scalar, 0); + ASSERT_EQ(errors_h_scalar, 0); /* Covers case "Resize on Host" */ a.modify_host(); - if (Initialize) - Kokkos::resize(Kokkos::WithoutInitializing, a, n / factor, m / factor); - else + if constexpr (Initialize) Kokkos::resize(a, n / factor, m / factor); + else + Kokkos::resize(Kokkos::WithoutInitializing, a, n / factor, m / factor); ASSERT_EQ(a.extent(0), n / factor); ASSERT_EQ(a.extent(1), m / factor); @@ -332,30 +339,33 @@ struct test_dualview_resize { a.sync_device(Kokkos::DefaultExecutionSpace{}); // Check device view is initialized as expected - a_d_sum = 0; + Kokkos::deep_copy(errors_d, 0); // Execute on the execution_space associated with t_dev's memory space using t_dev_exec_space = typename ViewType::t_dev::memory_space::execution_space; - Kokkos::parallel_reduce( - Kokkos::RangePolicy(0, a.d_view.extent(0)), - SumViewEntriesFunctor(a.d_view), - a_d_sum); + Kokkos::parallel_for( + Kokkos::MDRangePolicy>( + {0, 0}, {a.d_view.extent(0), a.d_view.extent(1)}), + KOKKOS_LAMBDA(int i, int j) { + if (a.d_view(i, j) != 1) Kokkos::atomic_inc(errors_d.data()); + }); + Kokkos::deep_copy(errors_d_scalar, errors_d); // Check host view is synced as expected - a_h_sum = 0; + errors_h_scalar = 0; for (size_t i = 0; i < a.h_view.extent(0); ++i) for (size_t j = 0; j < a.h_view.extent(1); ++j) { - a_h_sum += a.h_view(i, j); + if (a.h_view(i, j) != 1) ++errors_h_scalar; } // Check - ASSERT_EQ(a_h_sum, scalar_type(a.extent(0) * a.extent(1))); - ASSERT_EQ(a_h_sum, a_d_sum); + ASSERT_EQ(errors_d_scalar, 0); + ASSERT_EQ(errors_h_scalar, 0); } // end run_me test_dualview_resize() { - run_me >(); + run_me>(); } }; @@ -369,40 +379,51 @@ struct test_dualview_realloc { const unsigned int n = 10; const unsigned int m = 5; - ViewType a("A", n, m); - if (Initialize) - Kokkos::realloc(Kokkos::WithoutInitializing, a, n, m); - else + ViewType a; + if constexpr (Initialize) { + a = ViewType("A", n, m); Kokkos::realloc(a, n, m); + } else { + a = ViewType(Kokkos::view_alloc(Kokkos::WithoutInitializing, "A"), n, m); + Kokkos::realloc(Kokkos::WithoutInitializing, a, n, m); + } + ASSERT_EQ(a.extent(0), n); + ASSERT_EQ(a.extent(1), m); Kokkos::deep_copy(a.d_view, 1); + a.modify_device(); a.sync_host(); // Check device view is initialized as expected - scalar_type a_d_sum = 0; // Execute on the execution_space associated with t_dev's memory space using t_dev_exec_space = typename ViewType::t_dev::memory_space::execution_space; - Kokkos::parallel_reduce( - Kokkos::RangePolicy(0, a.d_view.extent(0)), - SumViewEntriesFunctor(a.d_view), - a_d_sum); + Kokkos::View errors_d( + "errors"); + Kokkos::parallel_for( + Kokkos::MDRangePolicy>( + {0, 0}, {a.d_view.extent(0), a.d_view.extent(1)}), + KOKKOS_LAMBDA(int i, int j) { + if (a.d_view(i, j) != 1) Kokkos::atomic_inc(errors_d.data()); + }); + int errors_d_scalar; + Kokkos::deep_copy(errors_d_scalar, errors_d); // Check host view is synced as expected - scalar_type a_h_sum = 0; + int errors_h_scalar = 0; for (size_t i = 0; i < a.h_view.extent(0); ++i) for (size_t j = 0; j < a.h_view.extent(1); ++j) { - a_h_sum += a.h_view(i, j); + if (a.h_view(i, j) != 1) ++errors_h_scalar; } // Check - ASSERT_EQ(a_h_sum, scalar_type(a.extent(0) * a.extent(1))); - ASSERT_EQ(a_h_sum, a_d_sum); + ASSERT_EQ(errors_d_scalar, 0); + ASSERT_EQ(errors_h_scalar, 0); } // end run_me test_dualview_realloc() { - run_me >(); + run_me>(); } }; @@ -463,12 +484,23 @@ TEST(TEST_CATEGORY, dualview_deep_copy) { test_dualview_deep_copy(); } +struct NoDefaultConstructor { + NoDefaultConstructor(int i_) : i(i_) {} + KOKKOS_FUNCTION operator int() const { return i; } + + int i; +}; + TEST(TEST_CATEGORY, dualview_realloc) { test_dualview_realloc(); + Impl::test_dualview_realloc(); } TEST(TEST_CATEGORY, dualview_resize) { test_dualview_resize(); + Impl::test_dualview_resize(); } namespace { diff --git a/lib/kokkos/containers/unit_tests/TestUnorderedMap.hpp b/lib/kokkos/containers/unit_tests/TestUnorderedMap.hpp index f63f1c6afe..4a7e826ecb 100644 --- a/lib/kokkos/containers/unit_tests/TestUnorderedMap.hpp +++ b/lib/kokkos/containers/unit_tests/TestUnorderedMap.hpp @@ -68,7 +68,7 @@ struct TestInsert { } while (rehash_on_fail && failed_count > 0u); // Trigger the m_size mutable bug. - typename map_type::HostMirror map_h; + auto map_h = create_mirror(map); execution_space().fence(); Kokkos::deep_copy(map_h, map); execution_space().fence(); @@ -367,7 +367,7 @@ void test_deep_copy(uint32_t num_nodes) { } } - host_map_type hmap; + auto hmap = create_mirror(map); Kokkos::deep_copy(hmap, map); ASSERT_EQ(map.size(), hmap.size()); @@ -380,6 +380,7 @@ void test_deep_copy(uint32_t num_nodes) { } map_type mmap; + mmap.allocate_view(hmap); Kokkos::deep_copy(mmap, hmap); const_map_type cmap = mmap; @@ -424,7 +425,7 @@ TEST(TEST_CATEGORY, UnorderedMap_valid_empty) { Map n{}; n = Map{m.capacity()}; n.rehash(m.capacity()); - Kokkos::deep_copy(n, m); + n.create_copy_view(m); ASSERT_TRUE(m.is_allocated()); ASSERT_TRUE(n.is_allocated()); } diff --git a/lib/kokkos/containers/unit_tests/TestVector.hpp b/lib/kokkos/containers/unit_tests/TestVector.hpp index a7d341b789..abed2676d7 100644 --- a/lib/kokkos/containers/unit_tests/TestVector.hpp +++ b/lib/kokkos/containers/unit_tests/TestVector.hpp @@ -21,6 +21,8 @@ #include #include #include +#include +KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_PUSH() #include namespace Test { @@ -231,7 +233,7 @@ void test_vector_allocate(unsigned int size) { TEST(TEST_CATEGORY, vector_combination) { test_vector_allocate(10); test_vector_combinations(10); - test_vector_combinations(3057); + test_vector_combinations(3057); } TEST(TEST_CATEGORY, vector_insert) { diff --git a/lib/kokkos/core/perf_test/test_atomic.cpp b/lib/kokkos/core/perf_test/test_atomic.cpp index ce3059f47d..af74723e7e 100644 --- a/lib/kokkos/core/perf_test/test_atomic.cpp +++ b/lib/kokkos/core/perf_test/test_atomic.cpp @@ -390,7 +390,7 @@ static void Test_Atomic(benchmark::State& state) { static constexpr int LOOP = 100'000; -BENCHMARK(Test_Atomic)->Arg(LOOP)->Iterations(10); +BENCHMARK(Test_Atomic)->Arg(30'000)->Iterations(10); BENCHMARK(Test_Atomic)->Arg(LOOP)->Iterations(10); BENCHMARK(Test_Atomic)->Arg(LOOP)->Iterations(10); BENCHMARK(Test_Atomic)->Arg(LOOP)->Iterations(10); @@ -398,4 +398,3 @@ BENCHMARK(Test_Atomic)->Arg(LOOP)->Iterations(10); BENCHMARK(Test_Atomic)->Arg(LOOP)->Iterations(10); BENCHMARK(Test_Atomic)->Arg(LOOP)->Iterations(10); BENCHMARK(Test_Atomic)->Arg(LOOP)->Iterations(10); -BENCHMARK(Test_Atomic)->Arg(LOOP)->Iterations(10); diff --git a/lib/kokkos/core/perf_test/test_atomic_minmax_simple.cpp b/lib/kokkos/core/perf_test/test_atomic_minmax_simple.cpp index b838c8eccf..bc35d1c776 100644 --- a/lib/kokkos/core/perf_test/test_atomic_minmax_simple.cpp +++ b/lib/kokkos/core/perf_test/test_atomic_minmax_simple.cpp @@ -183,7 +183,8 @@ double atomic_contentious_max_replacement(benchmark::State& state, Kokkos::parallel_reduce( con_length, KOKKOS_LAMBDA(const int i, T& inner) { - inner = Kokkos::atomic_max_fetch(&(input(0)), inner + 1); + inner = Kokkos::atomic_max_fetch(&(input(0)), + Kokkos::min(inner, max - 1) + 1); if (i == con_length - 1) { Kokkos::atomic_max_fetch(&(input(0)), max); inner = max; @@ -223,7 +224,8 @@ double atomic_contentious_min_replacement(benchmark::State& state, Kokkos::parallel_reduce( con_length, KOKKOS_LAMBDA(const int i, T& inner) { - inner = Kokkos::atomic_min_fetch(&(input(0)), inner - 1); + inner = Kokkos::atomic_min_fetch(&(input(0)), + Kokkos::max(inner, min + 1) - 1); if (i == con_length - 1) { Kokkos::atomic_min_fetch(&(input(0)), min); inner = min; @@ -246,7 +248,7 @@ static void Atomic_ContentiousMinReplacements(benchmark::State& state) { auto inp = prepare_input(1, std::numeric_limits::max()); for (auto _ : state) { - const auto time = atomic_contentious_max_replacement(state, inp, length); + const auto time = atomic_contentious_min_replacement(state, inp, length); state.SetIterationTime(time); } diff --git a/lib/kokkos/core/src/Cuda/Kokkos_Cuda.hpp b/lib/kokkos/core/src/Cuda/Kokkos_Cuda.hpp index 276d03da26..fd86976d3b 100644 --- a/lib/kokkos/core/src/Cuda/Kokkos_Cuda.hpp +++ b/lib/kokkos/core/src/Cuda/Kokkos_Cuda.hpp @@ -166,8 +166,17 @@ class Cuda { Cuda(); - Cuda(cudaStream_t stream, - Impl::ManageStream manage_stream = Impl::ManageStream::no); + explicit Cuda(cudaStream_t stream) : Cuda(stream, Impl::ManageStream::no) {} + +#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4 + template + KOKKOS_DEPRECATED_WITH_COMMENT( + "Cuda execution space should be constructed explicitly.") + Cuda(cudaStream_t stream) + : Cuda(stream) {} +#endif + + Cuda(cudaStream_t stream, Impl::ManageStream manage_stream); KOKKOS_DEPRECATED Cuda(cudaStream_t stream, bool manage_stream); @@ -186,7 +195,7 @@ class Cuda { /// /// This matches the __CUDA_ARCH__ specification. KOKKOS_DEPRECATED static size_type device_arch() { - const cudaDeviceProp& cudaProp = Cuda().cuda_device_prop(); + const cudaDeviceProp cudaProp = Cuda().cuda_device_prop(); return cudaProp.major * 100 + cudaProp.minor; } diff --git a/lib/kokkos/core/src/Cuda/Kokkos_CudaSpace.cpp b/lib/kokkos/core/src/Cuda/Kokkos_CudaSpace.cpp index 0944937e1b..75318aff77 100644 --- a/lib/kokkos/core/src/Cuda/Kokkos_CudaSpace.cpp +++ b/lib/kokkos/core/src/Cuda/Kokkos_CudaSpace.cpp @@ -59,12 +59,6 @@ const std::unique_ptr &Kokkos::Impl::cuda_get_deep_copy_space( namespace Kokkos { namespace Impl { -namespace { - -static std::atomic num_uvm_allocations(0); - -} // namespace - void DeepCopyCuda(void *dst, const void *src, size_t n) { KOKKOS_IMPL_CUDA_SAFE_CALL((CudaInternal::singleton().cuda_memcpy_wrapper( dst, src, n, cudaMemcpyDefault))); @@ -204,10 +198,7 @@ void *impl_allocate_common(const int device_id, // we should do here since we're turning it into an // exception here cudaGetLastError(); - throw Experimental::CudaRawMemoryAllocationFailure( - arg_alloc_size, error_code, - Experimental::RawMemoryAllocationFailure::AllocationMechanism:: - CudaMalloc); + Kokkos::Impl::throw_bad_alloc(arg_handle.name, arg_alloc_size, arg_label); } if (Kokkos::Profiling::profileLibraryLoaded()) { @@ -252,8 +243,6 @@ void *CudaUVMSpace::impl_allocate( Cuda::impl_static_fence( "Kokkos::CudaUVMSpace::impl_allocate: Pre UVM Allocation"); if (arg_alloc_size > 0) { - Kokkos::Impl::num_uvm_allocations++; - KOKKOS_IMPL_CUDA_SAFE_CALL(cudaSetDevice(m_device)); cudaError_t error_code = cudaMallocManaged(&ptr, arg_alloc_size, cudaMemAttachGlobal); @@ -263,10 +252,7 @@ void *CudaUVMSpace::impl_allocate( // we should do here since we're turning it into an // exception here cudaGetLastError(); - throw Experimental::CudaRawMemoryAllocationFailure( - arg_alloc_size, error_code, - Experimental::RawMemoryAllocationFailure::AllocationMechanism:: - CudaMallocManaged); + Kokkos::Impl::throw_bad_alloc(name(), arg_alloc_size, arg_label); } #ifdef KOKKOS_IMPL_DEBUG_CUDA_PIN_UVM_TO_HOST @@ -307,10 +293,7 @@ void *CudaHostPinnedSpace::impl_allocate( // we should do here since we're turning it into an // exception here cudaGetLastError(); - throw Experimental::CudaRawMemoryAllocationFailure( - arg_alloc_size, error_code, - Experimental::RawMemoryAllocationFailure::AllocationMechanism:: - CudaHostAlloc); + Kokkos::Impl::throw_bad_alloc(name(), arg_alloc_size, arg_label); } if (Kokkos::Profiling::profileLibraryLoaded()) { const size_t reported_size = @@ -341,27 +324,24 @@ void CudaSpace::impl_deallocate( Kokkos::Profiling::deallocateData(arg_handle, arg_label, arg_alloc_ptr, reported_size); } - try { #ifndef CUDART_VERSION #error CUDART_VERSION undefined! #elif (defined(KOKKOS_ENABLE_IMPL_CUDA_MALLOC_ASYNC) && CUDART_VERSION >= 11020) - if (arg_alloc_size >= memory_threshold_g) { - Impl::cuda_device_synchronize( - "Kokkos::Cuda: backend fence before async free"); - KOKKOS_IMPL_CUDA_SAFE_CALL(cudaSetDevice(m_device)); - KOKKOS_IMPL_CUDA_SAFE_CALL(cudaFreeAsync(arg_alloc_ptr, m_stream)); - Impl::cuda_device_synchronize( - "Kokkos::Cuda: backend fence after async free"); - } else { - KOKKOS_IMPL_CUDA_SAFE_CALL(cudaSetDevice(m_device)); - KOKKOS_IMPL_CUDA_SAFE_CALL(cudaFree(arg_alloc_ptr)); - } -#else + if (arg_alloc_size >= memory_threshold_g) { + Impl::cuda_device_synchronize( + "Kokkos::Cuda: backend fence before async free"); + KOKKOS_IMPL_CUDA_SAFE_CALL(cudaSetDevice(m_device)); + KOKKOS_IMPL_CUDA_SAFE_CALL(cudaFreeAsync(arg_alloc_ptr, m_stream)); + Impl::cuda_device_synchronize( + "Kokkos::Cuda: backend fence after async free"); + } else { KOKKOS_IMPL_CUDA_SAFE_CALL(cudaSetDevice(m_device)); KOKKOS_IMPL_CUDA_SAFE_CALL(cudaFree(arg_alloc_ptr)); -#endif - } catch (...) { } +#else + KOKKOS_IMPL_CUDA_SAFE_CALL(cudaSetDevice(m_device)); + KOKKOS_IMPL_CUDA_SAFE_CALL(cudaFree(arg_alloc_ptr)); +#endif } void CudaUVMSpace::deallocate(void *const arg_alloc_ptr, const size_t arg_alloc_size) const { @@ -387,13 +367,9 @@ void CudaUVMSpace::impl_deallocate( Kokkos::Profiling::deallocateData(arg_handle, arg_label, arg_alloc_ptr, reported_size); } - try { - if (arg_alloc_ptr != nullptr) { - Kokkos::Impl::num_uvm_allocations--; - KOKKOS_IMPL_CUDA_SAFE_CALL(cudaSetDevice(m_device)); - KOKKOS_IMPL_CUDA_SAFE_CALL(cudaFree(arg_alloc_ptr)); - } - } catch (...) { + if (arg_alloc_ptr != nullptr) { + KOKKOS_IMPL_CUDA_SAFE_CALL(cudaSetDevice(m_device)); + KOKKOS_IMPL_CUDA_SAFE_CALL(cudaFree(arg_alloc_ptr)); } Cuda::impl_static_fence( "Kokkos::CudaUVMSpace::impl_deallocate: Post UVM Deallocation"); @@ -420,11 +396,8 @@ void CudaHostPinnedSpace::impl_deallocate( Kokkos::Profiling::deallocateData(arg_handle, arg_label, arg_alloc_ptr, reported_size); } - try { - KOKKOS_IMPL_CUDA_SAFE_CALL(cudaSetDevice(m_device)); - KOKKOS_IMPL_CUDA_SAFE_CALL(cudaFreeHost(arg_alloc_ptr)); - } catch (...) { - } + KOKKOS_IMPL_CUDA_SAFE_CALL(cudaSetDevice(m_device)); + KOKKOS_IMPL_CUDA_SAFE_CALL(cudaFreeHost(arg_alloc_ptr)); } } // namespace Kokkos diff --git a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Error.hpp b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Error.hpp index c4458c910c..66656fefda 100644 --- a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Error.hpp +++ b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Error.hpp @@ -22,7 +22,6 @@ #include #include -#include namespace Kokkos { namespace Impl { @@ -69,52 +68,6 @@ inline void cuda_internal_safe_call(cudaError e, const char* name, Kokkos::Impl::cuda_internal_safe_call(call, #call, __FILE__, __LINE__) } // namespace Impl - -namespace Experimental { - -class CudaRawMemoryAllocationFailure : public RawMemoryAllocationFailure { - private: - using base_t = RawMemoryAllocationFailure; - - cudaError_t m_error_code = cudaSuccess; - - static FailureMode get_failure_mode(cudaError_t error_code) { - switch (error_code) { - case cudaErrorMemoryAllocation: return FailureMode::OutOfMemoryError; - case cudaErrorInvalidValue: return FailureMode::InvalidAllocationSize; - // TODO handle cudaErrorNotSupported for cudaMallocManaged - default: return FailureMode::Unknown; - } - } - - public: - // using base_t::base_t; - // would trigger - // - // error: cannot determine the exception specification of the default - // constructor due to a circular dependency - // - // using NVCC 9.1 and gcc 7.4 - CudaRawMemoryAllocationFailure( - size_t arg_attempted_size, size_t arg_attempted_alignment, - FailureMode arg_failure_mode = FailureMode::OutOfMemoryError, - AllocationMechanism arg_mechanism = - AllocationMechanism::StdMalloc) noexcept - : base_t(arg_attempted_size, arg_attempted_alignment, arg_failure_mode, - arg_mechanism) {} - - CudaRawMemoryAllocationFailure(size_t arg_attempted_size, - cudaError_t arg_error_code, - AllocationMechanism arg_mechanism) noexcept - : base_t(arg_attempted_size, /* CudaSpace doesn't handle alignment? */ 1, - get_failure_mode(arg_error_code), arg_mechanism), - m_error_code(arg_error_code) {} - - void append_additional_error_information(std::ostream& o) const override; -}; - -} // end namespace Experimental - } // namespace Kokkos #endif // KOKKOS_ENABLE_CUDA diff --git a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Graph_Impl.hpp b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Graph_Impl.hpp index fcc3ff04ff..625d8c317a 100644 --- a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Graph_Impl.hpp +++ b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Graph_Impl.hpp @@ -72,7 +72,7 @@ struct GraphImpl { GraphNodeImpl; - // Not moveable or copyable; it spends its whole life as a shared_ptr in the + // Not movable or copyable; it spends its whole life as a shared_ptr in the // Graph object GraphImpl() = delete; GraphImpl(GraphImpl const&) = delete; @@ -115,12 +115,9 @@ struct GraphImpl { template // requires NodeImplPtr is a shared_ptr to specialization of GraphNodeImpl - // Also requires that the kernel has the graph node tag in it's policy + // Also requires that the kernel has the graph node tag in its policy void add_node(std::shared_ptr const& arg_node_ptr) { - static_assert( - NodeImpl::kernel_type::Policy::is_graph_kernel::value, - "Something has gone horribly wrong, but it's too complicated to " - "explain here. Buy Daisy a coffee and she'll explain it to you."); + static_assert(NodeImpl::kernel_type::Policy::is_graph_kernel::value); KOKKOS_EXPECTS(bool(arg_node_ptr)); // The Kernel launch from the execute() method has been shimmed to insert // the node into the graph diff --git a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Instance.cpp b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Instance.cpp index 849e8b3b30..89a0002896 100644 --- a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Instance.cpp +++ b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Instance.cpp @@ -737,6 +737,14 @@ namespace Impl { int g_cuda_space_factory_initialized = initialize_space_factory("150_Cuda"); +int CudaInternal::m_cudaArch = -1; +cudaDeviceProp CudaInternal::m_deviceProp; +std::set CudaInternal::cuda_devices = {}; +std::map CudaInternal::constantMemHostStagingPerDevice = + {}; +std::map CudaInternal::constantMemReusablePerDevice = {}; +std::map CudaInternal::constantMemMutexPerDevice = {}; + } // namespace Impl } // namespace Kokkos diff --git a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Instance.hpp b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Instance.hpp index 24f4af3101..ffaa0f5474 100644 --- a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Instance.hpp +++ b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Instance.hpp @@ -91,10 +91,10 @@ class CudaInternal { int m_cudaDev = -1; // Device Properties - inline static int m_cudaArch = -1; + static int m_cudaArch; static int concurrency(); - inline static cudaDeviceProp m_deviceProp; + static cudaDeviceProp m_deviceProp; // Scratch Spaces for Reductions mutable std::size_t m_scratchSpaceCount; @@ -120,11 +120,10 @@ class CudaInternal { bool was_initialized = false; bool was_finalized = false; - inline static std::set cuda_devices = {}; - inline static std::map constantMemHostStagingPerDevice = - {}; - inline static std::map constantMemReusablePerDevice = {}; - inline static std::map constantMemMutexPerDevice = {}; + static std::set cuda_devices; + static std::map constantMemHostStagingPerDevice; + static std::map constantMemReusablePerDevice; + static std::map constantMemMutexPerDevice; static CudaInternal& singleton(); @@ -421,23 +420,6 @@ class CudaInternal { return cudaStreamSynchronize(stream); } - // The following are only available for cuda 11.2 and greater -#if (defined(KOKKOS_ENABLE_IMPL_CUDA_MALLOC_ASYNC) && CUDART_VERSION >= 11020) - template - cudaError_t cuda_malloc_async_wrapper(void** devPtr, size_t size, - cudaStream_t hStream = nullptr) const { - if constexpr (setCudaDevice) set_cuda_device(); - return cudaMallocAsync(devPtr, size, get_input_stream(hStream)); - } - - template - cudaError_t cuda_free_async_wrapper(void* devPtr, - cudaStream_t hStream = nullptr) const { - if constexpr (setCudaDevice) set_cuda_device(); - return cudaFreeAsync(devPtr, get_input_stream(hStream)); - } -#endif - // C++ API routines template cudaError_t cuda_func_get_attributes_wrapper(cudaFuncAttributes* attr, diff --git a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Parallel_Team.hpp b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Parallel_Team.hpp index 9f7be45c83..71e7751821 100644 --- a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Parallel_Team.hpp +++ b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Parallel_Team.hpp @@ -539,17 +539,9 @@ class ParallelFor, m_vector_size(arg_policy.impl_vector_length()) { auto internal_space_instance = m_policy.space().impl_internal_space_instance(); - cudaFuncAttributes attr = - CudaParallelLaunch::get_cuda_func_attributes( - internal_space_instance->m_cudaDev); - m_team_size = - m_team_size >= 0 - ? m_team_size - : Kokkos::Impl::cuda_get_opt_block_size( - internal_space_instance, attr, m_functor, m_vector_size, - m_policy.team_scratch_size(0), - m_policy.thread_scratch_size(0)) / - m_vector_size; + m_team_size = m_team_size >= 0 ? m_team_size + : arg_policy.team_size_recommended( + arg_functor, ParallelForTag()); m_shmem_begin = (sizeof(double) * (m_team_size + 2)); m_shmem_size = @@ -585,13 +577,7 @@ class ParallelFor, "Kokkos::Impl::ParallelFor< Cuda > insufficient shared memory")); } - if (int(m_team_size) > - int(Kokkos::Impl::cuda_get_max_block_size( - internal_space_instance, attr, arg_functor, - arg_policy.impl_vector_length(), - arg_policy.team_scratch_size(0), - arg_policy.thread_scratch_size(0)) / - arg_policy.impl_vector_length())) { + if (m_team_size > arg_policy.team_size_max(arg_functor, ParallelForTag())) { Kokkos::Impl::throw_runtime_exception(std::string( "Kokkos::Impl::ParallelFor< Cuda > requested too large team size.")); } @@ -909,17 +895,11 @@ class ParallelReduce:: - get_cuda_func_attributes(internal_space_instance->m_cudaDev); - m_team_size = - m_team_size >= 0 - ? m_team_size - : Kokkos::Impl::cuda_get_opt_block_size( - internal_space_instance, attr, - m_functor_reducer.get_functor(), m_vector_size, - m_policy.team_scratch_size(0), - m_policy.thread_scratch_size(0)) / - m_vector_size; + m_team_size = m_team_size >= 0 ? m_team_size + : arg_policy.team_size_recommended( + arg_functor_reducer.get_functor(), + arg_functor_reducer.get_reducer(), + ParallelReduceTag()); m_team_begin = UseShflReduction diff --git a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_abort.hpp b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_abort.hpp index c8d6641d1e..18aca15065 100644 --- a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_abort.hpp +++ b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_abort.hpp @@ -28,35 +28,20 @@ extern "C" { /* Cuda runtime function, declared in * Requires capability 2.x or better. */ -extern __device__ void __assertfail(const void *message, const void *file, - unsigned int line, const void *function, - size_t charsize); +[[noreturn]] __device__ void __assertfail(const void *message, const void *file, + unsigned int line, + const void *function, + size_t charsize); } namespace Kokkos { namespace Impl { -// required to workaround failures in random number generator unit tests with -// pre-volta architectures -#if defined(KOKKOS_ENABLE_DEBUG_BOUNDS_CHECK) -__device__ inline void cuda_abort(const char *const message) { -#else -[[noreturn]] __device__ inline void cuda_abort(const char *const message) { -#endif +[[noreturn]] __device__ static void cuda_abort(const char *const message) { const char empty[] = ""; __assertfail((const void *)message, (const void *)empty, (unsigned int)0, (const void *)empty, sizeof(char)); - - // This loop is never executed. It's intended to suppress warnings that the - // function returns, even though it does not. This is necessary because - // __assertfail is not marked as [[noreturn]], even though it does not return. - // Disable with KOKKOS_ENABLE_DEBUG_BOUNDS_CHECK to workaround failures - // in random number generator unit tests with pre-volta architectures -#if !defined(KOKKOS_ENABLE_DEBUG_BOUNDS_CHECK) - while (true) - ; -#endif } } // namespace Impl diff --git a/lib/kokkos/core/src/HIP/Kokkos_HIP.hpp b/lib/kokkos/core/src/HIP/Kokkos_HIP.hpp index 3a88e97ee3..439075fc6c 100644 --- a/lib/kokkos/core/src/HIP/Kokkos_HIP.hpp +++ b/lib/kokkos/core/src/HIP/Kokkos_HIP.hpp @@ -48,8 +48,19 @@ class HIP { using scratch_memory_space = ScratchMemorySpace; HIP(); - HIP(hipStream_t stream, - Impl::ManageStream manage_stream = Impl::ManageStream::no); + + explicit HIP(hipStream_t stream) : HIP(stream, Impl::ManageStream::no) {} + +#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4 + template + KOKKOS_DEPRECATED_WITH_COMMENT( + "HIP execution space should be constructed explicitly.") + HIP(hipStream_t stream) + : HIP(stream) {} +#endif + + HIP(hipStream_t stream, Impl::ManageStream manage_stream); + KOKKOS_DEPRECATED HIP(hipStream_t stream, bool manage_stream); //@} diff --git a/lib/kokkos/core/src/HIP/Kokkos_HIP_Error.hpp b/lib/kokkos/core/src/HIP/Kokkos_HIP_Error.hpp index 43d63c090b..fa45dcfec3 100644 --- a/lib/kokkos/core/src/HIP/Kokkos_HIP_Error.hpp +++ b/lib/kokkos/core/src/HIP/Kokkos_HIP_Error.hpp @@ -22,8 +22,6 @@ #include -#include - namespace Kokkos { namespace Impl { @@ -44,39 +42,4 @@ inline void hip_internal_safe_call(hipError_t e, const char* name, #define KOKKOS_IMPL_HIP_SAFE_CALL(call) \ Kokkos::Impl::hip_internal_safe_call(call, #call, __FILE__, __LINE__) -namespace Kokkos { -namespace Experimental { - -class HIPRawMemoryAllocationFailure : public RawMemoryAllocationFailure { - private: - hipError_t m_error_code = hipSuccess; - - static FailureMode get_failure_mode(hipError_t error_code) { - switch (error_code) { - case hipErrorMemoryAllocation: return FailureMode::OutOfMemoryError; - case hipErrorInvalidValue: return FailureMode::InvalidAllocationSize; - default: return FailureMode::Unknown; - } - } - - public: - HIPRawMemoryAllocationFailure(size_t arg_attempted_size, - hipError_t arg_error_code, - AllocationMechanism arg_mechanism) noexcept - : RawMemoryAllocationFailure( - arg_attempted_size, /* HIPSpace doesn't handle alignment? */ 1, - get_failure_mode(arg_error_code), arg_mechanism), - m_error_code(arg_error_code) {} - - void append_additional_error_information(std::ostream& o) const override { - if (m_error_code != hipSuccess) { - o << " The HIP allocation returned the error code \"" - << hipGetErrorName(m_error_code) << "\"."; - } - } -}; - -} // namespace Experimental -} // namespace Kokkos - #endif diff --git a/lib/kokkos/core/src/HIP/Kokkos_HIP_Graph_Impl.hpp b/lib/kokkos/core/src/HIP/Kokkos_HIP_Graph_Impl.hpp index 7cc06d02fb..a0989fe671 100644 --- a/lib/kokkos/core/src/HIP/Kokkos_HIP_Graph_Impl.hpp +++ b/lib/kokkos/core/src/HIP/Kokkos_HIP_Graph_Impl.hpp @@ -40,7 +40,7 @@ class GraphImpl { GraphNodeImpl; - // Not moveable or copyable; it spends its whole life as a shared_ptr in the + // Not movable or copyable; it spends its whole life as a shared_ptr in the // Graph object. GraphImpl() = delete; GraphImpl(GraphImpl const&) = delete; @@ -108,7 +108,7 @@ inline void GraphImpl::add_node( } // Requires NodeImplPtr is a shared_ptr to specialization of GraphNodeImpl -// Also requires that the kernel has the graph node tag in it's policy +// Also requires that the kernel has the graph node tag in its policy template inline void GraphImpl::add_node( std::shared_ptr const& arg_node_ptr) { diff --git a/lib/kokkos/core/src/HIP/Kokkos_HIP_Instance.cpp b/lib/kokkos/core/src/HIP/Kokkos_HIP_Instance.cpp index 22c0db047f..e0b25c6939 100644 --- a/lib/kokkos/core/src/HIP/Kokkos_HIP_Instance.cpp +++ b/lib/kokkos/core/src/HIP/Kokkos_HIP_Instance.cpp @@ -353,6 +353,22 @@ void HIPInternal::finalize() { m_num_scratch_locks = 0; } +int HIPInternal::m_hipDev = -1; +unsigned HIPInternal::m_multiProcCount = 0; +unsigned HIPInternal::m_maxWarpCount = 0; +std::array HIPInternal::m_maxBlock = {0, 0, 0}; +unsigned HIPInternal::m_maxWavesPerCU = 0; +int HIPInternal::m_shmemPerSM = 0; +int HIPInternal::m_maxShmemPerBlock = 0; +int HIPInternal::m_maxThreadsPerSM = 0; + +hipDeviceProp_t HIPInternal::m_deviceProp; + +std::mutex HIPInternal::scratchFunctorMutex; +unsigned long *HIPInternal::constantMemHostStaging = nullptr; +hipEvent_t HIPInternal::constantMemReusable = nullptr; +std::mutex HIPInternal::constantMemMutex; + //---------------------------------------------------------------------------- Kokkos::HIP::size_type hip_internal_multiprocessor_count() { diff --git a/lib/kokkos/core/src/HIP/Kokkos_HIP_Instance.hpp b/lib/kokkos/core/src/HIP/Kokkos_HIP_Instance.hpp index 7b55f519c2..19349e90bb 100644 --- a/lib/kokkos/core/src/HIP/Kokkos_HIP_Instance.hpp +++ b/lib/kokkos/core/src/HIP/Kokkos_HIP_Instance.hpp @@ -35,8 +35,7 @@ struct HIPTraits { static constexpr int WarpSize = 64; static constexpr int WarpIndexMask = 0x003f; /* hexadecimal for 63 */ static constexpr int WarpIndexShift = 6; /* WarpSize == 1 << WarpShift*/ -#elif defined(KOKKOS_ARCH_AMD_GFX1030) || defined(KOKKOS_ARCH_AMD_GFX1100) || \ - defined(KOKKOS_ARCH_AMD_GFX1103) +#elif defined(KOKKOS_ARCH_AMD_GFX1030) || defined(KOKKOS_ARCH_AMD_GFX1100) static constexpr int WarpSize = 32; static constexpr int WarpIndexMask = 0x001f; /* hexadecimal for 31 */ static constexpr int WarpIndexShift = 5; /* WarpSize == 1 << WarpShift*/ @@ -71,16 +70,16 @@ class HIPInternal { public: using size_type = ::Kokkos::HIP::size_type; - inline static int m_hipDev = -1; - inline static unsigned m_multiProcCount = 0; - inline static unsigned m_maxWarpCount = 0; - inline static std::array m_maxBlock = {0, 0, 0}; - inline static unsigned m_maxWavesPerCU = 0; - inline static int m_shmemPerSM = 0; - inline static int m_maxShmemPerBlock = 0; - inline static int m_maxThreadsPerSM = 0; + static int m_hipDev; + static unsigned m_multiProcCount; + static unsigned m_maxWarpCount; + static std::array m_maxBlock; + static unsigned m_maxWavesPerCU; + static int m_shmemPerSM; + static int m_maxShmemPerBlock; + static int m_maxThreadsPerSM; - inline static hipDeviceProp_t m_deviceProp; + static hipDeviceProp_t m_deviceProp; static int concurrency(); @@ -93,7 +92,7 @@ class HIPInternal { size_type *m_scratchFlags = nullptr; mutable size_type *m_scratchFunctor = nullptr; mutable size_type *m_scratchFunctorHost = nullptr; - inline static std::mutex scratchFunctorMutex; + static std::mutex scratchFunctorMutex; hipStream_t m_stream = nullptr; uint32_t m_instance_id = @@ -112,9 +111,9 @@ class HIPInternal { // FIXME_HIP: these want to be per-device, not per-stream... use of 'static' // here will break once there are multiple devices though - inline static unsigned long *constantMemHostStaging = nullptr; - inline static hipEvent_t constantMemReusable = nullptr; - inline static std::mutex constantMemMutex; + static unsigned long *constantMemHostStaging; + static hipEvent_t constantMemReusable; + static std::mutex constantMemMutex; static HIPInternal &singleton(); diff --git a/lib/kokkos/core/src/HIP/Kokkos_HIP_ParallelReduce_MDRange.hpp b/lib/kokkos/core/src/HIP/Kokkos_HIP_ParallelReduce_MDRange.hpp index 55b6218d1c..1629511646 100644 --- a/lib/kokkos/core/src/HIP/Kokkos_HIP_ParallelReduce_MDRange.hpp +++ b/lib/kokkos/core/src/HIP/Kokkos_HIP_ParallelReduce_MDRange.hpp @@ -50,6 +50,7 @@ class ParallelReduce class ParallelReduce, HIP> { public: - using Policy = TeamPolicyInternal; + using Policy = TeamPolicy; using FunctorType = typename CombinedFunctorReducerType::functor_type; using ReducerType = typename CombinedFunctorReducerType::reducer_type; @@ -46,6 +46,7 @@ class ParallelReduce is_first_hip_managed_allocation(true); @@ -66,7 +67,6 @@ void* HIPSpace::allocate( return impl_allocate(arg_label, arg_alloc_size, arg_logical_size); } void* HIPSpace::impl_allocate( - const char* arg_label, const size_t arg_alloc_size, const size_t arg_logical_size, const Kokkos::Tools::SpaceHandle arg_handle) const { @@ -77,10 +77,7 @@ void* HIPSpace::impl_allocate( // This is the only way to clear the last error, which we should do here // since we're turning it into an exception here (void)hipGetLastError(); - throw Experimental::HIPRawMemoryAllocationFailure( - arg_alloc_size, error_code, - Experimental::RawMemoryAllocationFailure::AllocationMechanism:: - HIPMalloc); + Kokkos::Impl::throw_bad_alloc(name(), arg_alloc_size, arg_label); } if (Kokkos::Profiling::profileLibraryLoaded()) { const size_t reported_size = @@ -111,10 +108,7 @@ void* HIPHostPinnedSpace::impl_allocate( // This is the only way to clear the last error, which we should do here // since we're turning it into an exception here (void)hipGetLastError(); - throw Experimental::HIPRawMemoryAllocationFailure( - arg_alloc_size, error_code, - Experimental::RawMemoryAllocationFailure::AllocationMechanism:: - HIPHostMalloc); + Kokkos::Impl::throw_bad_alloc(name(), arg_alloc_size, arg_label); } if (Kokkos::Profiling::profileLibraryLoaded()) { const size_t reported_size = @@ -178,10 +172,7 @@ Kokkos::HIP::runtime WARNING: Kokkos did not find an environment variable 'HSA_X // This is the only way to clear the last error, which we should do here // since we're turning it into an exception here (void)hipGetLastError(); - throw Experimental::HIPRawMemoryAllocationFailure( - arg_alloc_size, error_code, - Experimental::RawMemoryAllocationFailure::AllocationMechanism:: - HIPMallocManaged); + Kokkos::Impl::throw_bad_alloc(name(), arg_alloc_size, arg_label); } KOKKOS_IMPL_HIP_SAFE_CALL(hipMemAdvise( ptr, arg_alloc_size, hipMemAdviseSetCoarseGrain, m_device)); diff --git a/lib/kokkos/core/src/HPX/Kokkos_HPX.cpp b/lib/kokkos/core/src/HPX/Kokkos_HPX.cpp index 6d541a6414..1f3d078344 100644 --- a/lib/kokkos/core/src/HPX/Kokkos_HPX.cpp +++ b/lib/kokkos/core/src/HPX/Kokkos_HPX.cpp @@ -153,7 +153,7 @@ void HPX::impl_instance_fence_locked(const std::string &name) const { auto &s = impl_get_sender(); hpx::this_thread::experimental::sync_wait(std::move(s)); - s = hpx::execution::experimental::unique_any_sender( + s = hpx::execution::experimental::unique_any_sender<>( hpx::execution::experimental::just()); }); } @@ -184,7 +184,7 @@ void HPX::impl_static_fence(const std::string &name) { } hpx::this_thread::experimental::sync_wait(std::move(s)); - s = hpx::execution::experimental::unique_any_sender( + s = hpx::execution::experimental::unique_any_sender<>( hpx::execution::experimental::just()); }); } diff --git a/lib/kokkos/core/src/HPX/Kokkos_HPX.hpp b/lib/kokkos/core/src/HPX/Kokkos_HPX.hpp index 26181a7c05..245dc128ca 100644 --- a/lib/kokkos/core/src/HPX/Kokkos_HPX.hpp +++ b/lib/kokkos/core/src/HPX/Kokkos_HPX.hpp @@ -168,17 +168,31 @@ class HPX { : m_instance_data(Kokkos::Impl::HostSharedPtr( &m_default_instance_data, &default_instance_deleter)) {} ~HPX() = default; - HPX(instance_mode mode) + explicit HPX(instance_mode mode) : m_instance_data( mode == instance_mode::independent ? (Kokkos::Impl::HostSharedPtr( new instance_data(m_next_instance_id++))) : Kokkos::Impl::HostSharedPtr( &m_default_instance_data, &default_instance_deleter)) {} - HPX(hpx::execution::experimental::unique_any_sender<> &&sender) + explicit HPX(hpx::execution::experimental::unique_any_sender<> &&sender) : m_instance_data(Kokkos::Impl::HostSharedPtr( new instance_data(m_next_instance_id++, std::move(sender)))) {} +#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4 + template + KOKKOS_DEPRECATED_WITH_COMMENT( + "HPX execution space should be constructed explicitly.") + HPX(instance_mode mode) + : HPX(mode) {} + + template + KOKKOS_DEPRECATED_WITH_COMMENT( + "HPX execution space should be constructed explicitly.") + HPX(hpx::execution::experimental::unique_any_sender<> &&sender) + : HPX(std::move(sender)) {} +#endif + HPX(HPX &&other) = default; HPX(const HPX &other) = default; diff --git a/lib/kokkos/core/src/Kokkos_Array.hpp b/lib/kokkos/core/src/Kokkos_Array.hpp index ba1626bb72..4d905fbc55 100644 --- a/lib/kokkos/core/src/Kokkos_Array.hpp +++ b/lib/kokkos/core/src/Kokkos_Array.hpp @@ -29,7 +29,6 @@ #include #include #include -#include #include namespace Kokkos { @@ -80,7 +79,11 @@ struct ArrayBoundsCheck { /**\brief Derived from the C++17 'std::array'. * Dropping the iterator interface. */ +#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4 template +#else +template +#endif struct Array { public: /** @@ -129,10 +132,38 @@ struct Array { KOKKOS_INLINE_FUNCTION constexpr const_pointer data() const { return &m_internal_implementation_private_member_data[0]; } + + friend KOKKOS_FUNCTION constexpr bool operator==(Array const& lhs, + Array const& rhs) noexcept { + for (size_t i = 0; i != N; ++i) + if (lhs[i] != rhs[i]) return false; + return true; + } + + friend KOKKOS_FUNCTION constexpr bool operator!=(Array const& lhs, + Array const& rhs) noexcept { + return !(lhs == rhs); + } + + private: + template + friend KOKKOS_INLINE_FUNCTION constexpr std::enable_if_t< + Impl::is_swappable::value> + kokkos_swap(Array& a, + Array& b) noexcept(Impl::is_nothrow_swappable_v) { + for (std::size_t i = 0; i < N; ++i) { + kokkos_swap(a[i], b[i]); + } + } }; +#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4 template struct Array { +#else +template +struct Array { +#endif public: using reference = T&; using const_reference = std::add_const_t&; @@ -167,25 +198,35 @@ struct Array { KOKKOS_INLINE_FUNCTION pointer data() { return nullptr; } KOKKOS_INLINE_FUNCTION const_pointer data() const { return nullptr; } - KOKKOS_DEFAULTED_FUNCTION ~Array() = default; - KOKKOS_DEFAULTED_FUNCTION Array() = default; - KOKKOS_DEFAULTED_FUNCTION Array(const Array&) = default; - KOKKOS_DEFAULTED_FUNCTION Array& operator=(const Array&) = default; + friend KOKKOS_FUNCTION constexpr bool operator==(Array const&, + Array const&) noexcept { + return true; + } + friend KOKKOS_FUNCTION constexpr bool operator!=(Array const&, + Array const&) noexcept { + return false; + } - // Some supported compilers are not sufficiently C++11 compliant - // for default move constructor and move assignment operator. - // Array( Array && ) = default ; - // Array & operator = ( Array && ) = default ; + private: + friend KOKKOS_INLINE_FUNCTION constexpr void kokkos_swap( + Array&, Array&) noexcept {} }; +#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4 +namespace Impl { +struct KokkosArrayContiguous {}; +struct KokkosArrayStrided {}; +} // namespace Impl + template <> -struct Array { - struct contiguous {}; - struct strided {}; +struct KOKKOS_DEPRECATED Array { + using contiguous = Impl::KokkosArrayContiguous; + using strided = Impl::KokkosArrayStrided; }; template -struct Array::contiguous> { +struct KOKKOS_DEPRECATED + Array { private: T* m_elem; size_t m_size; @@ -253,7 +294,8 @@ struct Array::contiguous> { }; template -struct Array::strided> { +struct KOKKOS_DEPRECATED + Array { private: T* m_elem; size_t m_size; @@ -320,10 +362,37 @@ struct Array::strided> { size_type arg_stride) : m_elem(arg_ptr), m_size(arg_size), m_stride(arg_stride) {} }; +#endif template Array(T, Us...)->Array; +namespace Impl { + +template +KOKKOS_FUNCTION constexpr Array, N> to_array_impl( + T (&a)[N], std::index_sequence) { + return {{a[I]...}}; +} + +template +KOKKOS_FUNCTION constexpr Array, N> to_array_impl( + T(&&a)[N], std::index_sequence) { + return {{std::move(a[I])...}}; +} + +} // namespace Impl + +template +KOKKOS_FUNCTION constexpr auto to_array(T (&a)[N]) { + return Impl::to_array_impl(a, std::make_index_sequence{}); +} + +template +KOKKOS_FUNCTION constexpr auto to_array(T(&&a)[N]) { + return Impl::to_array_impl(std::move(a), std::make_index_sequence{}); +} + } // namespace Kokkos // @@ -333,6 +402,7 @@ struct std::tuple_size> template struct std::tuple_element> { + static_assert(I < N); using type = T; }; @@ -340,21 +410,25 @@ namespace Kokkos { template KOKKOS_FUNCTION constexpr T& get(Array& a) noexcept { + static_assert(I < N); return a[I]; } template KOKKOS_FUNCTION constexpr T const& get(Array const& a) noexcept { + static_assert(I < N); return a[I]; } template KOKKOS_FUNCTION constexpr T&& get(Array&& a) noexcept { + static_assert(I < N); return std::move(a[I]); } template KOKKOS_FUNCTION constexpr T const&& get(Array const&& a) noexcept { + static_assert(I < N); return std::move(a[I]); } diff --git a/lib/kokkos/core/src/Kokkos_Atomics_Desul_Volatile_Wrapper.hpp b/lib/kokkos/core/src/Kokkos_Atomics_Desul_Volatile_Wrapper.hpp index 9acacef901..bf57dcae65 100644 --- a/lib/kokkos/core/src/Kokkos_Atomics_Desul_Volatile_Wrapper.hpp +++ b/lib/kokkos/core/src/Kokkos_Atomics_Desul_Volatile_Wrapper.hpp @@ -22,7 +22,6 @@ static_assert(false, #ifndef KOKKOS_DESUL_ATOMICS_VOLATILE_WRAPPER_HPP_ #define KOKKOS_DESUL_ATOMICS_VOLATILE_WRAPPER_HPP_ #include -#include #include #ifdef KOKKOS_ENABLE_ATOMICS_BYPASS diff --git a/lib/kokkos/core/src/Kokkos_Atomics_Desul_Wrapper.hpp b/lib/kokkos/core/src/Kokkos_Atomics_Desul_Wrapper.hpp index eebdd20f15..26db69ac1f 100644 --- a/lib/kokkos/core/src/Kokkos_Atomics_Desul_Wrapper.hpp +++ b/lib/kokkos/core/src/Kokkos_Atomics_Desul_Wrapper.hpp @@ -22,8 +22,6 @@ static_assert(false, #ifndef KOKKOS_DESUL_ATOMICS_WRAPPER_HPP_ #define KOKKOS_DESUL_ATOMICS_WRAPPER_HPP_ #include - -#include #include #include diff --git a/lib/kokkos/core/src/Kokkos_Complex.hpp b/lib/kokkos/core/src/Kokkos_Complex.hpp index 4d405116cc..7dd2a9ddbb 100644 --- a/lib/kokkos/core/src/Kokkos_Complex.hpp +++ b/lib/kokkos/core/src/Kokkos_Complex.hpp @@ -28,6 +28,7 @@ #include #include #include +#include namespace Kokkos { @@ -256,6 +257,12 @@ class return *this; } + template + friend constexpr const RT& get(const complex&) noexcept; + + template + friend constexpr const RT&& get(const complex&&) noexcept; + #ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4 //! Copy constructor from volatile. template < @@ -423,6 +430,75 @@ class #endif // KOKKOS_ENABLE_DEPRECATED_CODE_4 }; +} // namespace Kokkos + +// Tuple protocol for complex based on https://wg21.link/P2819R2 (voted into +// the C++26 working draft on 2023-11) + +template +struct std::tuple_size> + : std::integral_constant {}; + +template +struct std::tuple_element> { + static_assert(I < 2); + using type = RealType; +}; + +namespace Kokkos { + +// get<...>(...) defined here so as not to be hidden friends, as per P2819R2 + +template +KOKKOS_FUNCTION constexpr RealType& get(complex& z) noexcept { + static_assert(I < 2); + if constexpr (I == 0) + return z.real(); + else + return z.imag(); +#ifdef KOKKOS_COMPILER_INTEL + __builtin_unreachable(); +#endif +} + +template +KOKKOS_FUNCTION constexpr RealType&& get(complex&& z) noexcept { + static_assert(I < 2); + if constexpr (I == 0) + return std::move(z.real()); + else + return std::move(z.imag()); +#ifdef KOKKOS_COMPILER_INTEL + __builtin_unreachable(); +#endif +} + +template +KOKKOS_FUNCTION constexpr const RealType& get( + const complex& z) noexcept { + static_assert(I < 2); + if constexpr (I == 0) + return z.re_; + else + return z.im_; +#ifdef KOKKOS_COMPILER_INTEL + __builtin_unreachable(); +#endif +} + +template +KOKKOS_FUNCTION constexpr const RealType&& get( + const complex&& z) noexcept { + static_assert(I < 2); + if constexpr (I == 0) + return std::move(z.re_); + else + return std::move(z.im_); +#ifdef KOKKOS_COMPILER_INTEL + __builtin_unreachable(); +#endif +} + //============================================================================== // {{{1 diff --git a/lib/kokkos/core/src/Kokkos_CopyViews.hpp b/lib/kokkos/core/src/Kokkos_CopyViews.hpp index 08f6ba8d69..e856b19247 100644 --- a/lib/kokkos/core/src/Kokkos_CopyViews.hpp +++ b/lib/kokkos/core/src/Kokkos_CopyViews.hpp @@ -221,10 +221,12 @@ struct ViewFill { ViewFill(const ViewType& a_, typename ViewType::const_value_type& val_, const ExecSpace& space) : a(a_), val(val_) { + // MDRangePolicy is not supported for 7D views + // Iterate separately over extent(2) Kokkos::parallel_for("Kokkos::ViewFill-7D", policy_type(space, {0, 0, 0, 0, 0, 0}, - {a.extent(0), a.extent(1), a.extent(2), - a.extent(3), a.extent(5), a.extent(6)}), + {a.extent(0), a.extent(1), a.extent(3), + a.extent(4), a.extent(5), a.extent(6)}), *this); } @@ -249,6 +251,8 @@ struct ViewFill { ViewFill(const ViewType& a_, typename ViewType::const_value_type& val_, const ExecSpace& space) : a(a_), val(val_) { + // MDRangePolicy is not supported for 8D views + // Iterate separately over extent(2) and extent(4) Kokkos::parallel_for("Kokkos::ViewFill-8D", policy_type(space, {0, 0, 0, 0, 0, 0}, {a.extent(0), a.extent(1), a.extent(3), @@ -293,9 +297,11 @@ struct ViewCopy { ViewTypeA a; ViewTypeB b; static const Kokkos::Iterate outer_iteration_pattern = - Kokkos::layout_iterate_type_selector::outer_iteration_pattern; + Kokkos::Impl::layout_iterate_type_selector< + Layout>::outer_iteration_pattern; static const Kokkos::Iterate inner_iteration_pattern = - Kokkos::layout_iterate_type_selector::inner_iteration_pattern; + Kokkos::Impl::layout_iterate_type_selector< + Layout>::inner_iteration_pattern; using iterate_type = Kokkos::Rank<2, outer_iteration_pattern, inner_iteration_pattern>; using policy_type = @@ -323,9 +329,11 @@ struct ViewCopy { ViewTypeB b; static const Kokkos::Iterate outer_iteration_pattern = - Kokkos::layout_iterate_type_selector::outer_iteration_pattern; + Kokkos::Impl::layout_iterate_type_selector< + Layout>::outer_iteration_pattern; static const Kokkos::Iterate inner_iteration_pattern = - Kokkos::layout_iterate_type_selector::inner_iteration_pattern; + Kokkos::Impl::layout_iterate_type_selector< + Layout>::inner_iteration_pattern; using iterate_type = Kokkos::Rank<3, outer_iteration_pattern, inner_iteration_pattern>; using policy_type = @@ -354,9 +362,11 @@ struct ViewCopy { ViewTypeB b; static const Kokkos::Iterate outer_iteration_pattern = - Kokkos::layout_iterate_type_selector::outer_iteration_pattern; + Kokkos::Impl::layout_iterate_type_selector< + Layout>::outer_iteration_pattern; static const Kokkos::Iterate inner_iteration_pattern = - Kokkos::layout_iterate_type_selector::inner_iteration_pattern; + Kokkos::Impl::layout_iterate_type_selector< + Layout>::inner_iteration_pattern; using iterate_type = Kokkos::Rank<4, outer_iteration_pattern, inner_iteration_pattern>; using policy_type = @@ -386,9 +396,11 @@ struct ViewCopy { ViewTypeB b; static const Kokkos::Iterate outer_iteration_pattern = - Kokkos::layout_iterate_type_selector::outer_iteration_pattern; + Kokkos::Impl::layout_iterate_type_selector< + Layout>::outer_iteration_pattern; static const Kokkos::Iterate inner_iteration_pattern = - Kokkos::layout_iterate_type_selector::inner_iteration_pattern; + Kokkos::Impl::layout_iterate_type_selector< + Layout>::inner_iteration_pattern; using iterate_type = Kokkos::Rank<5, outer_iteration_pattern, inner_iteration_pattern>; using policy_type = @@ -418,9 +430,11 @@ struct ViewCopy { ViewTypeB b; static const Kokkos::Iterate outer_iteration_pattern = - Kokkos::layout_iterate_type_selector::outer_iteration_pattern; + Kokkos::Impl::layout_iterate_type_selector< + Layout>::outer_iteration_pattern; static const Kokkos::Iterate inner_iteration_pattern = - Kokkos::layout_iterate_type_selector::inner_iteration_pattern; + Kokkos::Impl::layout_iterate_type_selector< + Layout>::inner_iteration_pattern; using iterate_type = Kokkos::Rank<6, outer_iteration_pattern, inner_iteration_pattern>; using policy_type = @@ -450,9 +464,11 @@ struct ViewCopy { ViewTypeB b; static const Kokkos::Iterate outer_iteration_pattern = - Kokkos::layout_iterate_type_selector::outer_iteration_pattern; + Kokkos::Impl::layout_iterate_type_selector< + Layout>::outer_iteration_pattern; static const Kokkos::Iterate inner_iteration_pattern = - Kokkos::layout_iterate_type_selector::inner_iteration_pattern; + Kokkos::Impl::layout_iterate_type_selector< + Layout>::inner_iteration_pattern; using iterate_type = Kokkos::Rank<6, outer_iteration_pattern, inner_iteration_pattern>; using policy_type = @@ -461,6 +477,8 @@ struct ViewCopy { ViewCopy(const ViewTypeA& a_, const ViewTypeB& b_, const ExecSpace space = ExecSpace()) : a(a_), b(b_) { + // MDRangePolicy is not supported for 7D views + // Iterate separately over extent(2) Kokkos::parallel_for("Kokkos::ViewCopy-7D", policy_type(space, {0, 0, 0, 0, 0, 0}, {a.extent(0), a.extent(1), a.extent(3), @@ -483,9 +501,11 @@ struct ViewCopy { ViewTypeB b; static const Kokkos::Iterate outer_iteration_pattern = - Kokkos::layout_iterate_type_selector::outer_iteration_pattern; + Kokkos::Impl::layout_iterate_type_selector< + Layout>::outer_iteration_pattern; static const Kokkos::Iterate inner_iteration_pattern = - Kokkos::layout_iterate_type_selector::inner_iteration_pattern; + Kokkos::Impl::layout_iterate_type_selector< + Layout>::inner_iteration_pattern; using iterate_type = Kokkos::Rank<6, outer_iteration_pattern, inner_iteration_pattern>; using policy_type = @@ -494,6 +514,8 @@ struct ViewCopy { ViewCopy(const ViewTypeA& a_, const ViewTypeB& b_, const ExecSpace space = ExecSpace()) : a(a_), b(b_) { + // MDRangePolicy is not supported for 8D views + // Iterate separately over extent(2) and extent(4) Kokkos::parallel_for("Kokkos::ViewCopy-8D", policy_type(space, {0, 0, 0, 0, 0, 0}, {a.extent(0), a.extent(1), a.extent(3), @@ -539,11 +561,8 @@ void view_copy(const ExecutionSpace& space, const DstType& dst, int64_t strides[DstType::rank + 1]; dst.stride(strides); Kokkos::Iterate iterate; - if (Kokkos::is_layouttiled::value) { - iterate = Kokkos::layout_iterate_type_selector< - typename DstType::array_layout>::outer_iteration_pattern; - } else if (std::is_same::value) { + if (std::is_same::value) { iterate = Kokkos::Iterate::Right; } else if (std::is_same::value) { @@ -630,11 +649,8 @@ void view_copy(const DstType& dst, const SrcType& src) { int64_t strides[DstType::rank + 1]; dst.stride(strides); Kokkos::Iterate iterate; - if (Kokkos::is_layouttiled::value) { - iterate = Kokkos::layout_iterate_type_selector< - typename DstType::array_layout>::outer_iteration_pattern; - } else if (std::is_same::value) { + if (std::is_same::value) { iterate = Kokkos::Iterate::Right; } else if (std::is_same::value) { @@ -3092,8 +3108,7 @@ inline std::enable_if_t< std::is_same::array_layout, Kokkos::LayoutRight>::value || std::is_same::array_layout, - Kokkos::LayoutStride>::value || - is_layouttiled::array_layout>::value> + Kokkos::LayoutStride>::value> impl_resize(const Impl::ViewCtorProp& arg_prop, Kokkos::View& v, const typename Kokkos::View::array_layout& layout) { @@ -3139,8 +3154,7 @@ inline std::enable_if_t< std::is_same::array_layout, Kokkos::LayoutRight>::value || std::is_same::array_layout, - Kokkos::LayoutStride>::value || - is_layouttiled::array_layout>::value)> + Kokkos::LayoutStride>::value)> impl_resize(const Impl::ViewCtorProp& arg_prop, Kokkos::View& v, const typename Kokkos::View::array_layout& layout) { @@ -3235,7 +3249,10 @@ impl_realloc(Kokkos::View& v, const size_t n0, const size_t n1, v = view_type(); // Best effort to deallocate in case no other view refers // to the shared allocation v = view_type(arg_prop_copy, n0, n1, n2, n3, n4, n5, n6, n7); - } else if (alloc_prop_input::initialize) { + return; + } + + if constexpr (alloc_prop_input::initialize) { if constexpr (alloc_prop_input::has_execution_space) { const auto& exec_space = Impl::get_property(arg_prop); @@ -3308,8 +3325,7 @@ inline std::enable_if_t< std::is_same::array_layout, Kokkos::LayoutRight>::value || std::is_same::array_layout, - Kokkos::LayoutStride>::value || - is_layouttiled::array_layout>::value> + Kokkos::LayoutStride>::value> impl_realloc(Kokkos::View& v, const typename Kokkos::View::array_layout& layout, const Impl::ViewCtorProp& arg_prop) { @@ -3331,7 +3347,10 @@ impl_realloc(Kokkos::View& v, if (v.layout() != layout) { v = view_type(); // Deallocate first, if the only view to allocation v = view_type(arg_prop, layout); - } else if (alloc_prop_input::initialize) { + return; + } + + if constexpr (alloc_prop_input::initialize) { if constexpr (alloc_prop_input::has_execution_space) { const auto& exec_space = Impl::get_property(arg_prop); @@ -3351,8 +3370,7 @@ inline std::enable_if_t< std::is_same::array_layout, Kokkos::LayoutRight>::value || std::is_same::array_layout, - Kokkos::LayoutStride>::value || - is_layouttiled::array_layout>::value)> + Kokkos::LayoutStride>::value)> impl_realloc(Kokkos::View& v, const typename Kokkos::View::array_layout& layout, const Impl::ViewCtorProp& arg_prop) { @@ -3452,6 +3470,7 @@ struct MirrorType { using view_type = Kokkos::View; }; +// collection of static asserts for create_mirror and create_mirror_view template void check_view_ctor_args_create_mirror() { using alloc_prop_input = Impl::ViewCtorProp; @@ -3470,232 +3489,231 @@ void check_view_ctor_args_create_mirror() { "not explicitly allow padding!"); } +// create a mirror +// private interface that accepts arbitrary view constructor args passed by a +// view_alloc template -inline std::enable_if_t::has_memory_space, - typename Kokkos::View::HostMirror> -create_mirror(const Kokkos::View& src, - const Impl::ViewCtorProp& arg_prop) { - using src_type = View; - using dst_type = typename src_type::HostMirror; - +inline auto create_mirror(const Kokkos::View& src, + const Impl::ViewCtorProp& arg_prop) { check_view_ctor_args_create_mirror(); auto prop_copy = Impl::with_properties_if_unset( arg_prop, std::string(src.label()).append("_mirror")); - return dst_type(prop_copy, src.layout()); -} - -// Create a mirror in a new space (specialization for different space) -template ::has_memory_space>> -auto create_mirror(const Kokkos::View& src, - const Impl::ViewCtorProp& arg_prop) { - check_view_ctor_args_create_mirror(); - - auto prop_copy = Impl::with_properties_if_unset( - arg_prop, std::string(src.label()).append("_mirror")); - using alloc_prop = decltype(prop_copy); - - return typename Impl::MirrorType::view_type(prop_copy, src.layout()); + if constexpr (Impl::ViewCtorProp::has_memory_space) { + using memory_space = typename decltype(prop_copy)::memory_space; + using dst_type = + typename Impl::MirrorType::view_type; + return dst_type(prop_copy, src.layout()); + } else { + using dst_type = typename View::HostMirror; + return dst_type(prop_copy, src.layout()); + } +#if defined(KOKKOS_COMPILER_INTEL) || \ + (defined(KOKKOS_COMPILER_NVCC) && KOKKOS_COMPILER_NVCC >= 1130 && \ + !defined(KOKKOS_COMPILER_MSVC)) + __builtin_unreachable(); +#endif } } // namespace Impl -template -std::enable_if_t::specialize>::value, - typename Kokkos::View::HostMirror> -create_mirror(Kokkos::View const& v) { - return Impl::create_mirror(v, Impl::ViewCtorProp<>{}); +// public interface +template ::specialize>>> +auto create_mirror(Kokkos::View const& src) { + return Impl::create_mirror(src, Impl::ViewCtorProp<>{}); } -template -std::enable_if_t::specialize>::value, - typename Kokkos::View::HostMirror> -create_mirror(Kokkos::Impl::WithoutInitializing_t wi, - Kokkos::View const& v) { - return Impl::create_mirror(v, view_alloc(wi)); +// public interface that accepts a without initializing flag +template ::specialize>>> +auto create_mirror(Kokkos::Impl::WithoutInitializing_t wi, + Kokkos::View const& src) { + return Impl::create_mirror(src, view_alloc(wi)); } +// public interface that accepts a space template ::value>> -std::enable_if_t::specialize>::value, - typename Impl::MirrorType::view_type> -create_mirror(Space const&, Kokkos::View const& v) { - return Impl::create_mirror(v, view_alloc(typename Space::memory_space{})); -} - -template ::specialize>::value && - Impl::ViewCtorProp::has_memory_space>> + Kokkos::is_space::value && + std::is_void_v::specialize>>> +auto create_mirror(Space const&, Kokkos::View const& src) { + return Impl::create_mirror(src, view_alloc(typename Space::memory_space{})); +} + +// public interface that accepts arbitrary view constructor args passed by a +// view_alloc +template ::specialize>>> auto create_mirror(Impl::ViewCtorProp const& arg_prop, - Kokkos::View const& v) { - return Impl::create_mirror(v, arg_prop); -} - -template -std::enable_if_t< - std::is_void::specialize>::value && - !Impl::ViewCtorProp::has_memory_space, - typename Kokkos::View::HostMirror> -create_mirror(Impl::ViewCtorProp const& arg_prop, - Kokkos::View const& v) { - return Impl::create_mirror(v, arg_prop); + Kokkos::View const& src) { + return Impl::create_mirror(src, arg_prop); } +// public interface that accepts a space and a without initializing flag template ::value>> -std::enable_if_t::specialize>::value, - typename Impl::MirrorType::view_type> -create_mirror(Kokkos::Impl::WithoutInitializing_t wi, Space const&, - Kokkos::View const& v) { - return Impl::create_mirror(v, view_alloc(typename Space::memory_space{}, wi)); + typename Enable = std::enable_if_t< + Kokkos::is_space::value && + std::is_void_v::specialize>>> +auto create_mirror(Kokkos::Impl::WithoutInitializing_t wi, Space const&, + Kokkos::View const& src) { + return Impl::create_mirror(src, + view_alloc(typename Space::memory_space{}, wi)); } namespace Impl { +// choose a `Kokkos::create_mirror` adapted for the provided view and the +// provided arguments +template +inline auto choose_create_mirror( + const View& src, const Impl::ViewCtorProp& arg_prop) { + // Due to the fact that users can overload `Kokkos::create_mirror`, but also + // that they may not have implemented all of its different possible + // variations, this function chooses the correct private or public version of + // it to call. + // This helper should be used by any overload of + // `Kokkos::Impl::create_mirror_view`. + + if constexpr (std::is_void_v) { + // if the view is not specialized, just call the Impl function + + // using ADL to find the later defined overload of the function + using namespace Kokkos::Impl; + + return create_mirror(src, arg_prop); + } else { + // otherwise, recreate the public call + using ViewProp = Impl::ViewCtorProp; + + // using ADL to find the later defined overload of the function + using namespace Kokkos; + + if constexpr (sizeof...(ViewCtorArgs) == 0) { + // if there are no view constructor args, call the specific public + // function + return create_mirror(src); + } else if constexpr (sizeof...(ViewCtorArgs) == 1 && + ViewProp::has_memory_space) { + // if there is one view constructor arg and it has a memory space, call + // the specific public function + return create_mirror(typename ViewProp::memory_space{}, src); + } else if constexpr (sizeof...(ViewCtorArgs) == 1 && + !ViewProp::initialize) { + // if there is one view constructor arg and it has a without initializing + // mark, call the specific public function + return create_mirror(typename Kokkos::Impl::WithoutInitializing_t{}, src); + } else if constexpr (sizeof...(ViewCtorArgs) == 2 && + ViewProp::has_memory_space && !ViewProp::initialize) { + // if there is two view constructor args and they have a memory space and + // a without initializing mark, call the specific public function + return create_mirror(typename Kokkos::Impl::WithoutInitializing_t{}, + typename ViewProp::memory_space{}, src); + } else { + // if there are other constructor args, call the generic public function + + // Beware, there are some libraries using Kokkos that don't implement + // this overload (hence the reason for this present function to exist). + return create_mirror(arg_prop, src); + } + } + +#if defined(KOKKOS_COMPILER_INTEL) || \ + (defined(KOKKOS_COMPILER_NVCC) && KOKKOS_COMPILER_NVCC >= 1130 && \ + !defined(KOKKOS_COMPILER_MSVC)) + __builtin_unreachable(); +#endif +} + +// create a mirror view +// private interface that accepts arbitrary view constructor args passed by a +// view_alloc template -inline std::enable_if_t< - !Impl::ViewCtorProp::has_memory_space && - (std::is_same< - typename Kokkos::View::memory_space, - typename Kokkos::View::HostMirror::memory_space>::value && - std::is_same< - typename Kokkos::View::data_type, - typename Kokkos::View::HostMirror::data_type>::value), - typename Kokkos::View::HostMirror> -create_mirror_view(const Kokkos::View& src, - const Impl::ViewCtorProp&) { - check_view_ctor_args_create_mirror(); - return src; -} - -template -inline std::enable_if_t< - !Impl::ViewCtorProp::has_memory_space && - !(std::is_same::memory_space, - typename Kokkos::View< - T, P...>::HostMirror::memory_space>::value && - std::is_same< - typename Kokkos::View::data_type, - typename Kokkos::View::HostMirror::data_type>::value), - typename Kokkos::View::HostMirror> -create_mirror_view(const Kokkos::View& src, - const Impl::ViewCtorProp& arg_prop) { - return Kokkos::Impl::create_mirror(src, arg_prop); -} - -// Create a mirror view in a new space (specialization for same space) -template ::has_memory_space>> -std::enable_if_t::memory_space, - T, P...>::is_same_memspace, - typename Impl::MirrorViewType< - typename Impl::ViewCtorProp::memory_space, - T, P...>::view_type> -create_mirror_view(const Kokkos::View& src, - const Impl::ViewCtorProp&) { - check_view_ctor_args_create_mirror(); - return src; -} - -// Create a mirror view in a new space (specialization for different space) -template ::has_memory_space>> -std::enable_if_t::memory_space, - T, P...>::is_same_memspace, - typename Impl::MirrorViewType< - typename Impl::ViewCtorProp::memory_space, - T, P...>::view_type> -create_mirror_view(const Kokkos::View& src, - const Impl::ViewCtorProp& arg_prop) { - return Kokkos::Impl::create_mirror(src, arg_prop); +inline auto create_mirror_view( + const Kokkos::View& src, + [[maybe_unused]] const Impl::ViewCtorProp& arg_prop) { + if constexpr (!Impl::ViewCtorProp::has_memory_space) { + if constexpr (std::is_same::memory_space, + typename Kokkos::View< + T, P...>::HostMirror::memory_space>::value && + std::is_same::data_type, + typename Kokkos::View< + T, P...>::HostMirror::data_type>::value) { + check_view_ctor_args_create_mirror(); + return typename Kokkos::View::HostMirror(src); + } else { + return Kokkos::Impl::choose_create_mirror(src, arg_prop); + } + } else { + if constexpr (Impl::MirrorViewType::memory_space, + T, P...>::is_same_memspace) { + check_view_ctor_args_create_mirror(); + return typename Impl::MirrorViewType< + typename Impl::ViewCtorProp::memory_space, T, + P...>::view_type(src); + } else { + return Kokkos::Impl::choose_create_mirror(src, arg_prop); + } + } +#if defined(KOKKOS_COMPILER_INTEL) || \ + (defined(KOKKOS_COMPILER_NVCC) && KOKKOS_COMPILER_NVCC >= 1130 && \ + !defined(KOKKOS_COMPILER_MSVC)) + __builtin_unreachable(); +#endif } } // namespace Impl +// public interface template -std::enable_if_t< - std::is_same< - typename Kokkos::View::memory_space, - typename Kokkos::View::HostMirror::memory_space>::value && - std::is_same< - typename Kokkos::View::data_type, - typename Kokkos::View::HostMirror::data_type>::value, - typename Kokkos::View::HostMirror> -create_mirror_view(const Kokkos::View& src) { - return src; +auto create_mirror_view(const Kokkos::View& src) { + return Impl::create_mirror_view(src, view_alloc()); } +// public interface that accepts a without initializing flag template -std::enable_if_t< - !(std::is_same< - typename Kokkos::View::memory_space, - typename Kokkos::View::HostMirror::memory_space>::value && - std::is_same< - typename Kokkos::View::data_type, - typename Kokkos::View::HostMirror::data_type>::value), - typename Kokkos::View::HostMirror> -create_mirror_view(const Kokkos::View& src) { - return Kokkos::create_mirror(src); +auto create_mirror_view(Kokkos::Impl::WithoutInitializing_t wi, + Kokkos::View const& src) { + return Impl::create_mirror_view(src, view_alloc(wi)); } -template -typename Kokkos::View::HostMirror create_mirror_view( - Kokkos::Impl::WithoutInitializing_t wi, Kokkos::View const& v) { - return Impl::create_mirror_view(v, view_alloc(wi)); -} - -// FIXME_C++17 Improve SFINAE here. +// public interface that accepts a space template ::value>> -typename Impl::MirrorViewType::view_type create_mirror_view( - const Space&, const Kokkos::View& src, - std::enable_if_t::is_same_memspace>* = - nullptr) { - return src; -} - -// FIXME_C++17 Improve SFINAE here. -template ::value>> -typename Impl::MirrorViewType::view_type create_mirror_view( - const Space& space, const Kokkos::View& src, - std::enable_if_t::is_same_memspace>* = - nullptr) { - return Kokkos::create_mirror(space, src); +auto create_mirror_view(const Space&, const Kokkos::View& src) { + return Impl::create_mirror_view(src, + view_alloc(typename Space::memory_space())); } +// public interface that accepts a space and a without initializing flag template ::value>> -typename Impl::MirrorViewType::view_type create_mirror_view( - Kokkos::Impl::WithoutInitializing_t wi, Space const&, - Kokkos::View const& v) { +auto create_mirror_view(Kokkos::Impl::WithoutInitializing_t wi, Space const&, + Kokkos::View const& src) { return Impl::create_mirror_view( - v, view_alloc(typename Space::memory_space{}, wi)); + src, view_alloc(typename Space::memory_space{}, wi)); } -template +// public interface that accepts arbitrary view constructor args passed by a +// view_alloc +template ::specialize>>> auto create_mirror_view(const Impl::ViewCtorProp& arg_prop, - const Kokkos::View& v) { - return Impl::create_mirror_view(v, arg_prop); + const Kokkos::View& src) { + return Impl::create_mirror_view(src, arg_prop); } -template -auto create_mirror_view_and_copy( - const Impl::ViewCtorProp&, - const Kokkos::View& src, - std::enable_if_t< - std::is_void::specialize>::value && - Impl::MirrorViewType< - typename Impl::ViewCtorProp::memory_space, T, - P...>::is_same_memspace>* = nullptr) { +namespace Impl { + +// collection of static asserts for create_mirror_view_and_copy +template +void check_view_ctor_args_create_mirror_view_and_copy() { using alloc_prop_input = Impl::ViewCtorProp; + static_assert( alloc_prop_input::has_memory_space, "The view constructor arguments passed to " @@ -3708,52 +3726,53 @@ auto create_mirror_view_and_copy( "The view constructor arguments passed to " "Kokkos::create_mirror_view_and_copy must " "not explicitly allow padding!"); - - // same behavior as deep_copy(src, src) - if (!alloc_prop_input::has_execution_space) - fence( - "Kokkos::create_mirror_view_and_copy: fence before returning src view"); - return src; } -template +} // namespace Impl + +// create a mirror view and deep copy it +// public interface that accepts arbitrary view constructor args passed by a +// view_alloc +template ::specialize>>> auto create_mirror_view_and_copy( - const Impl::ViewCtorProp& arg_prop, - const Kokkos::View& src, - std::enable_if_t< - std::is_void::specialize>::value && - !Impl::MirrorViewType< - typename Impl::ViewCtorProp::memory_space, T, - P...>::is_same_memspace>* = nullptr) { + [[maybe_unused]] const Impl::ViewCtorProp& arg_prop, + const Kokkos::View& src) { using alloc_prop_input = Impl::ViewCtorProp; - static_assert( - alloc_prop_input::has_memory_space, - "The view constructor arguments passed to " - "Kokkos::create_mirror_view_and_copy must include a memory space!"); - static_assert(!alloc_prop_input::has_pointer, - "The view constructor arguments passed to " - "Kokkos::create_mirror_view_and_copy must " - "not include a pointer!"); - static_assert(!alloc_prop_input::allow_padding, - "The view constructor arguments passed to " - "Kokkos::create_mirror_view_and_copy must " - "not explicitly allow padding!"); - using Space = typename alloc_prop_input::memory_space; - using Mirror = typename Impl::MirrorViewType::view_type; - auto arg_prop_copy = Impl::with_properties_if_unset( - arg_prop, std::string{}, WithoutInitializing, - typename Space::execution_space{}); + Impl::check_view_ctor_args_create_mirror_view_and_copy(); - std::string& label = Impl::get_property(arg_prop_copy); - if (label.empty()) label = src.label(); - auto mirror = typename Mirror::non_const_type{arg_prop_copy, src.layout()}; - if constexpr (alloc_prop_input::has_execution_space) { - deep_copy(Impl::get_property(arg_prop_copy), - mirror, src); - } else - deep_copy(mirror, src); - return mirror; + if constexpr (Impl::MirrorViewType::is_same_memspace) { + // same behavior as deep_copy(src, src) + if constexpr (!alloc_prop_input::has_execution_space) + fence( + "Kokkos::create_mirror_view_and_copy: fence before returning src " + "view"); + return src; + } else { + using Space = typename alloc_prop_input::memory_space; + using Mirror = typename Impl::MirrorViewType::view_type; + + auto arg_prop_copy = Impl::with_properties_if_unset( + arg_prop, std::string{}, WithoutInitializing, + typename Space::execution_space{}); + + std::string& label = Impl::get_property(arg_prop_copy); + if (label.empty()) label = src.label(); + auto mirror = typename Mirror::non_const_type{arg_prop_copy, src.layout()}; + if constexpr (alloc_prop_input::has_execution_space) { + deep_copy(Impl::get_property(arg_prop_copy), + mirror, src); + } else + deep_copy(mirror, src); + return mirror; + } +#if defined(KOKKOS_COMPILER_NVCC) && KOKKOS_COMPILER_NVCC >= 1130 && \ + !defined(KOKKOS_COMPILER_MSVC) + __builtin_unreachable(); +#endif } // Previously when using auto here, the intel compiler 19.3 would diff --git a/lib/kokkos/core/src/Kokkos_ExecPolicy.hpp b/lib/kokkos/core/src/Kokkos_ExecPolicy.hpp index 5f251eeb26..b8d7f77deb 100644 --- a/lib/kokkos/core/src/Kokkos_ExecPolicy.hpp +++ b/lib/kokkos/core/src/Kokkos_ExecPolicy.hpp @@ -40,7 +40,12 @@ struct ParallelReduceTag {}; struct ChunkSize { int value; + explicit ChunkSize(int value_) : value(value_) {} +#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4 + template + KOKKOS_DEPRECATED_WITH_COMMENT("ChunkSize should be constructed explicitly.") ChunkSize(int value_) : value(value_) {} +#endif }; /** \brief Execution policy for work over a range of an integral type. @@ -714,6 +719,58 @@ class TeamPolicy } }; +// Execution space not provided deduces to TeamPolicy<> + +TeamPolicy()->TeamPolicy<>; + +TeamPolicy(int, int)->TeamPolicy<>; +TeamPolicy(int, int, int)->TeamPolicy<>; +TeamPolicy(int, Kokkos::AUTO_t const&)->TeamPolicy<>; +TeamPolicy(int, Kokkos::AUTO_t const&, int)->TeamPolicy<>; +TeamPolicy(int, Kokkos::AUTO_t const&, Kokkos::AUTO_t const&)->TeamPolicy<>; +TeamPolicy(int, int, Kokkos::AUTO_t const&)->TeamPolicy<>; + +// DefaultExecutionSpace deduces to TeamPolicy<> + +TeamPolicy(DefaultExecutionSpace const&, int, int)->TeamPolicy<>; +TeamPolicy(DefaultExecutionSpace const&, int, int, int)->TeamPolicy<>; +TeamPolicy(DefaultExecutionSpace const&, int, Kokkos::AUTO_t const&) + ->TeamPolicy<>; +TeamPolicy(DefaultExecutionSpace const&, int, Kokkos::AUTO_t const&, int) + ->TeamPolicy<>; +TeamPolicy(DefaultExecutionSpace const&, int, Kokkos::AUTO_t const&, + Kokkos::AUTO_t const&) + ->TeamPolicy<>; +TeamPolicy(DefaultExecutionSpace const&, int, int, Kokkos::AUTO_t const&) + ->TeamPolicy<>; + +// ES != DefaultExecutionSpace deduces to TeamPolicy + +template >> +TeamPolicy(ES const&, int, int)->TeamPolicy; + +template >> +TeamPolicy(ES const&, int, int, int)->TeamPolicy; + +template >> +TeamPolicy(ES const&, int, Kokkos::AUTO_t const&)->TeamPolicy; + +template >> +TeamPolicy(ES const&, int, Kokkos::AUTO_t const&, int)->TeamPolicy; + +template >> +TeamPolicy(ES const&, int, Kokkos::AUTO_t const&, Kokkos::AUTO_t const&) + ->TeamPolicy; + +template >> +TeamPolicy(ES const&, int, int, Kokkos::AUTO_t const&)->TeamPolicy; + namespace Impl { template @@ -968,9 +1025,9 @@ struct TeamThreadMDRange, TeamHandle> { static constexpr auto par_vector = Impl::TeamMDRangeParVector::NotParVector; static constexpr Iterate direction = - OuterDir == Iterate::Default - ? layout_iterate_type_selector::outer_iteration_pattern - : iter; + OuterDir == Iterate::Default ? Impl::layout_iterate_type_selector< + ArrayLayout>::outer_iteration_pattern + : iter; template KOKKOS_FUNCTION TeamThreadMDRange(TeamHandleType const& team_, Args&&... args) @@ -983,7 +1040,7 @@ struct TeamThreadMDRange, TeamHandle> { }; template -TeamThreadMDRange(TeamHandle const&, Args&&...) +KOKKOS_DEDUCTION_GUIDE TeamThreadMDRange(TeamHandle const&, Args&&...) ->TeamThreadMDRange, TeamHandle>; template @@ -1004,9 +1061,9 @@ struct ThreadVectorMDRange, TeamHandle> { static constexpr auto par_vector = Impl::TeamMDRangeParVector::ParVector; static constexpr Iterate direction = - OuterDir == Iterate::Default - ? layout_iterate_type_selector::outer_iteration_pattern - : iter; + OuterDir == Iterate::Default ? Impl::layout_iterate_type_selector< + ArrayLayout>::outer_iteration_pattern + : iter; template KOKKOS_INLINE_FUNCTION ThreadVectorMDRange(TeamHandleType const& team_, @@ -1020,7 +1077,7 @@ struct ThreadVectorMDRange, TeamHandle> { }; template -ThreadVectorMDRange(TeamHandle const&, Args&&...) +KOKKOS_DEDUCTION_GUIDE ThreadVectorMDRange(TeamHandle const&, Args&&...) ->ThreadVectorMDRange, TeamHandle>; template @@ -1041,9 +1098,9 @@ struct TeamVectorMDRange, TeamHandle> { static constexpr auto par_vector = Impl::TeamMDRangeParVector::ParVector; static constexpr Iterate direction = - iter == Iterate::Default - ? layout_iterate_type_selector::outer_iteration_pattern - : iter; + iter == Iterate::Default ? Impl::layout_iterate_type_selector< + ArrayLayout>::outer_iteration_pattern + : iter; template KOKKOS_INLINE_FUNCTION TeamVectorMDRange(TeamHandleType const& team_, @@ -1057,7 +1114,7 @@ struct TeamVectorMDRange, TeamHandle> { }; template -TeamVectorMDRange(TeamHandle const&, Args&&...) +KOKKOS_DEDUCTION_GUIDE TeamVectorMDRange(TeamHandle const&, Args&&...) ->TeamVectorMDRange, TeamHandle>; template #include #include +#ifdef KOKKOS_ENABLE_IMPL_MDSPAN +#include +#else +#include +#endif namespace Kokkos { + +#ifndef KOKKOS_ENABLE_IMPL_MDSPAN +constexpr size_t dynamic_extent = std::numeric_limits::max(); +#endif + namespace Experimental { -constexpr ptrdiff_t dynamic_extent = -1; - -template +template struct Extents { /* TODO @enhancement flesh this out more */ }; -template +template struct PrependExtent; -template +template struct PrependExtent, NewExtent> { using type = Extents; }; -template +template struct AppendExtent; -template +template struct AppendExtent, NewExtent> { using type = Extents; }; - } // end namespace Experimental namespace Impl { @@ -75,33 +82,32 @@ struct _parse_impl { // We have to treat the case of int**[x] specially, since it *doesn't* go // backwards -template +template struct _parse_impl, std::enable_if_t<_all_remaining_extents_dynamic::value>> - : _parse_impl> { -}; + : _parse_impl> {}; // int*(*[x])[y] should still work also (meaning int[][x][][y]) -template +template struct _parse_impl< T*, Kokkos::Experimental::Extents, std::enable_if_t::value>> { using _next = Kokkos::Experimental::AppendExtent< typename _parse_impl, void>::type, - Kokkos::Experimental::dynamic_extent>; + Kokkos::dynamic_extent>; using type = typename _next::type; }; -template +template struct _parse_impl, void> - : _parse_impl< - T, Kokkos::Experimental::Extents // TODO @pedantic this - // could be a - // narrowing cast - > {}; + : _parse_impl // TODO @pedantic + // this could be a + // narrowing cast + > {}; } // end namespace _parse_view_extents_impl @@ -111,38 +117,34 @@ struct ParseViewExtents { DataType, Kokkos::Experimental::Extents<>>::type; }; -template +template struct ApplyExtent { using type = ValueType[Ext]; }; template -struct ApplyExtent { +struct ApplyExtent { using type = ValueType*; }; -template +template struct ApplyExtent { using type = typename ApplyExtent::type[N]; }; -template +template struct ApplyExtent { using type = ValueType * [Ext]; }; template -struct ApplyExtent { - using type = - typename ApplyExtent::type*; +struct ApplyExtent { + using type = typename ApplyExtent::type*; }; template -struct ApplyExtent { - using type = - typename ApplyExtent::type[N]; +struct ApplyExtent { + using type = typename ApplyExtent::type[N]; }; } // end namespace Impl diff --git a/lib/kokkos/core/src/Kokkos_Graph.hpp b/lib/kokkos/core/src/Kokkos_Graph.hpp index 643bdcc02c..9cc6650e26 100644 --- a/lib/kokkos/core/src/Kokkos_Graph.hpp +++ b/lib/kokkos/core/src/Kokkos_Graph.hpp @@ -167,6 +167,9 @@ Graph create_graph(Closure&& arg_closure) { #include #endif #endif +#ifdef SYCL_EXT_ONEAPI_GRAPH +#include +#endif #ifdef KOKKOS_IMPL_PUBLIC_INCLUDE_NOTDEFINED_GRAPH #undef KOKKOS_IMPL_PUBLIC_INCLUDE #undef KOKKOS_IMPL_PUBLIC_INCLUDE_NOTDEFINED_GRAPH diff --git a/lib/kokkos/core/src/Kokkos_HostSpace.hpp b/lib/kokkos/core/src/Kokkos_HostSpace.hpp index a1fb0f5a67..8b5f29f95b 100644 --- a/lib/kokkos/core/src/Kokkos_HostSpace.hpp +++ b/lib/kokkos/core/src/Kokkos_HostSpace.hpp @@ -113,7 +113,6 @@ class HostSpace { const size_t arg_alloc_size, const size_t arg_logical_size = 0) const; - private: void* impl_allocate(const char* arg_label, const size_t arg_alloc_size, const size_t arg_logical_size = 0, const Kokkos::Tools::SpaceHandle = @@ -124,7 +123,6 @@ class HostSpace { const Kokkos::Tools::SpaceHandle = Kokkos::Tools::make_space_handle(name())) const; - public: /**\brief Return Name of the MemorySpace */ static constexpr const char* name() { return m_name; } diff --git a/lib/kokkos/core/src/Kokkos_Layout.hpp b/lib/kokkos/core/src/Kokkos_Layout.hpp index ca4d956784..37b80e54a8 100644 --- a/lib/kokkos/core/src/Kokkos_Layout.hpp +++ b/lib/kokkos/core/src/Kokkos_Layout.hpp @@ -217,81 +217,12 @@ enum class Iterate { Right // Right indices stride fastest }; -// To check for LayoutTiled -// This is to hide extra compile-time 'identifier' info within the LayoutTiled -// class by not relying on template specialization to include the ArgN*'s -template -struct is_layouttiled : std::false_type {}; - -template -struct is_layouttiled> - : std::true_type {}; - -namespace Experimental { - -/// LayoutTiled -// Must have Rank >= 2 -template < - Kokkos::Iterate OuterP, Kokkos::Iterate InnerP, unsigned ArgN0, - unsigned ArgN1, unsigned ArgN2 = 0, unsigned ArgN3 = 0, unsigned ArgN4 = 0, - unsigned ArgN5 = 0, unsigned ArgN6 = 0, unsigned ArgN7 = 0, - bool IsPowerOfTwo = - (Kokkos::Impl::is_integral_power_of_two(ArgN0) && - Kokkos::Impl::is_integral_power_of_two(ArgN1) && - (Kokkos::Impl::is_integral_power_of_two(ArgN2) || (ArgN2 == 0)) && - (Kokkos::Impl::is_integral_power_of_two(ArgN3) || (ArgN3 == 0)) && - (Kokkos::Impl::is_integral_power_of_two(ArgN4) || (ArgN4 == 0)) && - (Kokkos::Impl::is_integral_power_of_two(ArgN5) || (ArgN5 == 0)) && - (Kokkos::Impl::is_integral_power_of_two(ArgN6) || (ArgN6 == 0)) && - (Kokkos::Impl::is_integral_power_of_two(ArgN7) || (ArgN7 == 0)))> -struct LayoutTiled { - static_assert(IsPowerOfTwo, - "LayoutTiled must be given power-of-two tile dimensions"); - - using array_layout = LayoutTiled; - static constexpr Iterate outer_pattern = OuterP; - static constexpr Iterate inner_pattern = InnerP; - - enum { N0 = ArgN0 }; - enum { N1 = ArgN1 }; - enum { N2 = ArgN2 }; - enum { N3 = ArgN3 }; - enum { N4 = ArgN4 }; - enum { N5 = ArgN5 }; - enum { N6 = ArgN6 }; - enum { N7 = ArgN7 }; - - size_t dimension[ARRAY_LAYOUT_MAX_RANK]; - - enum : bool { is_extent_constructible = true }; - - LayoutTiled(LayoutTiled const&) = default; - LayoutTiled(LayoutTiled&&) = default; - LayoutTiled& operator=(LayoutTiled const&) = default; - LayoutTiled& operator=(LayoutTiled&&) = default; - - KOKKOS_INLINE_FUNCTION - explicit constexpr LayoutTiled(size_t argN0 = 0, size_t argN1 = 0, - size_t argN2 = 0, size_t argN3 = 0, - size_t argN4 = 0, size_t argN5 = 0, - size_t argN6 = 0, size_t argN7 = 0) - : dimension{argN0, argN1, argN2, argN3, argN4, argN5, argN6, argN7} {} - - friend bool operator==(const LayoutTiled& left, const LayoutTiled& right) { - for (unsigned int rank = 0; rank < ARRAY_LAYOUT_MAX_RANK; ++rank) - if (left.dimension[rank] != right.dimension[rank]) return false; - return true; - } - - friend bool operator!=(const LayoutTiled& left, const LayoutTiled& right) { - return !(left == right); - } -}; - -} // namespace Experimental +#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4 +template +struct KOKKOS_DEPRECATED is_layouttiled : std::false_type {}; +#endif +namespace Impl { // For use with view_copy template struct layout_iterate_type_selector { @@ -320,42 +251,13 @@ struct layout_iterate_type_selector { static const Kokkos::Iterate inner_iteration_pattern = Kokkos::Iterate::Default; }; +} // namespace Impl -template -struct layout_iterate_type_selector> { - static const Kokkos::Iterate outer_iteration_pattern = Kokkos::Iterate::Left; - static const Kokkos::Iterate inner_iteration_pattern = Kokkos::Iterate::Left; -}; - -template -struct layout_iterate_type_selector> { - static const Kokkos::Iterate outer_iteration_pattern = Kokkos::Iterate::Right; - static const Kokkos::Iterate inner_iteration_pattern = Kokkos::Iterate::Left; -}; - -template -struct layout_iterate_type_selector> { - static const Kokkos::Iterate outer_iteration_pattern = Kokkos::Iterate::Left; - static const Kokkos::Iterate inner_iteration_pattern = Kokkos::Iterate::Right; -}; - -template -struct layout_iterate_type_selector> { - static const Kokkos::Iterate outer_iteration_pattern = Kokkos::Iterate::Right; - static const Kokkos::Iterate inner_iteration_pattern = Kokkos::Iterate::Right; -}; +#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4 +template +using layout_iterate_type_selector KOKKOS_DEPRECATED = + Impl::layout_iterate_type_selector; +#endif } // namespace Kokkos diff --git a/lib/kokkos/core/src/Kokkos_Macros.hpp b/lib/kokkos/core/src/Kokkos_Macros.hpp index b255d2a519..0a0acd303f 100644 --- a/lib/kokkos/core/src/Kokkos_Macros.hpp +++ b/lib/kokkos/core/src/Kokkos_Macros.hpp @@ -55,9 +55,22 @@ #ifndef KOKKOS_DONT_INCLUDE_CORE_CONFIG_H #include +#include #include #endif +#if !defined(KOKKOS_ENABLE_CXX17) +#if __has_include() +#include +#else +#include +#endif +#if defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE < 10 +#error \ + "Compiling with support for C++20 or later requires a libstdc++ version later than 9" +#endif +#endif + //---------------------------------------------------------------------------- /** Pick up compiler specific #define macros: * @@ -332,6 +345,10 @@ #define KOKKOS_DEFAULTED_FUNCTION #endif +#if !defined(KOKKOS_DEDUCTION_GUIDE) +#define KOKKOS_DEDUCTION_GUIDE +#endif + #if !defined(KOKKOS_IMPL_HOST_FUNCTION) #define KOKKOS_IMPL_HOST_FUNCTION #endif @@ -562,8 +579,44 @@ static constexpr bool kokkos_omp_on_host() { return false; } #define KOKKOS_IMPL_WARNING(desc) KOKKOS_IMPL_DO_PRAGMA(message(#desc)) #endif +// clang-format off +#if defined(__NVCOMPILER) + #define KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_PUSH() \ + _Pragma("diag_suppress 1216") + #define KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_POP() \ + _Pragma("diag_default 1216") +#elif defined(__EDG__) + #define KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_PUSH() \ + _Pragma("warning push") \ + _Pragma("warning disable 1478") + #define KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_POP() \ + _Pragma("warning pop") +#elif defined(__GNUC__) || defined(__clang__) + #define KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_PUSH() \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") + #define KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_POP() \ + _Pragma("GCC diagnostic pop") +#elif defined(_MSC_VER) + #define KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_PUSH() \ + _Pragma("warning(push)") \ + _Pragma("warning(disable: 4996)") + #define KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_POP() \ + _Pragma("warning(pop)") +#else + #define KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_PUSH() + #define KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_POP() +#endif +// clang-format on + #define KOKKOS_ATTRIBUTE_NODISCARD [[nodiscard]] +#ifndef KOKKOS_ENABLE_CXX17 +#define KOKKOS_IMPL_ATTRIBUTE_UNLIKELY [[unlikely]] +#else +#define KOKKOS_IMPL_ATTRIBUTE_UNLIKELY +#endif + #if (defined(KOKKOS_COMPILER_GNU) || defined(KOKKOS_COMPILER_CLANG) || \ defined(KOKKOS_COMPILER_INTEL) || defined(KOKKOS_COMPILER_INTEL_LLVM) || \ defined(KOKKOS_COMPILER_NVHPC)) && \ diff --git a/lib/kokkos/core/src/Kokkos_MathematicalFunctions.hpp b/lib/kokkos/core/src/Kokkos_MathematicalFunctions.hpp index 3fead8dd29..19967782e5 100644 --- a/lib/kokkos/core/src/Kokkos_MathematicalFunctions.hpp +++ b/lib/kokkos/core/src/Kokkos_MathematicalFunctions.hpp @@ -277,12 +277,20 @@ KOKKOS_INLINE_FUNCTION long long abs(long long n) { #endif } KOKKOS_INLINE_FUNCTION float abs(float x) { +#ifdef KOKKOS_ENABLE_SYCL + return sycl::fabs(x); // sycl::abs is only provided for integral types +#else using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::abs; return abs(x); +#endif } KOKKOS_INLINE_FUNCTION double abs(double x) { +#ifdef KOKKOS_ENABLE_SYCL + return sycl::fabs(x); // sycl::abs is only provided for integral types +#else using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::abs; return abs(x); +#endif } inline long double abs(long double x) { using std::abs; diff --git a/lib/kokkos/core/src/Kokkos_Pair.hpp b/lib/kokkos/core/src/Kokkos_Pair.hpp index 9be8d8d7aa..e569fefc14 100644 --- a/lib/kokkos/core/src/Kokkos_Pair.hpp +++ b/lib/kokkos/core/src/Kokkos_Pair.hpp @@ -413,12 +413,13 @@ KOKKOS_FORCEINLINE_FUNCTION pair tie(T1& x, T2& y) { return (pair(x, y)); } +#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4 // // Specialization of Kokkos::pair for a \c void second argument. This // is not actually a "pair"; it only contains one element, the first. // template -struct pair { +struct KOKKOS_DEPRECATED pair { using first_type = T1; using second_type = void; @@ -448,41 +449,48 @@ struct pair { // Specialization of relational operators for Kokkos::pair. // +#if defined(KOKKOS_COMPILER_GNU) && (KOKKOS_COMPILER_GNU < 1110) +KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_PUSH() +#endif template -KOKKOS_FORCEINLINE_FUNCTION constexpr bool operator==( +KOKKOS_DEPRECATED KOKKOS_FORCEINLINE_FUNCTION constexpr bool operator==( const pair& lhs, const pair& rhs) { return lhs.first == rhs.first; } template -KOKKOS_FORCEINLINE_FUNCTION constexpr bool operator!=( +KOKKOS_DEPRECATED KOKKOS_FORCEINLINE_FUNCTION constexpr bool operator!=( const pair& lhs, const pair& rhs) { return !(lhs == rhs); } template -KOKKOS_FORCEINLINE_FUNCTION constexpr bool operator<( +KOKKOS_DEPRECATED KOKKOS_FORCEINLINE_FUNCTION constexpr bool operator<( const pair& lhs, const pair& rhs) { return lhs.first < rhs.first; } template -KOKKOS_FORCEINLINE_FUNCTION constexpr bool operator<=( +KOKKOS_DEPRECATED KOKKOS_FORCEINLINE_FUNCTION constexpr bool operator<=( const pair& lhs, const pair& rhs) { return !(rhs < lhs); } template -KOKKOS_FORCEINLINE_FUNCTION constexpr bool operator>( +KOKKOS_DEPRECATED KOKKOS_FORCEINLINE_FUNCTION constexpr bool operator>( const pair& lhs, const pair& rhs) { return rhs < lhs; } template -KOKKOS_FORCEINLINE_FUNCTION constexpr bool operator>=( +KOKKOS_DEPRECATED KOKKOS_FORCEINLINE_FUNCTION constexpr bool operator>=( const pair& lhs, const pair& rhs) { return !(lhs < rhs); } +#if defined(KOKKOS_COMPILER_GNU) && (KOKKOS_COMPILER_GNU < 1110) +KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_POP() +#endif +#endif namespace Impl { template diff --git a/lib/kokkos/core/src/Kokkos_Parallel.hpp b/lib/kokkos/core/src/Kokkos_Parallel.hpp index 484f6c0d5f..122239df79 100644 --- a/lib/kokkos/core/src/Kokkos_Parallel.hpp +++ b/lib/kokkos/core/src/Kokkos_Parallel.hpp @@ -137,9 +137,9 @@ inline void parallel_for(const std::string& str, const ExecPolicy& policy, ExecPolicy inner_policy = policy; Kokkos::Tools::Impl::begin_parallel_for(inner_policy, functor, str, kpID); - Kokkos::Impl::shared_allocation_tracking_disable(); - Impl::ParallelFor closure(functor, inner_policy); - Kokkos::Impl::shared_allocation_tracking_enable(); + auto closure = + Kokkos::Impl::construct_with_shared_allocation_tracking_disabled< + Impl::ParallelFor>(functor, inner_policy); closure.execute(); @@ -352,10 +352,10 @@ inline void parallel_scan(const std::string& str, const ExecutionPolicy& policy, ExecutionPolicy inner_policy = policy; Kokkos::Tools::Impl::begin_parallel_scan(inner_policy, functor, str, kpID); - Kokkos::Impl::shared_allocation_tracking_disable(); - Impl::ParallelScan closure(functor, - inner_policy); - Kokkos::Impl::shared_allocation_tracking_enable(); + auto closure = + Kokkos::Impl::construct_with_shared_allocation_tracking_disabled< + Impl::ParallelScan>(functor, + inner_policy); closure.execute(); @@ -398,18 +398,19 @@ inline void parallel_scan(const std::string& str, const ExecutionPolicy& policy, Kokkos::Tools::Impl::begin_parallel_scan(inner_policy, functor, str, kpID); if constexpr (Kokkos::is_view::value) { - Kokkos::Impl::shared_allocation_tracking_disable(); - Impl::ParallelScanWithTotal - closure(functor, inner_policy, return_value); - Kokkos::Impl::shared_allocation_tracking_enable(); + auto closure = + Kokkos::Impl::construct_with_shared_allocation_tracking_disabled< + Impl::ParallelScanWithTotal>( + functor, inner_policy, return_value); closure.execute(); } else { - Kokkos::Impl::shared_allocation_tracking_disable(); Kokkos::View view(&return_value); - Impl::ParallelScanWithTotal - closure(functor, inner_policy, view); - Kokkos::Impl::shared_allocation_tracking_enable(); + auto closure = + Kokkos::Impl::construct_with_shared_allocation_tracking_disabled< + Impl::ParallelScanWithTotal>(functor, inner_policy, + view); closure.execute(); } diff --git a/lib/kokkos/core/src/Kokkos_Parallel_Reduce.hpp b/lib/kokkos/core/src/Kokkos_Parallel_Reduce.hpp index d499eba6dc..53913266f1 100644 --- a/lib/kokkos/core/src/Kokkos_Parallel_Reduce.hpp +++ b/lib/kokkos/core/src/Kokkos_Parallel_Reduce.hpp @@ -72,7 +72,7 @@ struct Sum { }; template -Sum(View const&) +KOKKOS_DEDUCTION_GUIDE Sum(View const&) ->Sum::memory_space>; template @@ -117,7 +117,7 @@ struct Prod { }; template -Prod(View const&) +KOKKOS_DEDUCTION_GUIDE Prod(View const&) ->Prod::memory_space>; template @@ -164,7 +164,7 @@ struct Min { }; template -Min(View const&) +KOKKOS_DEDUCTION_GUIDE Min(View const&) ->Min::memory_space>; template @@ -212,7 +212,7 @@ struct Max { }; template -Max(View const&) +KOKKOS_DEDUCTION_GUIDE Max(View const&) ->Max::memory_space>; template @@ -258,7 +258,7 @@ struct LAnd { }; template -LAnd(View const&) +KOKKOS_DEDUCTION_GUIDE LAnd(View const&) ->LAnd::memory_space>; template @@ -305,7 +305,7 @@ struct LOr { }; template -LOr(View const&) +KOKKOS_DEDUCTION_GUIDE LOr(View const&) ->LOr::memory_space>; template @@ -352,7 +352,7 @@ struct BAnd { }; template -BAnd(View const&) +KOKKOS_DEDUCTION_GUIDE BAnd(View const&) ->BAnd::memory_space>; template @@ -399,7 +399,7 @@ struct BOr { }; template -BOr(View const&) +KOKKOS_DEDUCTION_GUIDE BOr(View const&) ->BOr::memory_space>; template @@ -458,7 +458,8 @@ struct MinLoc { }; template -MinLoc(View, Properties...> const&) +KOKKOS_DEDUCTION_GUIDE MinLoc( + View, Properties...> const&) ->MinLoc, Properties...>::memory_space>; @@ -513,7 +514,8 @@ struct MaxLoc { }; template -MaxLoc(View, Properties...> const&) +KOKKOS_DEDUCTION_GUIDE MaxLoc( + View, Properties...> const&) ->MaxLoc, Properties...>::memory_space>; @@ -577,7 +579,7 @@ struct MinMax { }; template -MinMax(View, Properties...> const&) +KOKKOS_DEDUCTION_GUIDE MinMax(View, Properties...> const&) ->MinMax, Properties...>::memory_space>; @@ -646,7 +648,8 @@ struct MinMaxLoc { }; template -MinMaxLoc(View, Properties...> const&) +KOKKOS_DEDUCTION_GUIDE MinMaxLoc( + View, Properties...> const&) ->MinMaxLoc, Properties...>::memory_space>; @@ -713,7 +716,8 @@ struct MaxFirstLoc { }; template -MaxFirstLoc(View, Properties...> const&) +KOKKOS_DEDUCTION_GUIDE MaxFirstLoc( + View, Properties...> const&) ->MaxFirstLoc, Properties...>::memory_space>; @@ -782,7 +786,7 @@ struct MaxFirstLocCustomComparator { template -MaxFirstLocCustomComparator( +KOKKOS_DEDUCTION_GUIDE MaxFirstLocCustomComparator( View, Properties...> const&, ComparatorType) ->MaxFirstLocCustomComparator, @@ -846,7 +850,8 @@ struct MinFirstLoc { }; template -MinFirstLoc(View, Properties...> const&) +KOKKOS_DEDUCTION_GUIDE MinFirstLoc( + View, Properties...> const&) ->MinFirstLoc, Properties...>::memory_space>; @@ -915,7 +920,7 @@ struct MinFirstLocCustomComparator { template -MinFirstLocCustomComparator( +KOKKOS_DEDUCTION_GUIDE MinFirstLocCustomComparator( View, Properties...> const&, ComparatorType) ->MinFirstLocCustomComparator, @@ -990,7 +995,8 @@ struct MinMaxFirstLastLoc { }; template -MinMaxFirstLastLoc(View, Properties...> const&) +KOKKOS_DEDUCTION_GUIDE MinMaxFirstLastLoc( + View, Properties...> const&) ->MinMaxFirstLastLoc, Properties...>::memory_space>; @@ -1069,7 +1075,7 @@ struct MinMaxFirstLastLocCustomComparator { template -MinMaxFirstLastLocCustomComparator( +KOKKOS_DEDUCTION_GUIDE MinMaxFirstLastLocCustomComparator( View, Properties...> const&, ComparatorType) ->MinMaxFirstLastLocCustomComparator< Scalar, Index, ComparatorType, @@ -1133,7 +1139,8 @@ struct FirstLoc { }; template -FirstLoc(View, Properties...> const&) +KOKKOS_DEDUCTION_GUIDE FirstLoc( + View, Properties...> const&) ->FirstLoc, Properties...>::memory_space>; @@ -1194,7 +1201,7 @@ struct LastLoc { }; template -LastLoc(View, Properties...> const&) +KOKKOS_DEDUCTION_GUIDE LastLoc(View, Properties...> const&) ->LastLoc, Properties...>::memory_space>; @@ -1261,7 +1268,8 @@ struct StdIsPartitioned { }; template -StdIsPartitioned(View, Properties...> const&) +KOKKOS_DEDUCTION_GUIDE StdIsPartitioned( + View, Properties...> const&) ->StdIsPartitioned, Properties...>::memory_space>; @@ -1323,7 +1331,8 @@ struct StdPartitionPoint { }; template -StdPartitionPoint(View, Properties...> const&) +KOKKOS_DEDUCTION_GUIDE StdPartitionPoint( + View, Properties...> const&) ->StdPartitionPoint, Properties...>::memory_space>; @@ -1502,18 +1511,18 @@ struct ParallelReduceAdaptor { using Analysis = FunctorAnalysis; - Kokkos::Impl::shared_allocation_tracking_disable(); - CombinedFunctorReducer functor_reducer( - functor, typename Analysis::Reducer( - ReducerSelector::select(functor, return_value))); - // FIXME Remove "Wrapper" once all backends implement the new interface - Impl::ParallelReduce::execution_space> - closure(functor_reducer, inner_policy, - return_value_adapter::return_value(return_value, functor)); - Kokkos::Impl::shared_allocation_tracking_enable(); + using CombinedFunctorReducerType = + CombinedFunctorReducer; + auto closure = construct_with_shared_allocation_tracking_disabled< + Impl::ParallelReduce::execution_space>>( + CombinedFunctorReducerType( + functor, typename Analysis::Reducer( + ReducerSelector::select(functor, return_value))), + inner_policy, + return_value_adapter::return_value(return_value, functor)); closure.execute(); Kokkos::Tools::Impl::end_parallel_reduce( diff --git a/lib/kokkos/core/src/Kokkos_View.hpp b/lib/kokkos/core/src/Kokkos_View.hpp index 484a0e6f62..820a40a5f5 100644 --- a/lib/kokkos/core/src/Kokkos_View.hpp +++ b/lib/kokkos/core/src/Kokkos_View.hpp @@ -38,6 +38,8 @@ static_assert(false, #ifdef KOKKOS_ENABLE_IMPL_MDSPAN #include +#include +#include #endif #include @@ -372,6 +374,35 @@ struct ViewTraits { //------------------------------------ }; +#ifdef KOKKOS_ENABLE_IMPL_MDSPAN +namespace Impl { +struct UnsupportedKokkosArrayLayout; + +template +struct MDSpanViewTraits { + using mdspan_type = UnsupportedKokkosArrayLayout; +}; + +// "Natural" mdspan for a view if the View's ArrayLayout is supported. +template +struct MDSpanViewTraits::type>> { + using index_type = std::size_t; + using extents_type = + typename Impl::ExtentsFromDataType::type; + using mdspan_layout_type = + typename Impl::LayoutFromArrayLayout::type; + using accessor_type = Impl::SpaceAwareAccessor< + typename Traits::memory_space, + Kokkos::default_accessor>; + using mdspan_type = mdspan; +}; +} // namespace Impl +#endif // KOKKOS_ENABLE_IMPL_MDSPAN + /** \class View * \brief View to an array of data. * @@ -522,7 +553,6 @@ constexpr bool is_assignable(const Kokkos::View& dst, //---------------------------------------------------------------------------- #include -#include //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- @@ -923,57 +953,30 @@ class View : public ViewTraits { template KOKKOS_FORCEINLINE_FUNCTION std::enable_if_t< (Kokkos::Impl::always_true::value && // - (2 == rank) && is_default_map && is_layout_left && (rank_dynamic == 0)), + (2 == rank) && is_default_map && + (is_layout_left || is_layout_right || is_layout_stride)), reference_type> operator()(I0 i0, I1 i1) const { check_operator_parens_valid_args(i0, i1); KOKKOS_IMPL_VIEW_OPERATOR_VERIFY(m_track, m_map, i0, i1) - return m_map.m_impl_handle[i0 + m_map.m_impl_offset.m_dim.N0 * i1]; - } - - template - KOKKOS_FORCEINLINE_FUNCTION std::enable_if_t< - (Kokkos::Impl::always_true::value && // - (2 == rank) && is_default_map && is_layout_left && (rank_dynamic != 0)), - reference_type> - operator()(I0 i0, I1 i1) const { - check_operator_parens_valid_args(i0, i1); - KOKKOS_IMPL_VIEW_OPERATOR_VERIFY(m_track, m_map, i0, i1) - return m_map.m_impl_handle[i0 + m_map.m_impl_offset.m_stride * i1]; - } - - template - KOKKOS_FORCEINLINE_FUNCTION std::enable_if_t< - (Kokkos::Impl::always_true::value && // - (2 == rank) && is_default_map && is_layout_right && (rank_dynamic == 0)), - reference_type> - operator()(I0 i0, I1 i1) const { - check_operator_parens_valid_args(i0, i1); - KOKKOS_IMPL_VIEW_OPERATOR_VERIFY(m_track, m_map, i0, i1) - return m_map.m_impl_handle[i1 + m_map.m_impl_offset.m_dim.N1 * i0]; - } - - template - KOKKOS_FORCEINLINE_FUNCTION std::enable_if_t< - (Kokkos::Impl::always_true::value && // - (2 == rank) && is_default_map && is_layout_right && (rank_dynamic != 0)), - reference_type> - operator()(I0 i0, I1 i1) const { - check_operator_parens_valid_args(i0, i1); - KOKKOS_IMPL_VIEW_OPERATOR_VERIFY(m_track, m_map, i0, i1) - return m_map.m_impl_handle[i1 + m_map.m_impl_offset.m_stride * i0]; - } - - template - KOKKOS_FORCEINLINE_FUNCTION - std::enable_if_t<(Kokkos::Impl::always_true::value && // - (2 == rank) && is_default_map && is_layout_stride), - reference_type> - operator()(I0 i0, I1 i1) const { - check_operator_parens_valid_args(i0, i1); - KOKKOS_IMPL_VIEW_OPERATOR_VERIFY(m_track, m_map, i0, i1) - return m_map.m_impl_handle[i0 * m_map.m_impl_offset.m_stride.S0 + - i1 * m_map.m_impl_offset.m_stride.S1]; + if constexpr (is_layout_left) { + if constexpr (rank_dynamic == 0) + return m_map.m_impl_handle[i0 + m_map.m_impl_offset.m_dim.N0 * i1]; + else + return m_map.m_impl_handle[i0 + m_map.m_impl_offset.m_stride * i1]; + } else if constexpr (is_layout_right) { + if constexpr (rank_dynamic == 0) + return m_map.m_impl_handle[i1 + m_map.m_impl_offset.m_dim.N1 * i0]; + else + return m_map.m_impl_handle[i1 + m_map.m_impl_offset.m_stride * i0]; + } else { + static_assert(is_layout_stride); + return m_map.m_impl_handle[i0 * m_map.m_impl_offset.m_stride.S0 + + i1 * m_map.m_impl_offset.m_stride.S1]; + } +#if defined KOKKOS_COMPILER_INTEL + __builtin_unreachable(); +#endif } // Rank 0 -> 8 operator() except for rank-1 and rank-2 with default map which @@ -1066,57 +1069,30 @@ class View : public ViewTraits { template KOKKOS_FORCEINLINE_FUNCTION std::enable_if_t< (Kokkos::Impl::always_true::value && (2 == rank) && - is_default_map && is_layout_left && (rank_dynamic == 0)), + is_default_map && + (is_layout_left || is_layout_right || is_layout_stride)), reference_type> access(I0 i0, I1 i1, Is... extra) const { check_access_member_function_valid_args(i0, i1, extra...); KOKKOS_IMPL_VIEW_OPERATOR_VERIFY(m_track, m_map, i0, i1, extra...) - return m_map.m_impl_handle[i0 + m_map.m_impl_offset.m_dim.N0 * i1]; - } - - template - KOKKOS_FORCEINLINE_FUNCTION std::enable_if_t< - (Kokkos::Impl::always_true::value && (2 == rank) && - is_default_map && is_layout_left && (rank_dynamic != 0)), - reference_type> - access(I0 i0, I1 i1, Is... extra) const { - check_access_member_function_valid_args(i0, i1, extra...); - KOKKOS_IMPL_VIEW_OPERATOR_VERIFY(m_track, m_map, i0, i1, extra...) - return m_map.m_impl_handle[i0 + m_map.m_impl_offset.m_stride * i1]; - } - - template - KOKKOS_FORCEINLINE_FUNCTION std::enable_if_t< - (Kokkos::Impl::always_true::value && (2 == rank) && - is_default_map && is_layout_right && (rank_dynamic == 0)), - reference_type> - access(I0 i0, I1 i1, Is... extra) const { - check_access_member_function_valid_args(i0, i1, extra...); - KOKKOS_IMPL_VIEW_OPERATOR_VERIFY(m_track, m_map, i0, i1, extra...) - return m_map.m_impl_handle[i1 + m_map.m_impl_offset.m_dim.N1 * i0]; - } - - template - KOKKOS_FORCEINLINE_FUNCTION std::enable_if_t< - (Kokkos::Impl::always_true::value && (2 == rank) && - is_default_map && is_layout_right && (rank_dynamic != 0)), - reference_type> - access(I0 i0, I1 i1, Is... extra) const { - check_access_member_function_valid_args(i0, i1, extra...); - KOKKOS_IMPL_VIEW_OPERATOR_VERIFY(m_track, m_map, i0, i1, extra...) - return m_map.m_impl_handle[i1 + m_map.m_impl_offset.m_stride * i0]; - } - - template - KOKKOS_FORCEINLINE_FUNCTION - std::enable_if_t<(Kokkos::Impl::always_true::value && - (2 == rank) && is_default_map && is_layout_stride), - reference_type> - access(I0 i0, I1 i1, Is... extra) const { - check_access_member_function_valid_args(i0, i1, extra...); - KOKKOS_IMPL_VIEW_OPERATOR_VERIFY(m_track, m_map, i0, i1, extra...) - return m_map.m_impl_handle[i0 * m_map.m_impl_offset.m_stride.S0 + - i1 * m_map.m_impl_offset.m_stride.S1]; + if constexpr (is_layout_left) { + if constexpr (rank_dynamic == 0) + return m_map.m_impl_handle[i0 + m_map.m_impl_offset.m_dim.N0 * i1]; + else + return m_map.m_impl_handle[i0 + m_map.m_impl_offset.m_stride * i1]; + } else if constexpr (is_layout_right) { + if constexpr (rank_dynamic == 0) + return m_map.m_impl_handle[i1 + m_map.m_impl_offset.m_dim.N1 * i0]; + else + return m_map.m_impl_handle[i1 + m_map.m_impl_offset.m_stride * i0]; + } else { + static_assert(is_layout_stride); + return m_map.m_impl_handle[i0 * m_map.m_impl_offset.m_stride.S0 + + i1 * m_map.m_impl_offset.m_stride.S1]; + } +#if defined KOKKOS_COMPILER_INTEL + __builtin_unreachable(); +#endif } //------------------------------ @@ -1442,8 +1418,7 @@ class View : public ViewTraits { std::is_same_v || std::is_same_v || - is_layouttiled::value) { + Kokkos::LayoutStride>) { size_t i0 = arg_layout.dimension[0]; size_t i1 = arg_layout.dimension[1]; size_t i2 = arg_layout.dimension[2]; @@ -1495,8 +1470,7 @@ class View : public ViewTraits { std::is_same_v || std::is_same_v || - is_layouttiled::value) { + Kokkos::LayoutStride>) { size_t i0 = arg_layout.dimension[0]; size_t i1 = arg_layout.dimension[1]; size_t i2 = arg_layout.dimension[2]; @@ -1725,6 +1699,79 @@ class View : public ViewTraits { "Layout is not constructible from extent arguments. Use " "overload taking a layout object instead."); } + + //---------------------------------------- + // MDSpan converting constructors +#ifdef KOKKOS_ENABLE_IMPL_MDSPAN + template ::mdspan_type> + KOKKOS_INLINE_FUNCTION +#ifndef KOKKOS_ENABLE_CXX17 + explicit(traits::is_managed) +#endif + View(const typename Impl::MDSpanViewTraits::mdspan_type& mds, + std::enable_if_t< + !std::is_same_v>* = + nullptr) + : View(mds.data_handle(), + Impl::array_layout_from_mapping< + typename traits::array_layout, + typename Impl::MDSpanViewTraits::mdspan_type>( + mds.mapping())) { + } + + template + KOKKOS_INLINE_FUNCTION +#ifndef KOKKOS_ENABLE_CXX17 + explicit(!std::is_convertible_v< + Kokkos::mdspan, + typename Impl::MDSpanViewTraits::mdspan_type>) +#endif + View(const Kokkos::mdspan& mds) + : View(typename Impl::MDSpanViewTraits::mdspan_type(mds)) { + } + + //---------------------------------------- + // Conversion to MDSpan + template ::mdspan_type, + typename = std::enable_if_t, + std::false_type, + std::is_assignable, + ImplNaturalMDSpanType>>::value>> + KOKKOS_INLINE_FUNCTION constexpr operator mdspan< + OtherElementType, OtherExtents, OtherLayoutPolicy, OtherAccessor>() { + using mdspan_type = typename Impl::MDSpanViewTraits::mdspan_type; + return mdspan_type{data(), + Impl::mapping_from_view_mapping(m_map)}; + } + + template >, + typename = std::enable_if_t>> + KOKKOS_INLINE_FUNCTION constexpr auto to_mdspan( + const OtherAccessorType& other_accessor = + typename Impl::MDSpanViewTraits::accessor_type()) { + using mdspan_type = typename Impl::MDSpanViewTraits::mdspan_type; + using ret_mdspan_type = + mdspan; + return ret_mdspan_type{data(), + Impl::mapping_from_view_mapping(m_map), + other_accessor}; + } +#endif // KOKKOS_ENABLE_IMPL_MDSPAN }; template @@ -1878,23 +1925,6 @@ KOKKOS_INLINE_FUNCTION bool operator!=(const View& lhs, namespace Kokkos { namespace Impl { -inline void shared_allocation_tracking_disable() { - Kokkos::Impl::SharedAllocationRecord::tracking_disable(); -} - -inline void shared_allocation_tracking_enable() { - Kokkos::Impl::SharedAllocationRecord::tracking_enable(); -} - -} /* namespace Impl */ -} /* namespace Kokkos */ - -//---------------------------------------------------------------------------- -//---------------------------------------------------------------------------- - -namespace Kokkos { -namespace Impl { - template struct CommonViewValueType; diff --git a/lib/kokkos/core/src/OpenACC/Kokkos_OpenACCSpace.cpp b/lib/kokkos/core/src/OpenACC/Kokkos_OpenACCSpace.cpp index acc0dcd3c6..c8a5d28ba8 100644 --- a/lib/kokkos/core/src/OpenACC/Kokkos_OpenACCSpace.cpp +++ b/lib/kokkos/core/src/OpenACC/Kokkos_OpenACCSpace.cpp @@ -67,16 +67,7 @@ void *Kokkos::Experimental::OpenACCSpace::impl_allocate( ptr = acc_malloc(arg_alloc_size); if (!ptr) { - size_t alignment = 1; // OpenACC does not handle alignment - using Kokkos::Experimental::RawMemoryAllocationFailure; - auto failure_mode = - arg_alloc_size > 0 - ? RawMemoryAllocationFailure::FailureMode::OutOfMemoryError - : RawMemoryAllocationFailure::FailureMode::InvalidAllocationSize; - auto alloc_mechanism = - RawMemoryAllocationFailure::AllocationMechanism::OpenACCMalloc; - throw RawMemoryAllocationFailure(arg_alloc_size, alignment, failure_mode, - alloc_mechanism); + Kokkos::Impl::throw_bad_alloc(name(), arg_alloc_size, arg_label); } if (Kokkos::Profiling::profileLibraryLoaded()) { diff --git a/lib/kokkos/core/src/OpenACC/Kokkos_OpenACC_ParallelFor_Team.hpp b/lib/kokkos/core/src/OpenACC/Kokkos_OpenACC_ParallelFor_Team.hpp index 4fce680aef..2b98018e3b 100644 --- a/lib/kokkos/core/src/OpenACC/Kokkos_OpenACC_ParallelFor_Team.hpp +++ b/lib/kokkos/core/src/OpenACC/Kokkos_OpenACC_ParallelFor_Team.hpp @@ -44,10 +44,12 @@ class Kokkos::Impl::ParallelFor, auto team_size = m_policy.team_size(); auto vector_length = m_policy.impl_vector_length(); + int const async_arg = m_policy.space().acc_async_queue(); + auto const a_functor(m_functor); #pragma acc parallel loop gang vector num_gangs(league_size) \ - vector_length(team_size* vector_length) copyin(a_functor) + vector_length(team_size* vector_length) copyin(a_functor) async(async_arg) for (int i = 0; i < league_size * team_size * vector_length; i++) { int league_id = i / (team_size * vector_length); typename Policy::member_type team(league_id, league_size, team_size, @@ -145,10 +147,12 @@ class Kokkos::Impl::ParallelFor, auto team_size = m_policy.team_size(); auto vector_length = m_policy.impl_vector_length(); + int const async_arg = m_policy.space().acc_async_queue(); + auto const a_functor(m_functor); #pragma acc parallel loop gang num_gangs(league_size) num_workers(team_size) \ - vector_length(vector_length) copyin(a_functor) + vector_length(vector_length) copyin(a_functor) async(async_arg) for (int i = 0; i < league_size; i++) { int league_id = i; typename Policy::member_type team(league_id, league_size, team_size, diff --git a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP.cpp b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP.cpp index 81f2c5c305..82199d0d72 100644 --- a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP.cpp +++ b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP.cpp @@ -72,9 +72,28 @@ int OpenMP::concurrency(OpenMP const &instance) { int OpenMP::concurrency() const { return impl_thread_pool_size(); } #endif +void OpenMP::impl_static_fence(std::string const &name) { + Kokkos::Tools::Experimental::Impl::profile_fence_event( + name, + Kokkos::Tools::Experimental::SpecialSynchronizationCases:: + GlobalDeviceSynchronization, + []() { + std::lock_guard lock_all_instances( + Impl::OpenMPInternal::all_instances_mutex); + for (auto *instance_ptr : Impl::OpenMPInternal::all_instances) { + std::lock_guard lock_instance( + instance_ptr->m_instance_mutex); + } + }); +} + void OpenMP::fence(const std::string &name) const { Kokkos::Tools::Experimental::Impl::profile_fence_event( - name, Kokkos::Tools::Experimental::Impl::DirectFenceIDHandle{1}, []() {}); + name, Kokkos::Tools::Experimental::Impl::DirectFenceIDHandle{1}, + [this]() { + auto *internal_instance = this->impl_internal_space_instance(); + std::lock_guard lock(internal_instance->m_instance_mutex); + }); } bool OpenMP::impl_is_initialized() noexcept { diff --git a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP.hpp b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP.hpp index 11292af84a..a403909f67 100644 --- a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP.hpp +++ b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP.hpp @@ -67,7 +67,15 @@ class OpenMP { OpenMP(); - OpenMP(int pool_size); + explicit OpenMP(int pool_size); + +#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4 + template + KOKKOS_DEPRECATED_WITH_COMMENT( + "OpenMP execution space should be constructed explicitly.") + OpenMP(int pool_size) + : OpenMP(pool_size) {} +#endif /// \brief Print configuration information to the given output stream. void print_configuration(std::ostream& os, bool verbose = false) const; @@ -146,14 +154,6 @@ inline int OpenMP::impl_thread_pool_rank() noexcept { KOKKOS_IF_ON_DEVICE((return -1;)) } -inline void OpenMP::impl_static_fence(std::string const& name) { - Kokkos::Tools::Experimental::Impl::profile_fence_event( - name, - Kokkos::Tools::Experimental::SpecialSynchronizationCases:: - GlobalDeviceSynchronization, - []() {}); -} - inline bool OpenMP::is_asynchronous(OpenMP const& /*instance*/) noexcept { return false; } diff --git a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Instance.cpp b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Instance.cpp index 32172fbc6c..0f4c7d6052 100644 --- a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Instance.cpp +++ b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Instance.cpp @@ -34,18 +34,8 @@ namespace Kokkos { namespace Impl { -void OpenMPInternal::acquire_lock() { - while (1 == desul::atomic_compare_exchange(&m_pool_mutex, 0, 1, - desul::MemoryOrderAcquire(), - desul::MemoryScopeDevice())) { - // do nothing - } -} - -void OpenMPInternal::release_lock() { - desul::atomic_store(&m_pool_mutex, 0, desul::MemoryOrderRelease(), - desul::MemoryScopeDevice()); -} +std::vector OpenMPInternal::all_instances; +std::mutex OpenMPInternal::all_instances_mutex; void OpenMPInternal::clear_thread_data() { const size_t member_bytes = @@ -123,17 +113,11 @@ void OpenMPInternal::resize_thread_data(size_t pool_reduce_bytes, if (nullptr != m_pool[rank]) { m_pool[rank]->disband_pool(); - space.deallocate(m_pool[rank], old_alloc_bytes); + // impl_deallocate to not fence here + space.impl_deallocate("[unlabeled]", m_pool[rank], old_alloc_bytes); } - void *ptr = nullptr; - try { - ptr = space.allocate(alloc_bytes); - } catch ( - Kokkos::Experimental::RawMemoryAllocationFailure const &failure) { - // For now, just rethrow the error message the existing way - Kokkos::Impl::throw_runtime_exception(failure.get_error_message()); - } + void *ptr = space.allocate("Kokkos::OpenMP::scratch_mem", alloc_bytes); m_pool[rank] = new (ptr) HostThreadTeamData(); @@ -304,6 +288,18 @@ void OpenMPInternal::finalize() { } m_initialized = false; + + // guard erasing from all_instances + { + std::scoped_lock lock(all_instances_mutex); + + auto it = std::find(all_instances.begin(), all_instances.end(), this); + if (it == all_instances.end()) + Kokkos::abort( + "Execution space instance to be removed couldn't be found!"); + *it = all_instances.back(); + all_instances.pop_back(); + } } void OpenMPInternal::print_configuration(std::ostream &s) const { diff --git a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Instance.hpp b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Instance.hpp index 35b9aa93ba..f4a0d3e201 100644 --- a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Instance.hpp +++ b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Instance.hpp @@ -56,7 +56,13 @@ struct OpenMPTraits { class OpenMPInternal { private: OpenMPInternal(int arg_pool_size) - : m_pool_size{arg_pool_size}, m_level{omp_get_level()}, m_pool() {} + : m_pool_size{arg_pool_size}, m_level{omp_get_level()}, m_pool() { + // guard pushing to all_instances + { + std::scoped_lock lock(all_instances_mutex); + all_instances.push_back(this); + } + } ~OpenMPInternal() { clear_thread_data(); } @@ -66,7 +72,6 @@ class OpenMPInternal { int m_pool_size; int m_level; - int m_pool_mutex = 0; HostThreadTeamData* m_pool[OpenMPTraits::MAX_THREAD_COUNT]; @@ -83,12 +88,6 @@ class OpenMPInternal { int thread_pool_size() const { return m_pool_size; } - // Acquire lock used to protect access to m_pool - void acquire_lock(); - - // Release lock used to protect access to m_pool - void release_lock(); - void resize_thread_data(size_t pool_reduce_bytes, size_t team_reduce_bytes, size_t team_shared_bytes, size_t thread_local_bytes); @@ -107,6 +106,11 @@ class OpenMPInternal { bool verify_is_initialized(const char* const label) const; void print_configuration(std::ostream& s) const; + + std::mutex m_instance_mutex; + + static std::vector all_instances; + static std::mutex all_instances_mutex; }; inline bool execute_in_serial(OpenMP const& space = OpenMP()) { @@ -157,7 +161,7 @@ inline std::vector create_OpenMP_instances( "Kokkos::abort: Partition not enough resources left to create the last " "instance."); } - instances[weights.size() - 1] = resources_left; + instances[weights.size() - 1] = OpenMP(resources_left); return instances; } diff --git a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Parallel_For.hpp b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Parallel_For.hpp index 823a7e668e..79d7d295c0 100644 --- a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Parallel_For.hpp +++ b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Parallel_For.hpp @@ -108,6 +108,8 @@ class ParallelFor, Kokkos::OpenMP> { public: inline void execute() const { + // Serialize kernels on the same execution space instance + std::lock_guard lock(m_instance->m_instance_mutex); if (execute_in_serial(m_policy.space())) { exec_range(m_functor, m_policy.begin(), m_policy.end()); return; @@ -202,6 +204,9 @@ class ParallelFor, public: inline void execute() const { + // Serialize kernels on the same execution space instance + std::lock_guard lock(m_instance->m_instance_mutex); + #ifndef KOKKOS_COMPILER_INTEL if (execute_in_serial(m_iter.m_rp.space())) { exec_range(0, m_iter.m_rp.m_num_tiles); @@ -333,7 +338,8 @@ class ParallelFor, const size_t team_shared_size = m_shmem_size; const size_t thread_local_size = 0; // Never shrinks - m_instance->acquire_lock(); + // Serialize kernels on the same execution space instance + std::lock_guard lock(m_instance->m_instance_mutex); m_instance->resize_thread_data(pool_reduce_size, team_reduce_size, team_shared_size, thread_local_size); @@ -343,8 +349,6 @@ class ParallelFor, m_functor, *(m_instance->get_thread_data()), 0, m_policy.league_size(), m_policy.league_size()); - m_instance->release_lock(); - return; } @@ -383,8 +387,6 @@ class ParallelFor, data.disband_team(); } - - m_instance->release_lock(); } inline ParallelFor(const FunctorType& arg_functor, const Policy& arg_policy) diff --git a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Parallel_Reduce.hpp b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Parallel_Reduce.hpp index 05fd1c9dce..d22e1e7eda 100644 --- a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Parallel_Reduce.hpp +++ b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Parallel_Reduce.hpp @@ -83,7 +83,8 @@ class ParallelReduce, const size_t pool_reduce_bytes = reducer.value_size(); - m_instance->acquire_lock(); + // Serialize kernels on the same execution space instance + std::lock_guard lock(m_instance->m_instance_mutex); m_instance->resize_thread_data(pool_reduce_bytes, 0 // team_reduce_bytes , @@ -106,6 +107,7 @@ class ParallelReduce, update); reducer.final(ptr); + return; } const int pool_size = m_instance->thread_pool_size(); @@ -157,8 +159,6 @@ class ParallelReduce, m_result_ptr[j] = ptr[j]; } } - - m_instance->release_lock(); } //---------------------------------------- @@ -218,7 +218,8 @@ class ParallelReduceacquire_lock(); + // Serialize kernels on the same execution space instance + std::lock_guard lock(m_instance->m_instance_mutex); m_instance->resize_thread_data(pool_reduce_bytes, 0 // team_reduce_bytes , @@ -241,8 +242,6 @@ class ParallelReducerelease_lock(); - return; } #endif @@ -299,8 +298,6 @@ class ParallelReducerelease_lock(); } //---------------------------------------- @@ -415,7 +412,8 @@ class ParallelReduceacquire_lock(); + // Serialize kernels on the same execution space instance + std::lock_guard lock(m_instance->m_instance_mutex); m_instance->resize_thread_data(pool_reduce_size, team_reduce_size, team_shared_size, thread_local_size); @@ -433,8 +431,6 @@ class ParallelReducerelease_lock(); - return; } @@ -510,8 +506,6 @@ class ParallelReducerelease_lock(); } //---------------------------------------- diff --git a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Parallel_Scan.hpp b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Parallel_Scan.hpp index f843aef3a8..b9ce25d3ee 100644 --- a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Parallel_Scan.hpp +++ b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Parallel_Scan.hpp @@ -70,6 +70,9 @@ class ParallelScan, const int value_count = Analysis::value_count(m_functor); const size_t pool_reduce_bytes = 2 * Analysis::value_size(m_functor); + // Serialize kernels on the same execution space instance + std::lock_guard lock(m_instance->m_instance_mutex); + m_instance->resize_thread_data(pool_reduce_bytes, 0 // team_reduce_bytes , 0 // team_shared_bytes @@ -193,7 +196,8 @@ class ParallelScanWithTotal, const int value_count = Analysis::value_count(m_functor); const size_t pool_reduce_bytes = 2 * Analysis::value_size(m_functor); - m_instance->acquire_lock(); + // Serialize kernels on the same execution space instance + std::lock_guard lock(m_instance->m_instance_mutex); m_instance->resize_thread_data(pool_reduce_bytes, 0 // team_reduce_bytes , @@ -213,8 +217,6 @@ class ParallelScanWithTotal, *m_result_ptr = update; - m_instance->release_lock(); - return; } @@ -266,8 +268,6 @@ class ParallelScanWithTotal, *m_result_ptr = update_base; } } - - m_instance->release_lock(); } //---------------------------------------- diff --git a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Task.cpp b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Task.cpp index 3e67d8d625..54c1574d71 100644 --- a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Task.cpp +++ b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Task.cpp @@ -52,18 +52,7 @@ HostThreadTeamDataSingleton::HostThreadTeamDataSingleton() num_pool_reduce_bytes, num_team_reduce_bytes, num_team_shared_bytes, num_thread_local_bytes); - void* ptr = nullptr; - try { - ptr = space.allocate(alloc_bytes); - } catch (Kokkos::Experimental::RawMemoryAllocationFailure const& f) { - // For now, just rethrow the error message with a note - // Note that this could, in turn, trigger an out of memory exception, - // but it's pretty unlikely, so we won't worry about it for now. - // TODO reasonable error message when `std::string` causes OOM error - Kokkos::Impl::throw_runtime_exception( - std::string("Failure to allocate scratch memory: ") + - f.get_error_message()); - } + void* ptr = space.allocate("Kokkos::Impl::HostThreadTeamData", alloc_bytes); HostThreadTeamData::scratch_assign( ptr, alloc_bytes, num_pool_reduce_bytes, num_team_reduce_bytes, diff --git a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Task.hpp b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Task.hpp index 01b6694865..2877d940fa 100644 --- a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Task.hpp +++ b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Task.hpp @@ -73,7 +73,8 @@ class TaskQueueSpecialization> { execution_space().impl_internal_space_instance(); const int pool_size = get_max_team_count(scheduler.get_execution_space()); - instance->acquire_lock(); + // Serialize kernels on the same execution space instance + std::lock_guard lock(instance->m_instance_mutex); // TODO @tasking @new_feature DSH allow team sizes other than 1 const int team_size = 1; // Threads per core @@ -152,8 +153,6 @@ class TaskQueueSpecialization> { } self.disband_team(); } // end pragma omp parallel - - instance->release_lock(); } static uint32_t get_max_team_count(execution_space const& espace) { @@ -238,7 +237,8 @@ class TaskQueueSpecializationConstrained< execution_space().impl_internal_space_instance(); const int pool_size = instance->thread_pool_size(); - instance->acquire_lock(); + // Serialize kernels on the same execution space instance + std::lock_guard lock(instance->m_instance_mutex); const int team_size = 1; // Threads per core instance->resize_thread_data(0 /* global reduce buffer */ @@ -250,6 +250,7 @@ class TaskQueueSpecializationConstrained< 0 /* thread local buffer */ ); assert(pool_size % team_size == 0); + auto& queue = scheduler.queue(); queue.initialize_team_queues(pool_size / team_size); @@ -343,8 +344,6 @@ class TaskQueueSpecializationConstrained< } self.disband_team(); } // end pragma omp parallel - - instance->release_lock(); } template diff --git a/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget.hpp b/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget.hpp index ea4e7f6bab..84c7b85f11 100644 --- a/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget.hpp +++ b/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget.hpp @@ -146,7 +146,8 @@ struct DeviceTypeTraits<::Kokkos::Experimental::OpenMPTarget> { /*--------------------------------------------------------------------------*/ #include -#include +#include +#include #include /*--------------------------------------------------------------------------*/ diff --git a/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTargetSpace.cpp b/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTargetSpace.cpp index a414b34d7c..635b0e0504 100644 --- a/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTargetSpace.cpp +++ b/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTargetSpace.cpp @@ -54,9 +54,11 @@ void* OpenMPTargetSpace::impl_allocate( static_assert(sizeof(void*) == sizeof(uintptr_t), "Error sizeof(void*) != sizeof(uintptr_t)"); - void* ptr; + void* ptr = omp_target_alloc(arg_alloc_size, omp_get_default_device()); - ptr = omp_target_alloc(arg_alloc_size, omp_get_default_device()); + if (!ptr) { + Kokkos::Impl::throw_bad_alloc(name(), arg_alloc_size, arg_label); + } if (Kokkos::Profiling::profileLibraryLoaded()) { const size_t reported_size = diff --git a/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Exec.cpp b/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Exec.cpp index b39f5aca35..6c5eb048e3 100644 --- a/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Exec.cpp +++ b/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Exec.cpp @@ -71,8 +71,6 @@ void OpenMPTargetExec::verify_initialized(const char* const label) { void* OpenMPTargetExec::m_scratch_ptr = nullptr; int64_t OpenMPTargetExec::m_scratch_size = 0; -int* OpenMPTargetExec::m_lock_array = nullptr; -uint64_t OpenMPTargetExec::m_lock_size = 0; uint32_t* OpenMPTargetExec::m_uniquetoken_ptr = nullptr; int OpenMPTargetExec::MAX_ACTIVE_THREADS = 0; std::mutex OpenMPTargetExec::m_mutex_scratch_ptr; @@ -84,15 +82,6 @@ void OpenMPTargetExec::clear_scratch() { m_scratch_size = 0; } -void OpenMPTargetExec::clear_lock_array() { - if (m_lock_array != nullptr) { - Kokkos::Experimental::OpenMPTargetSpace space; - space.deallocate(m_lock_array, m_lock_size); - m_lock_array = nullptr; - m_lock_size = 0; - } -} - void* OpenMPTargetExec::get_scratch_ptr() { return m_scratch_ptr; } void OpenMPTargetExec::resize_scratch(int64_t team_size, int64_t shmem_size_L0, @@ -135,35 +124,6 @@ void OpenMPTargetExec::resize_scratch(int64_t team_size, int64_t shmem_size_L0, } } -int* OpenMPTargetExec::get_lock_array(int num_teams) { - Kokkos::Experimental::OpenMPTargetSpace space; - int max_active_league_size = MAX_ACTIVE_THREADS / 32; - int lock_array_elem = - (num_teams > max_active_league_size) ? num_teams : max_active_league_size; - if (m_lock_size < (lock_array_elem * sizeof(int))) { - space.deallocate(m_lock_array, m_lock_size); - m_lock_size = lock_array_elem * sizeof(int); - m_lock_array = static_cast(space.allocate(m_lock_size)); - - // FIXME_OPENMPTARGET - Creating a target region here to initialize the - // lock_array with 0's fails. Hence creating an equivalent host array to - // achieve the same. Value of host array are then copied to the lock_array. - int* h_lock_array = static_cast( - omp_target_alloc(m_lock_size, omp_get_initial_device())); - - for (int i = 0; i < lock_array_elem; ++i) h_lock_array[i] = 0; - - if (0 < m_lock_size) - KOKKOS_IMPL_OMPT_SAFE_CALL(omp_target_memcpy( - m_lock_array, h_lock_array, m_lock_size, 0, 0, - omp_get_default_device(), omp_get_initial_device())); - - omp_target_free(h_lock_array, omp_get_initial_device()); - } - - return m_lock_array; -} - } // namespace Impl } // namespace Kokkos diff --git a/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Instance.cpp b/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Instance.cpp index 3387108da3..44e9119ea8 100644 --- a/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Instance.cpp +++ b/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Instance.cpp @@ -106,7 +106,6 @@ void OpenMPTargetInternal::print_configuration(std::ostream& os, void OpenMPTargetInternal::impl_finalize() { m_is_initialized = false; Kokkos::Impl::OpenMPTargetExec space; - if (space.m_lock_array != nullptr) space.clear_lock_array(); if (space.m_uniquetoken_ptr != nullptr) Kokkos::kokkos_free( diff --git a/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_MDRangePolicy.hpp b/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_MDRangePolicy.hpp index d718f56d38..e353676b61 100644 --- a/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_MDRangePolicy.hpp +++ b/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_MDRangePolicy.hpp @@ -22,6 +22,10 @@ namespace Kokkos { namespace Impl { +using OpenMPTargetIterateLeft = std::integral_constant; +using OpenMPTargetIterateRight = + std::integral_constant; + template struct ThreadAndVectorNestLevel +#include +#include "Kokkos_OpenMPTarget_MDRangePolicy.hpp" + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +namespace Kokkos { +namespace Impl { + +template +class ParallelFor, + Kokkos::Experimental::OpenMPTarget> { + private: + using Policy = Kokkos::MDRangePolicy; + using WorkTag = typename Policy::work_tag; + using Member = typename Policy::member_type; + using Index = typename Policy::index_type; + + const FunctorType m_functor; + const Policy m_policy; + + public: + inline void execute() const { + OpenMPTargetExec::verify_is_process( + "Kokkos::Experimental::OpenMPTarget parallel_for"); + OpenMPTargetExec::verify_initialized( + "Kokkos::Experimental::OpenMPTarget parallel_for"); + FunctorType functor(m_functor); + Policy policy = m_policy; + + typename Policy::point_type unused; + static_assert(1 < Policy::rank && Policy::rank < 7); + static_assert(Policy::inner_direction == Iterate::Left || + Policy::inner_direction == Iterate::Right); + + execute_tile( + unused, functor, policy, + std::integral_constant()); + } + + template + inline std::enable_if_t execute_tile( + typename Policy::point_type offset, const FunctorType& functor, + const Policy& policy, OpenMPTargetIterateRight) const { + (void)offset; + const Index begin_0 = policy.m_lower[0]; + const Index begin_1 = policy.m_lower[1]; + + const Index end_0 = policy.m_upper[0]; + const Index end_1 = policy.m_upper[1]; + +#pragma omp target teams distribute parallel for collapse(2) map(to : functor) + for (auto i0 = begin_0; i0 < end_0; ++i0) + for (auto i1 = begin_1; i1 < end_1; ++i1) { + if constexpr (std::is_void::value) + functor(i0, i1); + else + functor(typename Policy::work_tag(), i0, i1); + } + } + + template + inline std::enable_if_t execute_tile( + typename Policy::point_type offset, const FunctorType& functor, + const Policy& policy, OpenMPTargetIterateRight) const { + (void)offset; + const Index begin_0 = policy.m_lower[0]; + const Index begin_1 = policy.m_lower[1]; + const Index begin_2 = policy.m_lower[2]; + + const Index end_0 = policy.m_upper[0]; + const Index end_1 = policy.m_upper[1]; + const Index end_2 = policy.m_upper[2]; + +#pragma omp target teams distribute parallel for collapse(3) map(to : functor) + for (auto i0 = begin_0; i0 < end_0; ++i0) { + for (auto i1 = begin_1; i1 < end_1; ++i1) { + for (auto i2 = begin_2; i2 < end_2; ++i2) { + if constexpr (std::is_void::value) + functor(i0, i1, i2); + else + functor(typename Policy::work_tag(), i0, i1, i2); + } + } + } + } + + template + inline std::enable_if_t execute_tile( + typename Policy::point_type offset, const FunctorType& functor, + const Policy& policy, OpenMPTargetIterateRight) const { + (void)offset; + const Index begin_0 = policy.m_lower[0]; + const Index begin_1 = policy.m_lower[1]; + const Index begin_2 = policy.m_lower[2]; + const Index begin_3 = policy.m_lower[3]; + + const Index end_0 = policy.m_upper[0]; + const Index end_1 = policy.m_upper[1]; + const Index end_2 = policy.m_upper[2]; + const Index end_3 = policy.m_upper[3]; + +#pragma omp target teams distribute parallel for collapse(4) map(to : functor) + for (auto i0 = begin_0; i0 < end_0; ++i0) { + for (auto i1 = begin_1; i1 < end_1; ++i1) { + for (auto i2 = begin_2; i2 < end_2; ++i2) { + for (auto i3 = begin_3; i3 < end_3; ++i3) { + if constexpr (std::is_void::value) + functor(i0, i1, i2, i3); + else + functor(typename Policy::work_tag(), i0, i1, i2, i3); + } + } + } + } + } + + template + inline std::enable_if_t execute_tile( + typename Policy::point_type offset, const FunctorType& functor, + const Policy& policy, OpenMPTargetIterateRight) const { + (void)offset; + const Index begin_0 = policy.m_lower[0]; + const Index begin_1 = policy.m_lower[1]; + const Index begin_2 = policy.m_lower[2]; + const Index begin_3 = policy.m_lower[3]; + const Index begin_4 = policy.m_lower[4]; + + const Index end_0 = policy.m_upper[0]; + const Index end_1 = policy.m_upper[1]; + const Index end_2 = policy.m_upper[2]; + const Index end_3 = policy.m_upper[3]; + const Index end_4 = policy.m_upper[4]; + +#pragma omp target teams distribute parallel for collapse(5) map(to : functor) + for (auto i0 = begin_0; i0 < end_0; ++i0) { + for (auto i1 = begin_1; i1 < end_1; ++i1) { + for (auto i2 = begin_2; i2 < end_2; ++i2) { + for (auto i3 = begin_3; i3 < end_3; ++i3) { + for (auto i4 = begin_4; i4 < end_4; ++i4) { + if constexpr (std::is_same::value) + functor(i0, i1, i2, i3, i4); + else + functor(typename Policy::work_tag(), i0, i1, i2, i3, i4); + } + } + } + } + } + } + + template + inline std::enable_if_t execute_tile( + typename Policy::point_type offset, const FunctorType& functor, + const Policy& policy, OpenMPTargetIterateRight) const { + (void)offset; + const Index begin_0 = policy.m_lower[0]; + const Index begin_1 = policy.m_lower[1]; + const Index begin_2 = policy.m_lower[2]; + const Index begin_3 = policy.m_lower[3]; + const Index begin_4 = policy.m_lower[4]; + const Index begin_5 = policy.m_lower[5]; + + const Index end_0 = policy.m_upper[0]; + const Index end_1 = policy.m_upper[1]; + const Index end_2 = policy.m_upper[2]; + const Index end_3 = policy.m_upper[3]; + const Index end_4 = policy.m_upper[4]; + const Index end_5 = policy.m_upper[5]; + +#pragma omp target teams distribute parallel for collapse(6) map(to : functor) + for (auto i0 = begin_0; i0 < end_0; ++i0) { + for (auto i1 = begin_1; i1 < end_1; ++i1) { + for (auto i2 = begin_2; i2 < end_2; ++i2) { + for (auto i3 = begin_3; i3 < end_3; ++i3) { + for (auto i4 = begin_4; i4 < end_4; ++i4) { + for (auto i5 = begin_5; i5 < end_5; ++i5) { + { + if constexpr (std::is_same::value) + functor(i0, i1, i2, i3, i4, i5); + else + functor(typename Policy::work_tag(), i0, i1, i2, i3, i4, + i5); + } + } + } + } + } + } + } + } + + template + inline std::enable_if_t execute_tile( + typename Policy::point_type offset, const FunctorType& functor, + const Policy& policy, OpenMPTargetIterateLeft) const { + (void)offset; + const Index begin_0 = policy.m_lower[0]; + const Index begin_1 = policy.m_lower[1]; + + const Index end_0 = policy.m_upper[0]; + const Index end_1 = policy.m_upper[1]; + +#pragma omp target teams distribute parallel for collapse(2) map(to : functor) + for (auto i1 = begin_1; i1 < end_1; ++i1) + for (auto i0 = begin_0; i0 < end_0; ++i0) { + if constexpr (std::is_void::value) + functor(i0, i1); + else + functor(typename Policy::work_tag(), i0, i1); + } + } + + template + inline std::enable_if_t execute_tile( + typename Policy::point_type offset, const FunctorType& functor, + const Policy& policy, OpenMPTargetIterateLeft) const { + (void)offset; + const Index begin_0 = policy.m_lower[0]; + const Index begin_1 = policy.m_lower[1]; + const Index begin_2 = policy.m_lower[2]; + + const Index end_0 = policy.m_upper[0]; + const Index end_1 = policy.m_upper[1]; + const Index end_2 = policy.m_upper[2]; + +#pragma omp target teams distribute parallel for collapse(3) map(to : functor) + for (auto i2 = begin_2; i2 < end_2; ++i2) { + for (auto i1 = begin_1; i1 < end_1; ++i1) { + for (auto i0 = begin_0; i0 < end_0; ++i0) { + if constexpr (std::is_void::value) + functor(i0, i1, i2); + else + functor(typename Policy::work_tag(), i0, i1, i2); + } + } + } + } + + template + inline std::enable_if_t execute_tile( + typename Policy::point_type offset, const FunctorType& functor, + const Policy& policy, OpenMPTargetIterateLeft) const { + (void)offset; + const Index begin_0 = policy.m_lower[0]; + const Index begin_1 = policy.m_lower[1]; + const Index begin_2 = policy.m_lower[2]; + const Index begin_3 = policy.m_lower[3]; + + const Index end_0 = policy.m_upper[0]; + const Index end_1 = policy.m_upper[1]; + const Index end_2 = policy.m_upper[2]; + const Index end_3 = policy.m_upper[3]; + +#pragma omp target teams distribute parallel for collapse(4) map(to : functor) + for (auto i3 = begin_3; i3 < end_3; ++i3) { + for (auto i2 = begin_2; i2 < end_2; ++i2) { + for (auto i1 = begin_1; i1 < end_1; ++i1) { + for (auto i0 = begin_0; i0 < end_0; ++i0) { + if constexpr (std::is_void::value) + functor(i0, i1, i2, i3); + else + functor(typename Policy::work_tag(), i0, i1, i2, i3); + } + } + } + } + } + + template + inline std::enable_if_t execute_tile( + typename Policy::point_type offset, const FunctorType& functor, + const Policy& policy, OpenMPTargetIterateLeft) const { + (void)offset; + const Index begin_0 = policy.m_lower[0]; + const Index begin_1 = policy.m_lower[1]; + const Index begin_2 = policy.m_lower[2]; + const Index begin_3 = policy.m_lower[3]; + const Index begin_4 = policy.m_lower[4]; + + const Index end_0 = policy.m_upper[0]; + const Index end_1 = policy.m_upper[1]; + const Index end_2 = policy.m_upper[2]; + const Index end_3 = policy.m_upper[3]; + const Index end_4 = policy.m_upper[4]; + +#pragma omp target teams distribute parallel for collapse(5) map(to : functor) + for (auto i4 = begin_4; i4 < end_4; ++i4) { + for (auto i3 = begin_3; i3 < end_3; ++i3) { + for (auto i2 = begin_2; i2 < end_2; ++i2) { + for (auto i1 = begin_1; i1 < end_1; ++i1) { + for (auto i0 = begin_0; i0 < end_0; ++i0) { + if constexpr (std::is_same::value) + functor(i0, i1, i2, i3, i4); + else + functor(typename Policy::work_tag(), i0, i1, i2, i3, i4); + } + } + } + } + } + } + + template + inline std::enable_if_t execute_tile( + typename Policy::point_type offset, const FunctorType& functor, + const Policy& policy, OpenMPTargetIterateLeft) const { + (void)offset; + const Index begin_0 = policy.m_lower[0]; + const Index begin_1 = policy.m_lower[1]; + const Index begin_2 = policy.m_lower[2]; + const Index begin_3 = policy.m_lower[3]; + const Index begin_4 = policy.m_lower[4]; + const Index begin_5 = policy.m_lower[5]; + + const Index end_0 = policy.m_upper[0]; + const Index end_1 = policy.m_upper[1]; + const Index end_2 = policy.m_upper[2]; + const Index end_3 = policy.m_upper[3]; + const Index end_4 = policy.m_upper[4]; + const Index end_5 = policy.m_upper[5]; + +#pragma omp target teams distribute parallel for collapse(6) map(to : functor) + for (auto i5 = begin_5; i5 < end_5; ++i5) { + for (auto i4 = begin_4; i4 < end_4; ++i4) { + for (auto i3 = begin_3; i3 < end_3; ++i3) { + for (auto i2 = begin_2; i2 < end_2; ++i2) { + for (auto i1 = begin_1; i1 < end_1; ++i1) { + for (auto i0 = begin_0; i0 < end_0; ++i0) { + { + if constexpr (std::is_same::value) + functor(i0, i1, i2, i3, i4, i5); + else + functor(typename Policy::work_tag(), i0, i1, i2, i3, i4, + i5); + } + } + } + } + } + } + } + } + + inline ParallelFor(const FunctorType& arg_functor, Policy arg_policy) + : m_functor(arg_functor), m_policy(arg_policy) {} + // TODO DZP: based on a conversation with Christian, we're using 256 as a + // heuristic here. We need something better once we can query these kinds of + // properties + template + static int max_tile_size_product(const Policy&, const Functor&) { + return 256; + } +}; + +} // namespace Impl +} // namespace Kokkos + +#endif /* KOKKOS_OPENMPTARGET_PARALLELFOR_MDRANGE_HPP */ diff --git a/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Parallel_MDRange.hpp b/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_ParallelReduce_MDRange.hpp similarity index 61% rename from lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Parallel_MDRange.hpp rename to lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_ParallelReduce_MDRange.hpp index 6878531730..e86a121974 100644 --- a/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Parallel_MDRange.hpp +++ b/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_ParallelReduce_MDRange.hpp @@ -14,397 +14,14 @@ // //@HEADER -#ifndef KOKKOS_OPENMPTARGET_PARALLEL_MDRANGE_HPP -#define KOKKOS_OPENMPTARGET_PARALLEL_MDRANGE_HPP +#ifndef KOKKOS_OPENMPTARGET_PARALLELREDUCE_MDRANGE_HPP +#define KOKKOS_OPENMPTARGET_PARALLELREDUCE_MDRANGE_HPP #include #include -#include +#include "Kokkos_OpenMPTarget_MDRangePolicy.hpp" #include -// WORKAROUND OPENMPTARGET: sometimes tile sizes don't make it correctly, -// this was tracked down to a bug in clang with regards of mapping structs -// with arrays of long in it. Arrays of int might be fine though ... -#define KOKKOS_IMPL_MDRANGE_USE_NO_TILES // undef EOF - -//---------------------------------------------------------------------------- -//---------------------------------------------------------------------------- - -namespace Kokkos { -namespace Impl { - -template -class ParallelFor, - Kokkos::Experimental::OpenMPTarget> { - private: - using Policy = Kokkos::MDRangePolicy; - using WorkTag = typename Policy::work_tag; - using Member = typename Policy::member_type; - using Index = typename Policy::index_type; - - const FunctorType m_functor; - const Policy m_policy; - - public: - inline void execute() const { - OpenMPTargetExec::verify_is_process( - "Kokkos::Experimental::OpenMPTarget parallel_for"); - OpenMPTargetExec::verify_initialized( - "Kokkos::Experimental::OpenMPTarget parallel_for"); - FunctorType functor(m_functor); - Policy policy = m_policy; - -#ifdef KOKKOS_IMPL_MDRANGE_USE_NO_TILES - typename Policy::point_type unused; - - execute_tile(unused, functor, policy); -#else - const int64_t begin = 0; - const int64_t end = m_policy.m_num_tiles; - -#pragma omp target teams distribute map(to : functor) num_teams(end - begin) - { - for (ptrdiff_t tile_idx = begin; tile_idx < end; ++tile_idx) { - -#pragma omp parallel - { - typename Policy::point_type offset; - if (Policy::outer_direction == Policy::Left) { - for (int i = 0; i < Policy::rank; ++i) { - offset[i] = (tile_idx % policy.m_tile_end[i]) * policy.m_tile[i] + - policy.m_lower[i]; - tile_idx /= policy.m_tile_end[i]; - } - } else { - for (int i = Policy::rank - 1; i >= 0; --i) { - offset[i] = (tile_idx % policy.m_tile_end[i]) * policy.m_tile[i] + - policy.m_lower[i]; - tile_idx /= policy.m_tile_end[i]; - } - } - execute_tile(offset, functor, policy); - } - } - } -#endif - } - - template - inline std::enable_if_t execute_tile( - typename Policy::point_type offset, const FunctorType& functor, - const Policy& policy) const { -#ifdef KOKKOS_IMPL_MDRANGE_USE_NO_TILES - (void)offset; - const Index begin_0 = policy.m_lower[0]; - const Index begin_1 = policy.m_lower[1]; - - const Index end_0 = policy.m_upper[0]; - const Index end_1 = policy.m_upper[1]; - -#pragma omp target teams distribute parallel for collapse(2) map(to : functor) - for (auto i0 = begin_0; i0 < end_0; ++i0) { - for (auto i1 = begin_1; i1 < end_1; ++i1) { - if constexpr (std::is_void::value) - functor(i0, i1); - else - functor(typename Policy::work_tag(), i0, i1); - } - } -#else - const ptrdiff_t begin_0 = offset[0]; - ptrdiff_t end_0 = begin_0 + policy.m_tile[0]; - end_0 = end_0 < policy.m_upper[0] ? end_0 : policy.m_upper[0]; - - const ptrdiff_t begin_1 = offset[1]; - ptrdiff_t end_1 = begin_1 + policy.m_tile[1]; - end_1 = end_1 < policy.m_upper[1] ? end_1 : policy.m_upper[1]; - -#pragma omp for collapse(2) - for (ptrdiff_t i0 = begin_0; i0 < end_0; ++i0) - for (ptrdiff_t i1 = begin_1; i1 < end_1; ++i1) { - if constexpr (std::is_void::value) - functor(i0, i1); - else - functor(typename Policy::work_tag(), i0, i1); - } -#endif - } - - template - inline std::enable_if_t execute_tile( - typename Policy::point_type offset, const FunctorType& functor, - const Policy& policy) const { -#ifdef KOKKOS_IMPL_MDRANGE_USE_NO_TILES - (void)offset; - const Index begin_0 = policy.m_lower[0]; - const Index begin_1 = policy.m_lower[1]; - const Index begin_2 = policy.m_lower[2]; - - const Index end_0 = policy.m_upper[0]; - const Index end_1 = policy.m_upper[1]; - const Index end_2 = policy.m_upper[2]; - -#pragma omp target teams distribute parallel for collapse(3) map(to : functor) - for (auto i0 = begin_0; i0 < end_0; ++i0) { - for (auto i1 = begin_1; i1 < end_1; ++i1) { - for (auto i2 = begin_2; i2 < end_2; ++i2) { - if constexpr (std::is_void::value) - functor(i0, i1, i2); - else - functor(typename Policy::work_tag(), i0, i1, i2); - } - } - } -#else - const ptrdiff_t begin_0 = offset[0]; - ptrdiff_t end_0 = begin_0 + policy.m_tile[0]; - end_0 = end_0 < policy.m_upper[0] ? end_0 : policy.m_upper[0]; - - const ptrdiff_t begin_1 = offset[1]; - ptrdiff_t end_1 = begin_1 + policy.m_tile[1]; - end_1 = end_1 < policy.m_upper[1] ? end_1 : policy.m_upper[1]; - - const ptrdiff_t begin_2 = offset[2]; - ptrdiff_t end_2 = begin_2 + policy.m_tile[2]; - end_2 = end_2 < policy.m_upper[2] ? end_2 : policy.m_upper[2]; - -#pragma omp for collapse(3) - for (ptrdiff_t i0 = begin_0; i0 < end_0; ++i0) - for (ptrdiff_t i1 = begin_1; i1 < end_1; ++i1) - for (ptrdiff_t i2 = begin_2; i2 < end_2; ++i2) { - if constexpr (std::is_void::value) - functor(i0, i1, i2); - else - functor(typename Policy::work_tag(), i0, i1, i2); - } -#endif - } - - template - inline std::enable_if_t execute_tile( - typename Policy::point_type offset, const FunctorType& functor, - const Policy& policy) const { -#ifdef KOKKOS_IMPL_MDRANGE_USE_NO_TILES - (void)offset; - const Index begin_0 = policy.m_lower[0]; - const Index begin_1 = policy.m_lower[1]; - const Index begin_2 = policy.m_lower[2]; - const Index begin_3 = policy.m_lower[3]; - - const Index end_0 = policy.m_upper[0]; - const Index end_1 = policy.m_upper[1]; - const Index end_2 = policy.m_upper[2]; - const Index end_3 = policy.m_upper[3]; - -#pragma omp target teams distribute parallel for collapse(4) map(to : functor) - for (auto i0 = begin_0; i0 < end_0; ++i0) { - for (auto i1 = begin_1; i1 < end_1; ++i1) { - for (auto i2 = begin_2; i2 < end_2; ++i2) { - for (auto i3 = begin_3; i3 < end_3; ++i3) { - if constexpr (std::is_void::value) - functor(i0, i1, i2, i3); - else - functor(typename Policy::work_tag(), i0, i1, i2, i3); - } - } - } - } -#else - const ptrdiff_t begin_0 = offset[0]; - ptrdiff_t end_0 = begin_0 + policy.m_tile[0]; - end_0 = end_0 < policy.m_upper[0] ? end_0 : policy.m_upper[0]; - - const ptrdiff_t begin_1 = offset[1]; - ptrdiff_t end_1 = begin_1 + policy.m_tile[1]; - end_1 = end_1 < policy.m_upper[1] ? end_1 : policy.m_upper[1]; - - const ptrdiff_t begin_2 = offset[2]; - ptrdiff_t end_2 = begin_2 + policy.m_tile[2]; - end_2 = end_2 < policy.m_upper[2] ? end_2 : policy.m_upper[2]; - - const ptrdiff_t begin_3 = offset[3]; - ptrdiff_t end_3 = begin_3 + policy.m_tile[3]; - end_3 = end_3 < policy.m_upper[3] ? end_3 : policy.m_upper[3]; - -#pragma omp for collapse(4) - for (ptrdiff_t i0 = begin_0; i0 < end_0; ++i0) - for (ptrdiff_t i1 = begin_1; i1 < end_1; ++i1) - for (ptrdiff_t i2 = begin_2; i2 < end_2; ++i2) - for (ptrdiff_t i3 = begin_3; i3 < end_3; ++i3) { - if constexpr (std::is_void::value) - functor(i0, i1, i2, i3); - else - functor(typename Policy::work_tag(), i0, i1, i2, i3); - } -#endif - } - - template - inline std::enable_if_t execute_tile( - typename Policy::point_type offset, const FunctorType& functor, - const Policy& policy) const { -#ifdef KOKKOS_IMPL_MDRANGE_USE_NO_TILES - (void)offset; - const Index begin_0 = policy.m_lower[0]; - const Index begin_1 = policy.m_lower[1]; - const Index begin_2 = policy.m_lower[2]; - const Index begin_3 = policy.m_lower[3]; - const Index begin_4 = policy.m_lower[4]; - - const Index end_0 = policy.m_upper[0]; - const Index end_1 = policy.m_upper[1]; - const Index end_2 = policy.m_upper[2]; - const Index end_3 = policy.m_upper[3]; - const Index end_4 = policy.m_upper[4]; - -#pragma omp target teams distribute parallel for collapse(5) map(to : functor) - for (auto i0 = begin_0; i0 < end_0; ++i0) { - for (auto i1 = begin_1; i1 < end_1; ++i1) { - for (auto i2 = begin_2; i2 < end_2; ++i2) { - for (auto i3 = begin_3; i3 < end_3; ++i3) { - for (auto i4 = begin_4; i4 < end_4; ++i4) { - if constexpr (std::is_same::value) - functor(i0, i1, i2, i3, i4); - else - functor(typename Policy::work_tag(), i0, i1, i2, i3, i4); - } - } - } - } - } -#else - const ptrdiff_t begin_0 = offset[0]; - ptrdiff_t end_0 = begin_0 + policy.m_tile[0]; - end_0 = end_0 < policy.m_upper[0] ? end_0 : policy.m_upper[0]; - - const ptrdiff_t begin_1 = offset[1]; - ptrdiff_t end_1 = begin_1 + policy.m_tile[1]; - end_1 = end_1 < policy.m_upper[1] ? end_1 : policy.m_upper[1]; - - const ptrdiff_t begin_2 = offset[2]; - ptrdiff_t end_2 = begin_2 + policy.m_tile[2]; - end_2 = end_2 < policy.m_upper[2] ? end_2 : policy.m_upper[2]; - - const ptrdiff_t begin_3 = offset[3]; - ptrdiff_t end_3 = begin_3 + policy.m_tile[3]; - end_3 = end_3 < policy.m_upper[3] ? end_3 : policy.m_upper[3]; - - const ptrdiff_t begin_4 = offset[4]; - ptrdiff_t end_4 = begin_4 + policy.m_tile[4]; - end_4 = end_4 < policy.m_upper[4] ? end_4 : policy.m_upper[4]; - -#pragma omp for collapse(5) - for (ptrdiff_t i0 = begin_0; i0 < end_0; ++i0) - for (ptrdiff_t i1 = begin_1; i1 < end_1; ++i1) - for (ptrdiff_t i2 = begin_2; i2 < end_2; ++i2) - for (ptrdiff_t i3 = begin_3; i3 < end_3; ++i3) - for (ptrdiff_t i4 = begin_4; i4 < end_4; ++i4) { - if constexpr (std::is_same::value) - functor(i0, i1, i2, i3, i4); - else - functor(typename Policy::work_tag(), i0, i1, i2, i3, i4); - } -#endif - } - - template - inline std::enable_if_t execute_tile( - typename Policy::point_type offset, const FunctorType& functor, - const Policy& policy) const { -#ifdef KOKKOS_IMPL_MDRANGE_USE_NO_TILES - (void)offset; - const Index begin_0 = policy.m_lower[0]; - const Index begin_1 = policy.m_lower[1]; - const Index begin_2 = policy.m_lower[2]; - const Index begin_3 = policy.m_lower[3]; - const Index begin_4 = policy.m_lower[4]; - const Index begin_5 = policy.m_lower[5]; - - const Index end_0 = policy.m_upper[0]; - const Index end_1 = policy.m_upper[1]; - const Index end_2 = policy.m_upper[2]; - const Index end_3 = policy.m_upper[3]; - const Index end_4 = policy.m_upper[4]; - const Index end_5 = policy.m_upper[5]; - -#pragma omp target teams distribute parallel for collapse(6) map(to : functor) - for (auto i0 = begin_0; i0 < end_0; ++i0) { - for (auto i1 = begin_1; i1 < end_1; ++i1) { - for (auto i2 = begin_2; i2 < end_2; ++i2) { - for (auto i3 = begin_3; i3 < end_3; ++i3) { - for (auto i4 = begin_4; i4 < end_4; ++i4) { - for (auto i5 = begin_5; i5 < end_5; ++i5) { - { - if constexpr (std::is_same::value) - functor(i0, i1, i2, i3, i4, i5); - else - functor(typename Policy::work_tag(), i0, i1, i2, i3, i4, - i5); - } - } - } - } - } - } - } -#else - const ptrdiff_t begin_0 = offset[0]; - ptrdiff_t end_0 = begin_0 + policy.m_tile[0]; - end_0 = end_0 < policy.m_upper[0] ? end_0 : policy.m_upper[0]; - - const ptrdiff_t begin_1 = offset[1]; - ptrdiff_t end_1 = begin_1 + policy.m_tile[1]; - end_1 = end_1 < policy.m_upper[1] ? end_1 : policy.m_upper[1]; - - const ptrdiff_t begin_2 = offset[2]; - ptrdiff_t end_2 = begin_2 + policy.m_tile[2]; - end_2 = end_2 < policy.m_upper[2] ? end_2 : policy.m_upper[2]; - - const ptrdiff_t begin_3 = offset[3]; - ptrdiff_t end_3 = begin_3 + policy.m_tile[3]; - end_3 = end_3 < policy.m_upper[3] ? end_3 : policy.m_upper[3]; - - const ptrdiff_t begin_4 = offset[4]; - ptrdiff_t end_4 = begin_4 + policy.m_tile[4]; - end_4 = end_4 < policy.m_upper[4] ? end_4 : policy.m_upper[4]; - - const ptrdiff_t begin_5 = offset[5]; - ptrdiff_t end_5 = begin_5 + policy.m_tile[5]; - end_5 = end_5 < policy.m_upper[5] ? end_5 : policy.m_upper[5]; - -#pragma omp for collapse(6) - for (ptrdiff_t i0 = begin_0; i0 < end_0; ++i0) - for (ptrdiff_t i1 = begin_1; i1 < end_1; ++i1) - for (ptrdiff_t i2 = begin_2; i2 < end_2; ++i2) - for (ptrdiff_t i3 = begin_3; i3 < end_3; ++i3) - for (ptrdiff_t i4 = begin_4; i4 < end_4; ++i4) - for (ptrdiff_t i5 = begin_5; i5 < end_5; ++i5) { - if constexpr (std::is_same::value) - functor(i0, i1, i2, i3, i4, i5); - else - functor(typename Policy::work_tag(), i0, i1, i2, i3, i4, i5); - } -#endif - } - - inline ParallelFor(const FunctorType& arg_functor, Policy arg_policy) - : m_functor(arg_functor), m_policy(arg_policy) {} - // TODO DZP: based on a conversation with Christian, we're using 256 as a - // heuristic here. We need something better once we can query these kinds of - // properties - template - static int max_tile_size_product(const Policy&, const Functor&) { - return 256; - } -}; - -} // namespace Impl -} // namespace Kokkos - //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- @@ -438,14 +55,14 @@ class ParallelReduce m_scratch_memory_lock; - public: inline void execute() const { + // Only let one ParallelReduce instance at a time use the scratch memory. + std::scoped_lock scratch_memory_lock( + OpenMPTargetExec::m_mutex_scratch_ptr); execute_tile( - m_functor_reducer.get_functor(), m_policy, m_result_ptr); + m_functor_reducer.get_functor(), m_policy, m_result_ptr, + std::integral_constant()); } template @@ -456,13 +73,330 @@ class ParallelReduce::accessible), - m_scratch_memory_lock(OpenMPTargetExec::m_mutex_scratch_ptr) {} + typename ViewType::memory_space>::accessible) {} template - inline std::enable_if_t execute_tile(const FunctorType& functor, - const Policy& policy, - pointer_type ptr) const { + inline std::enable_if_t execute_tile( + const FunctorType& functor, const Policy& policy, pointer_type ptr, + OpenMPTargetIterateLeft) const { + const Index begin_0 = policy.m_lower[0]; + const Index begin_1 = policy.m_lower[1]; + + const Index end_0 = policy.m_upper[0]; + const Index end_1 = policy.m_upper[1]; + + ValueType result = ValueType(); + + // FIXME_OPENMPTARGET: Unable to separate directives and their companion + // loops which leads to code duplication for different reduction types. + if constexpr (UseReducer) { +#pragma omp declare reduction( \ + custom:ValueType \ + : OpenMPTargetReducerWrapper ::join(omp_out, omp_in)) \ + initializer(OpenMPTargetReducerWrapper ::init(omp_priv)) + +#pragma omp target teams distribute parallel for collapse(2) map(to \ + : functor) \ + reduction(custom \ + : result) + for (auto i1 = begin_1; i1 < end_1; ++i1) { + for (auto i0 = begin_0; i0 < end_0; ++i0) { + if constexpr (std::is_void::value) + functor(i0, i1, result); + else + functor(typename Policy::work_tag(), i0, i1, result); + } + } + } else { +#pragma omp target teams distribute parallel for collapse(2) map(to : functor) \ +reduction(+:result) + for (auto i1 = begin_1; i1 < end_1; ++i1) { + for (auto i0 = begin_0; i0 < end_0; ++i0) { + if constexpr (std::is_void::value) + functor(i0, i1, result); + else + functor(typename Policy::work_tag(), i0, i1, result); + } + } + } + + ParReduceCopy::memcpy_result(ptr, &result, sizeof(ValueType), + m_result_ptr_on_device); + } + + template + inline std::enable_if_t execute_tile( + const FunctorType& functor, const Policy& policy, pointer_type ptr, + OpenMPTargetIterateLeft) const { + const Index begin_0 = policy.m_lower[0]; + const Index begin_1 = policy.m_lower[1]; + const Index begin_2 = policy.m_lower[2]; + + const Index end_0 = policy.m_upper[0]; + const Index end_1 = policy.m_upper[1]; + const Index end_2 = policy.m_upper[2]; + + ValueType result = ValueType(); + + // FIXME_OPENMPTARGET: Unable to separate directives and their companion + // loops which leads to code duplication for different reduction types. + if constexpr (UseReducer) { +#pragma omp declare reduction( \ + custom:ValueType \ + : OpenMPTargetReducerWrapper ::join( \ + omp_out, omp_in)) \ + initializer( \ + OpenMPTargetReducerWrapper ::init( \ + omp_priv)) + +#pragma omp target teams distribute parallel for collapse(3) map(to \ + : functor) \ + reduction(custom \ + : result) + for (auto i2 = begin_2; i2 < end_2; ++i2) { + for (auto i1 = begin_1; i1 < end_1; ++i1) { + for (auto i0 = begin_0; i0 < end_0; ++i0) { + if constexpr (std::is_void::value) + functor(i0, i1, i2, result); + else + functor(typename Policy::work_tag(), i0, i1, i2, result); + } + } + } + } else { +#pragma omp target teams distribute parallel for collapse(3) map(to : functor) \ +reduction(+:result) + for (auto i2 = begin_2; i2 < end_2; ++i2) { + for (auto i1 = begin_1; i1 < end_1; ++i1) { + for (auto i0 = begin_0; i0 < end_0; ++i0) { + if constexpr (std::is_void::value) + functor(i0, i1, i2, result); + else + functor(typename Policy::work_tag(), i0, i1, i2, result); + } + } + } + } + + ParReduceCopy::memcpy_result(ptr, &result, sizeof(ValueType), + m_result_ptr_on_device); + } + + template + inline std::enable_if_t execute_tile( + const FunctorType& functor, const Policy& policy, pointer_type ptr, + OpenMPTargetIterateLeft) const { + const Index begin_0 = policy.m_lower[0]; + const Index begin_1 = policy.m_lower[1]; + const Index begin_2 = policy.m_lower[3]; + const Index begin_3 = policy.m_lower[2]; + + const Index end_0 = policy.m_upper[0]; + const Index end_1 = policy.m_upper[1]; + const Index end_2 = policy.m_upper[2]; + const Index end_3 = policy.m_upper[3]; + + ValueType result = ValueType(); + + // FIXME_OPENMPTARGET: Unable to separate directives and their companion + // loops which leads to code duplication for different reduction types. + if constexpr (UseReducer) { +#pragma omp declare reduction( \ + custom:ValueType \ + : OpenMPTargetReducerWrapper ::join(omp_out, omp_in)) \ + initializer(OpenMPTargetReducerWrapper ::init(omp_priv)) + +#pragma omp target teams distribute parallel for collapse(4) map(to \ + : functor) \ + reduction(custom \ + : result) + for (auto i3 = begin_3; i3 < end_3; ++i3) { + for (auto i2 = begin_2; i2 < end_2; ++i2) { + for (auto i1 = begin_1; i1 < end_1; ++i1) { + for (auto i0 = begin_0; i0 < end_0; ++i0) { + if constexpr (std::is_same::value) + functor(i0, i1, i2, i3, result); + else + functor(typename Policy::work_tag(), i0, i1, i2, i3, result); + } + } + } + } + } else { +#pragma omp target teams distribute parallel for collapse(4) map(to : functor) \ +reduction(+:result) + for (auto i3 = begin_3; i3 < end_3; ++i3) { + for (auto i2 = begin_2; i2 < end_2; ++i2) { + for (auto i1 = begin_1; i1 < end_1; ++i1) { + for (auto i0 = begin_0; i0 < end_0; ++i0) { + if constexpr (std::is_same::value) + functor(i0, i1, i2, i3, result); + else + functor(typename Policy::work_tag(), i0, i1, i2, i3, result); + } + } + } + } + } + + ParReduceCopy::memcpy_result(ptr, &result, sizeof(ValueType), + m_result_ptr_on_device); + } + + template + inline std::enable_if_t execute_tile( + const FunctorType& functor, const Policy& policy, pointer_type ptr, + OpenMPTargetIterateLeft) const { + const Index begin_0 = policy.m_lower[0]; + const Index begin_1 = policy.m_lower[1]; + const Index begin_2 = policy.m_lower[2]; + const Index begin_3 = policy.m_lower[3]; + const Index begin_4 = policy.m_lower[4]; + + const Index end_0 = policy.m_upper[0]; + const Index end_1 = policy.m_upper[1]; + const Index end_2 = policy.m_upper[2]; + const Index end_3 = policy.m_upper[3]; + const Index end_4 = policy.m_upper[4]; + + ValueType result = ValueType(); + + // FIXME_OPENMPTARGET: Unable to separate directives and their companion + // loops which leads to code duplication for different reduction types. + if constexpr (UseReducer) { +#pragma omp declare reduction( \ + custom:ValueType \ + : OpenMPTargetReducerWrapper ::join(omp_out, omp_in)) \ + initializer(OpenMPTargetReducerWrapper ::init(omp_priv)) + +#pragma omp target teams distribute parallel for collapse(5) map(to \ + : functor) \ + reduction(custom \ + : result) + for (auto i4 = begin_4; i4 < end_4; ++i4) { + for (auto i3 = begin_3; i3 < end_3; ++i3) { + for (auto i2 = begin_2; i2 < end_2; ++i2) { + for (auto i1 = begin_1; i1 < end_1; ++i1) { + for (auto i0 = begin_0; i0 < end_0; ++i0) { + if constexpr (std::is_same::value) + functor(i0, i1, i2, i3, i4, result); + else + functor(typename Policy::work_tag(), i0, i1, i2, i3, i4, + result); + } + } + } + } + } + } else { +#pragma omp target teams distribute parallel for collapse(5) map(to : functor) \ +reduction(+:result) + for (auto i4 = begin_4; i4 < end_4; ++i4) { + for (auto i3 = begin_3; i3 < end_3; ++i3) { + for (auto i2 = begin_2; i2 < end_2; ++i2) { + for (auto i1 = begin_1; i1 < end_1; ++i1) { + for (auto i0 = begin_0; i0 < end_0; ++i0) { + if constexpr (std::is_same::value) + functor(i0, i1, i2, i3, i4, result); + else + functor(typename Policy::work_tag(), i0, i1, i2, i3, i4, + result); + } + } + } + } + } + } + + ParReduceCopy::memcpy_result(ptr, &result, sizeof(ValueType), + m_result_ptr_on_device); + } + + template + inline std::enable_if_t execute_tile( + const FunctorType& functor, const Policy& policy, pointer_type ptr, + OpenMPTargetIterateLeft) const { + const Index begin_0 = policy.m_lower[0]; + const Index begin_1 = policy.m_lower[1]; + const Index begin_2 = policy.m_lower[2]; + const Index begin_3 = policy.m_lower[3]; + const Index begin_4 = policy.m_lower[4]; + const Index begin_5 = policy.m_lower[5]; + + const Index end_0 = policy.m_upper[0]; + const Index end_1 = policy.m_upper[1]; + const Index end_2 = policy.m_upper[2]; + const Index end_3 = policy.m_upper[3]; + const Index end_4 = policy.m_upper[4]; + const Index end_5 = policy.m_upper[5]; + + ValueType result = ValueType(); + + // FIXME_OPENMPTARGET: Unable to separate directives and their companion + // loops which leads to code duplication for different reduction types. + if constexpr (UseReducer) { +#pragma omp declare reduction( \ + custom:ValueType \ + : OpenMPTargetReducerWrapper ::join(omp_out, omp_in)) \ + initializer(OpenMPTargetReducerWrapper ::init(omp_priv)) + +#pragma omp target teams distribute parallel for collapse(6) map(to \ + : functor) \ + reduction(custom \ + : result) + for (auto i5 = begin_5; i5 < end_5; ++i5) { + for (auto i4 = begin_4; i4 < end_4; ++i4) { + for (auto i3 = begin_3; i3 < end_3; ++i3) { + for (auto i2 = begin_2; i2 < end_2; ++i2) { + for (auto i1 = begin_1; i1 < end_1; ++i1) { + for (auto i0 = begin_0; i0 < end_0; ++i0) { + if constexpr (std::is_same::value) + functor(i0, i1, i2, i3, i4, i5, result); + else + functor(typename Policy::work_tag(), i0, i1, i2, i3, i4, i5, + result); + } + } + } + } + } + } + } else { +#pragma omp target teams distribute parallel for collapse(6) map(to : functor) \ +reduction(+:result) + for (auto i5 = begin_5; i5 < end_5; ++i5) { + for (auto i4 = begin_4; i4 < end_4; ++i4) { + for (auto i3 = begin_3; i3 < end_3; ++i3) { + for (auto i2 = begin_2; i2 < end_2; ++i2) { + for (auto i1 = begin_1; i1 < end_1; ++i1) { + for (auto i0 = begin_0; i0 < end_0; ++i0) { + if constexpr (std::is_same::value) + functor(i0, i1, i2, i3, i4, i5, result); + else + functor(typename Policy::work_tag(), i0, i1, i2, i3, i4, i5, + result); + } + } + } + } + } + } + } + + ParReduceCopy::memcpy_result(ptr, &result, sizeof(ValueType), + m_result_ptr_on_device); + } + + template + inline std::enable_if_t execute_tile( + const FunctorType& functor, const Policy& policy, pointer_type ptr, + OpenMPTargetIterateRight) const { const Index begin_0 = policy.m_lower[0]; const Index begin_1 = policy.m_lower[1]; @@ -509,9 +443,9 @@ reduction(+:result) } template - inline std::enable_if_t execute_tile(const FunctorType& functor, - const Policy& policy, - pointer_type ptr) const { + inline std::enable_if_t execute_tile( + const FunctorType& functor, const Policy& policy, pointer_type ptr, + OpenMPTargetIterateRight) const { const Index begin_0 = policy.m_lower[0]; const Index begin_1 = policy.m_lower[1]; const Index begin_2 = policy.m_lower[2]; @@ -567,9 +501,9 @@ reduction(+:result) } template - inline std::enable_if_t execute_tile(const FunctorType& functor, - const Policy& policy, - pointer_type ptr) const { + inline std::enable_if_t execute_tile( + const FunctorType& functor, const Policy& policy, pointer_type ptr, + OpenMPTargetIterateRight) const { const Index begin_0 = policy.m_lower[0]; const Index begin_1 = policy.m_lower[1]; const Index begin_2 = policy.m_lower[3]; @@ -630,9 +564,9 @@ reduction(+:result) } template - inline std::enable_if_t execute_tile(const FunctorType& functor, - const Policy& policy, - pointer_type ptr) const { + inline std::enable_if_t execute_tile( + const FunctorType& functor, const Policy& policy, pointer_type ptr, + OpenMPTargetIterateRight) const { const Index begin_0 = policy.m_lower[0]; const Index begin_1 = policy.m_lower[1]; const Index begin_2 = policy.m_lower[2]; @@ -701,9 +635,9 @@ reduction(+:result) } template - inline std::enable_if_t execute_tile(const FunctorType& functor, - const Policy& policy, - pointer_type ptr) const { + inline std::enable_if_t execute_tile( + const FunctorType& functor, const Policy& policy, pointer_type ptr, + OpenMPTargetIterateRight) const { const Index begin_0 = policy.m_lower[0]; const Index begin_1 = policy.m_lower[1]; const Index begin_2 = policy.m_lower[2]; @@ -788,5 +722,4 @@ reduction(+:result) //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- -#undef KOKKOS_IMPL_MDRANGE_USE_NO_TILES -#endif /* KOKKOS_OPENMPTARGET_PARALLEL_HPP */ +#endif /* KOKKOS_OPENMPTARGET_PARALLELREDUCE_MDRANGE_HPP */ diff --git a/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_ParallelReduce_Range.hpp b/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_ParallelReduce_Range.hpp index caa568a892..4a112ed11d 100644 --- a/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_ParallelReduce_Range.hpp +++ b/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_ParallelReduce_Range.hpp @@ -55,13 +55,13 @@ class ParallelReduce, const pointer_type m_result_ptr; bool m_result_ptr_on_device; const int m_result_ptr_num_elems; - // Only let one ParallelReduce instance at a time use the scratch memory. - // The constructor acquires the mutex which is released in the destructor. - std::scoped_lock m_scratch_memory_lock; using TagType = typename Policy::work_tag; public: void execute() const { + // Only let one ParallelReduce instance at a time use the scratch memory. + std::scoped_lock scratch_memory_lock( + OpenMPTargetExec::m_mutex_scratch_ptr); const FunctorType& functor = m_functor_reducer.get_functor(); if constexpr (FunctorHasJoin) { // Enter this loop if the Functor has a init-join. @@ -108,8 +108,7 @@ class ParallelReduce, m_result_ptr_on_device( MemorySpaceAccess::accessible), - m_result_ptr_num_elems(arg_result_view.size()), - m_scratch_memory_lock(OpenMPTargetExec::m_mutex_scratch_ptr) {} + m_result_ptr_num_elems(arg_result_view.size()) {} }; } // namespace Impl diff --git a/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_ParallelReduce_Team.hpp b/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_ParallelReduce_Team.hpp index 8abffa47a4..16c0eedb81 100644 --- a/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_ParallelReduce_Team.hpp +++ b/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_ParallelReduce_Team.hpp @@ -470,12 +470,11 @@ class ParallelReduce m_scratch_memory_lock; - public: void execute() const { + // Only let one ParallelReduce instance at a time use the scratch memory. + std::scoped_lock scratch_memory_lock( + OpenMPTargetExec::m_mutex_scratch_ptr); const FunctorType& functor = m_functor_reducer.get_functor(); if constexpr (FunctorHasJoin) { ParReduceSpecialize::execute_init_join(functor, m_policy, m_result_ptr, @@ -521,8 +520,7 @@ class ParallelReduce::value( - arg_functor_reducer.get_functor(), arg_policy.team_size())), - m_scratch_memory_lock(OpenMPTargetExec::m_mutex_scratch_ptr) {} + arg_functor_reducer.get_functor(), arg_policy.team_size())) {} }; } // namespace Impl diff --git a/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_ParallelScan_Range.hpp b/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_ParallelScan_Range.hpp index 30195d96e0..b0d6932802 100644 --- a/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_ParallelScan_Range.hpp +++ b/lib/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_ParallelScan_Range.hpp @@ -143,7 +143,7 @@ class ParallelScan, local_offset_value = element_values(team_id, i - 1); // FIXME_OPENMPTARGET We seem to access memory illegaly on AMD GPUs #if defined(KOKKOS_ARCH_AMD_GPU) && !defined(KOKKOS_ARCH_AMD_GFX1030) && \ - !defined(KOKKOS_ARCH_AMD_GFX1100) && !defined(KOKKOS_ARCH_AMD_GFX1103) + !defined(KOKKOS_ARCH_AMD_GFX1100) if constexpr (Analysis::Reducer::has_join_member_function()) { if constexpr (std::is_void_v) a_functor_reducer.get_functor().join(local_offset_value, @@ -177,6 +177,10 @@ class ParallelScan, const idx_type chunk_size = 128; const idx_type n_chunks = (N + chunk_size - 1) / chunk_size; + // Only let one ParallelReduce instance at a time use the scratch memory. + std::scoped_lock scratch_memory_lock( + OpenMPTargetExec::m_mutex_scratch_ptr); + // This could be scratch memory per team Kokkos::View @@ -225,6 +229,10 @@ class ParallelScanWithTotal, const int64_t n_chunks = (N + chunk_size - 1) / chunk_size; if (N > 0) { + // Only let one ParallelReduce instance at a time use the scratch memory. + std::scoped_lock scratch_memory_lock( + OpenMPTargetExec::m_mutex_scratch_ptr); + // This could be scratch memory per team Kokkos::View diff --git a/lib/kokkos/core/src/SYCL/Kokkos_SYCL.cpp b/lib/kokkos/core/src/SYCL/Kokkos_SYCL.cpp index 9a246f7642..4de6931918 100644 --- a/lib/kokkos/core/src/SYCL/Kokkos_SYCL.cpp +++ b/lib/kokkos/core/src/SYCL/Kokkos_SYCL.cpp @@ -110,6 +110,31 @@ void SYCL::print_configuration(std::ostream& os, bool verbose) const { #else os << "macro KOKKOS_IMPL_SYCL_USE_IN_ORDER_QUEUES : undefined\n"; #endif +#ifdef SYCL_EXT_ONEAPI_GRAPH + os << "macro SYCL_EXT_ONEAPI_GRAPH : defined\n"; +#else + os << "macro SYCL_EXT_ONEAPI_GRAPH : undefined\n"; +#endif +#ifdef SYCL_EXT_INTEL_QUEUE_IMMEDIATE_COMMAND_LIST + if (sycl_queue() + .has_property< + sycl::ext::intel::property::queue::immediate_command_list>()) + os << "Immediate command lists enforced\n"; + else if (sycl_queue() + .has_property()) + os << "Standard command queue enforced\n"; + else +#endif + { + os << "Immediate command lists and standard command queue allowed.\n"; + if (const char* environment_setting = + std::getenv("SYCL_PI_LEVEL_ZERO_USE_IMMEDIATE_COMMANDLISTS")) + os << "SYCL_PI_LEVEL_ZERO_USE_IMMEDIATE_COMMANDLISTS=" + << environment_setting << " takes precedence.\n"; + else + os << "SYCL_PI_LEVEL_ZERO_USE_IMMEDIATE_COMMANDLISTS not defined.\n"; + } int counter = 0; int active_device = Kokkos::device_id(); diff --git a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_GraphNodeKernel.hpp b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_GraphNodeKernel.hpp new file mode 100644 index 0000000000..9c39df9415 --- /dev/null +++ b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_GraphNodeKernel.hpp @@ -0,0 +1,157 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#ifndef KOKKOS_SYCL_GRAPHNODEKERNEL_HPP +#define KOKKOS_SYCL_GRAPHNODEKERNEL_HPP + +#include + +#include + +#include +#include +#include + +#include + +namespace Kokkos { +namespace Impl { + +template +class GraphNodeKernelImpl + : public PatternImplSpecializationFromTag< + PatternTag, Functor, PolicyType, Args..., + Kokkos::Experimental::SYCL>::type { + public: + using Policy = PolicyType; + using graph_kernel = GraphNodeKernelImpl; + using base_t = typename PatternImplSpecializationFromTag< + PatternTag, Functor, Policy, Args..., Kokkos::Experimental::SYCL>::type; + + // TODO use the name and executionspace + template + GraphNodeKernelImpl(std::string, Kokkos::Experimental::SYCL const&, + Functor arg_functor, PolicyDeduced&& arg_policy, + ArgsDeduced&&... args) + : base_t(std::move(arg_functor), (PolicyDeduced &&) arg_policy, + (ArgsDeduced &&) args...) {} + + template + GraphNodeKernelImpl(Kokkos::Experimental::SYCL const& exec_space, + Functor arg_functor, PolicyDeduced&& arg_policy) + : GraphNodeKernelImpl("", exec_space, std::move(arg_functor), + (PolicyDeduced &&) arg_policy) {} + + void set_sycl_graph_ptr( + sycl::ext::oneapi::experimental::command_graph< + sycl::ext::oneapi::experimental::graph_state::modifiable>* + arg_graph) { + m_graph_ptr = arg_graph; + } + + void set_sycl_graph_node_ptr( + std::optional* arg_node) { + m_graph_node_ptr = arg_node; + } + + std::optional& get_sycl_graph_node() + const { + return *m_graph_node_ptr; + } + + sycl::ext::oneapi::experimental::command_graph< + sycl::ext::oneapi::experimental::graph_state::modifiable>& + get_sycl_graph() const { + return *m_graph_ptr; + } + + private: + Kokkos::ObservingRawPtr> + m_graph_ptr = nullptr; + Kokkos::ObservingRawPtr> + m_graph_node_ptr = nullptr; +}; + +struct SYCLGraphNodeAggregateKernel { + using graph_kernel = SYCLGraphNodeAggregateKernel; + + // Aggregates don't need a policy, but for the purposes of checking the static + // assertions about graph kernels, + struct Policy { + using is_graph_kernel = std::true_type; + }; +}; + +template ::type> +struct get_graph_node_kernel_type + : type_identity> {}; + +template +struct get_graph_node_kernel_type + : type_identity, + Kokkos::ParallelReduceTag>> {}; + +template +auto& get_sycl_graph_from_kernel(KernelType const& kernel) { + using graph_node_kernel_t = + typename get_graph_node_kernel_type::type; + auto const& kernel_as_graph_kernel = + static_cast(kernel); + auto& graph = kernel_as_graph_kernel.get_sycl_graph(); + + return graph; +} + +template +auto& get_sycl_graph_node_from_kernel(KernelType const& kernel) { + using graph_node_kernel_t = + typename get_graph_node_kernel_type::type; + auto const& kernel_as_graph_kernel = + static_cast(kernel); + auto& graph_node = kernel_as_graph_kernel.get_sycl_graph_node(); + + return graph_node; +} + +template +void sycl_attach_kernel_to_node(Kernel& kernel, const Lambda& lambda) { + sycl::ext::oneapi::experimental::command_graph< + sycl::ext::oneapi::experimental::graph_state::modifiable>& graph = + Impl::get_sycl_graph_from_kernel(kernel); + std::optional& graph_node = + Impl::get_sycl_graph_node_from_kernel(kernel); + KOKKOS_ENSURES(!graph_node); + graph_node = graph.add(lambda); + KOKKOS_ENSURES(graph_node); + // FIXME_SYCL_GRAPH not yet implemented in the compiler + // KOKKOS_ENSURES(graph_node.get_type() == + // sycl::ext::oneapi::experimental::node_type::kernel) +} + +} // namespace Impl +} // namespace Kokkos + +#endif diff --git a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_GraphNode_Impl.hpp b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_GraphNode_Impl.hpp new file mode 100644 index 0000000000..6bbe6711a2 --- /dev/null +++ b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_GraphNode_Impl.hpp @@ -0,0 +1,56 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#ifndef KOKKOS_SYCL_GRAPHNODE_IMPL_HPP +#define KOKKOS_SYCL_GRAPHNODE_IMPL_HPP + +#include + +#include + +#include + +#include + +namespace Kokkos { +namespace Impl { +template <> +struct GraphNodeBackendSpecificDetails { + std::optional node; + + explicit GraphNodeBackendSpecificDetails() = default; + + explicit GraphNodeBackendSpecificDetails( + _graph_node_is_root_ctor_tag) noexcept {} +}; + +template +struct GraphNodeBackendDetailsBeforeTypeErasure { + protected: + GraphNodeBackendDetailsBeforeTypeErasure( + Kokkos::Experimental::SYCL const &, Kernel &, PredecessorRef const &, + GraphNodeBackendSpecificDetails &) noexcept {} + + GraphNodeBackendDetailsBeforeTypeErasure( + Kokkos::Experimental::SYCL const &, _graph_node_is_root_ctor_tag, + GraphNodeBackendSpecificDetails &) noexcept {} +}; + +} // namespace Impl +} // namespace Kokkos + +#endif diff --git a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_Graph_Impl.hpp b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_Graph_Impl.hpp new file mode 100644 index 0000000000..1dc4a9c997 --- /dev/null +++ b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_Graph_Impl.hpp @@ -0,0 +1,174 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#ifndef KOKKOS_SYCL_GRAPH_IMPL_HPP +#define KOKKOS_SYCL_GRAPH_IMPL_HPP + +#include + +#include + +#include +#include + +#include + +#include + +namespace Kokkos { +namespace Impl { +template <> +class GraphImpl { + public: + using node_details_t = + GraphNodeBackendSpecificDetails; + using root_node_impl_t = GraphNodeImpl; + using aggregate_kernel_impl_t = SYCLGraphNodeAggregateKernel; + using aggregate_node_impl_t = + GraphNodeImpl; + + // Not movable or copyable; it spends its whole life as a shared_ptr in the + // Graph object. + GraphImpl() = delete; + GraphImpl(GraphImpl const&) = delete; + GraphImpl(GraphImpl&&) = delete; + GraphImpl& operator=(GraphImpl const&) = delete; + GraphImpl& operator=(GraphImpl&&) = delete; + + ~GraphImpl(); + + explicit GraphImpl(Kokkos::Experimental::SYCL instance); + + void add_node(std::shared_ptr const& arg_node_ptr); + + template + void add_node(std::shared_ptr const& arg_node_ptr); + + template + void add_predecessor(NodeImplPtr arg_node_ptr, PredecessorRef arg_pred_ref); + + void submit(); + + Kokkos::Experimental::SYCL const& get_execution_space() const noexcept; + + auto create_root_node_ptr(); + + template + auto create_aggregate_ptr(PredecessorRefs&&...); + + private: + void instantiate_graph() { m_graph_exec = m_graph.finalize(); } + + Kokkos::Experimental::SYCL m_execution_space; + sycl::ext::oneapi::experimental::command_graph< + sycl::ext::oneapi::experimental::graph_state::modifiable> + m_graph; + std::optional> + m_graph_exec; +}; + +inline GraphImpl::~GraphImpl() { + m_execution_space.fence("Kokkos::GraphImpl::~GraphImpl: Graph Destruction"); +} + +inline GraphImpl::GraphImpl( + Kokkos::Experimental::SYCL instance) + : m_execution_space(std::move(instance)), + m_graph(m_execution_space.sycl_queue().get_context(), + m_execution_space.sycl_queue().get_device()) {} + +inline void GraphImpl::add_node( + std::shared_ptr const& arg_node_ptr) { + // add an empty node that needs to be set up before finalizing the graph + arg_node_ptr->node_details_t::node = m_graph.add(); +} + +// Requires NodeImplPtr is a shared_ptr to specialization of GraphNodeImpl +// Also requires that the kernel has the graph node tag in its policy +template +inline void GraphImpl::add_node( + std::shared_ptr const& arg_node_ptr) { + static_assert(NodeImpl::kernel_type::Policy::is_graph_kernel::value); + KOKKOS_EXPECTS(arg_node_ptr); + // The Kernel launch from the execute() method has been shimmed to insert + // the node into the graph + auto& kernel = arg_node_ptr->get_kernel(); + auto& node = static_cast(arg_node_ptr.get())->node; + KOKKOS_EXPECTS(!node); + kernel.set_sycl_graph_ptr(&m_graph); + kernel.set_sycl_graph_node_ptr(&node); + kernel.execute(); + KOKKOS_ENSURES(node); +} + +// Requires PredecessorRef is a specialization of GraphNodeRef that has +// already been added to this graph and NodeImpl is a specialization of +// GraphNodeImpl that has already been added to this graph. +template +inline void GraphImpl::add_predecessor( + NodeImplPtr arg_node_ptr, PredecessorRef arg_pred_ref) { + KOKKOS_EXPECTS(arg_node_ptr); + auto pred_ptr = GraphAccess::get_node_ptr(arg_pred_ref); + KOKKOS_EXPECTS(pred_ptr); + + auto& pred_node = pred_ptr->node_details_t::node; + KOKKOS_EXPECTS(pred_node); + + auto& node = arg_node_ptr->node_details_t::node; + KOKKOS_EXPECTS(node); + + m_graph.make_edge(*pred_node, *node); +} + +inline void GraphImpl::submit() { + if (!m_graph_exec) { + instantiate_graph(); + } + m_execution_space.sycl_queue().ext_oneapi_graph(*m_graph_exec); +} + +inline Kokkos::Experimental::SYCL const& +GraphImpl::get_execution_space() const noexcept { + return m_execution_space; +} + +inline auto GraphImpl::create_root_node_ptr() { + KOKKOS_EXPECTS(!m_graph_exec); + auto rv = std::make_shared(get_execution_space(), + _graph_node_is_root_ctor_tag{}); + rv->node_details_t::node = m_graph.add(); + return rv; +} + +template +inline auto GraphImpl::create_aggregate_ptr( + PredecessorRefs&&...) { + // The attachment to predecessors, which is all we really need, happens + // in the generic layer, which calls through to add_predecessor for + // each predecessor ref, so all we need to do here is create the (trivial) + // aggregate node. + return std::make_shared(m_execution_space, + _graph_node_kernel_ctor_tag{}, + aggregate_kernel_impl_t{}); +} +} // namespace Impl +} // namespace Kokkos + +#endif diff --git a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_Instance.cpp b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_Instance.cpp index 0e67adb578..5843dca812 100644 --- a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_Instance.cpp +++ b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_Instance.cpp @@ -166,26 +166,27 @@ int SYCLInternal::acquire_team_scratch_space() { return current_team_scratch; } -sycl::device_ptr SYCLInternal::resize_team_scratch_space( +Kokkos::Impl::sycl_device_ptr SYCLInternal::resize_team_scratch_space( int scratch_pool_id, std::int64_t bytes, bool force_shrink) { // Multiple ParallelFor/Reduce Teams can call this function at the same time // and invalidate the m_team_scratch_ptr. We use a pool to avoid any race // condition. - if (m_team_scratch_current_size[scratch_pool_id] == 0) { + auto mem_space = Kokkos::Experimental::SYCLDeviceUSMSpace(*m_queue); + if (m_team_scratch_current_size[scratch_pool_id] == 0 && bytes > 0) { m_team_scratch_current_size[scratch_pool_id] = bytes; - m_team_scratch_ptr[scratch_pool_id] = - Kokkos::kokkos_malloc( - "Kokkos::Experimental::SYCLDeviceUSMSpace::TeamScratchMemory", - m_team_scratch_current_size[scratch_pool_id]); + m_team_scratch_ptr[scratch_pool_id] = mem_space.allocate( + "Kokkos::Experimental::SYCL::InternalTeamScratchMemory", + m_team_scratch_current_size[scratch_pool_id]); } if ((bytes > m_team_scratch_current_size[scratch_pool_id]) || ((bytes < m_team_scratch_current_size[scratch_pool_id]) && (force_shrink))) { + mem_space.deallocate(m_team_scratch_ptr[scratch_pool_id], + m_team_scratch_current_size[scratch_pool_id]); m_team_scratch_current_size[scratch_pool_id] = bytes; - m_team_scratch_ptr[scratch_pool_id] = - Kokkos::kokkos_realloc( - m_team_scratch_ptr[scratch_pool_id], - m_team_scratch_current_size[scratch_pool_id]); + m_team_scratch_ptr[scratch_pool_id] = mem_space.allocate( + "Kokkos::Experimental::SYCL::InternalTeamScratchMemory", + m_team_scratch_current_size[scratch_pool_id]); } return m_team_scratch_ptr[scratch_pool_id]; } @@ -234,8 +235,8 @@ void SYCLInternal::finalize() { for (int i = 0; i < m_n_team_scratch; ++i) { if (m_team_scratch_current_size[i] > 0) { - Kokkos::kokkos_free( - m_team_scratch_ptr[i]); + device_mem_space.deallocate(m_team_scratch_ptr[i], + m_team_scratch_current_size[i]); m_team_scratch_current_size[i] = 0; m_team_scratch_ptr[i] = nullptr; } @@ -250,7 +251,8 @@ void SYCLInternal::finalize() { m_queue.reset(); } -sycl::device_ptr SYCLInternal::scratch_space(const std::size_t size) { +Kokkos::Impl::sycl_device_ptr SYCLInternal::scratch_space( + const std::size_t size) { if (verify_is_initialized("scratch_space") && m_scratchSpaceCount < scratch_count(size)) { auto mem_space = Kokkos::Experimental::SYCLDeviceUSMSpace(*m_queue); @@ -270,7 +272,8 @@ sycl::device_ptr SYCLInternal::scratch_space(const std::size_t size) { return m_scratchSpace; } -sycl::host_ptr SYCLInternal::scratch_host(const std::size_t size) { +Kokkos::Impl::sycl_host_ptr SYCLInternal::scratch_host( + const std::size_t size) { if (verify_is_initialized("scratch_unified") && m_scratchHostCount < scratch_count(size)) { auto mem_space = Kokkos::Experimental::SYCLHostUSMSpace(*m_queue); @@ -290,7 +293,8 @@ sycl::host_ptr SYCLInternal::scratch_host(const std::size_t size) { return m_scratchHost; } -sycl::device_ptr SYCLInternal::scratch_flags(const std::size_t size) { +Kokkos::Impl::sycl_device_ptr SYCLInternal::scratch_flags( + const std::size_t size) { if (verify_is_initialized("scratch_flags") && m_scratchFlagsCount < scratch_count(size)) { auto mem_space = Kokkos::Experimental::SYCLDeviceUSMSpace(*m_queue); diff --git a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_Instance.hpp b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_Instance.hpp index ab7e8ce71e..2d784ef8a5 100644 --- a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_Instance.hpp +++ b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_Instance.hpp @@ -43,13 +43,12 @@ class SYCLInternal { SYCLInternal& operator=(SYCLInternal&&) = delete; SYCLInternal(SYCLInternal&&) = delete; - sycl::device_ptr scratch_space(const std::size_t size); - sycl::device_ptr scratch_flags(const std::size_t size); - sycl::host_ptr scratch_host(const std::size_t size); + Kokkos::Impl::sycl_device_ptr scratch_space(const std::size_t size); + Kokkos::Impl::sycl_device_ptr scratch_flags(const std::size_t size); + Kokkos::Impl::sycl_host_ptr scratch_host(const std::size_t size); int acquire_team_scratch_space(); - sycl::device_ptr resize_team_scratch_space(int scratch_pool_id, - std::int64_t bytes, - bool force_shrink = false); + Kokkos::Impl::sycl_device_ptr resize_team_scratch_space( + int scratch_pool_id, std::int64_t bytes, bool force_shrink = false); void register_team_scratch_event(int scratch_pool_id, sycl::event event); uint32_t impl_get_instance_id() const; @@ -59,21 +58,22 @@ class SYCLInternal { uint32_t m_maxConcurrency = 0; uint64_t m_maxShmemPerBlock = 0; - std::size_t m_scratchSpaceCount = 0; - sycl::device_ptr m_scratchSpace = nullptr; - std::size_t m_scratchHostCount = 0; - sycl::host_ptr m_scratchHost = nullptr; - std::size_t m_scratchFlagsCount = 0; - sycl::device_ptr m_scratchFlags = nullptr; + std::size_t m_scratchSpaceCount = 0; + Kokkos::Impl::sycl_device_ptr m_scratchSpace = nullptr; + std::size_t m_scratchHostCount = 0; + Kokkos::Impl::sycl_host_ptr m_scratchHost = nullptr; + std::size_t m_scratchFlagsCount = 0; + Kokkos::Impl::sycl_device_ptr m_scratchFlags = nullptr; // mutex to access shared memory mutable std::mutex m_mutexScratchSpace; // Team Scratch Level 1 Space - static constexpr int m_n_team_scratch = 10; - mutable int64_t m_team_scratch_current_size[m_n_team_scratch] = {}; - mutable sycl::device_ptr m_team_scratch_ptr[m_n_team_scratch] = {}; - mutable int m_current_team_scratch = 0; - mutable sycl::event m_team_scratch_event[m_n_team_scratch] = {}; + static constexpr int m_n_team_scratch = 10; + mutable int64_t m_team_scratch_current_size[m_n_team_scratch] = {}; + mutable Kokkos::Impl::sycl_device_ptr + m_team_scratch_ptr[m_n_team_scratch] = {}; + mutable int m_current_team_scratch = 0; + mutable sycl::event m_team_scratch_event[m_n_team_scratch] = {}; mutable std::mutex m_team_scratch_mutex; uint32_t m_instance_id = Kokkos::Tools::Experimental::Impl::idForInstance< diff --git a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelFor_MDRange.hpp b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelFor_MDRange.hpp index 7fbf5420f8..cb7b1048da 100644 --- a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelFor_MDRange.hpp +++ b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelFor_MDRange.hpp @@ -120,7 +120,7 @@ class Kokkos::Impl::ParallelFor, desul::ensure_sycl_lock_arrays_on_device(q); - auto parallel_for_event = q.submit([&](sycl::handler& cgh) { + auto cgh_lambda = [&](sycl::handler& cgh) { const auto range = compute_ranges(); const sycl::range<3> global_range = range.get_global_range(); const sycl::range<3> local_range = range.get_local_range(); @@ -153,12 +153,22 @@ class Kokkos::Impl::ParallelFor, {global_x, global_y, global_z}, {local_x, local_y, local_z}) .exec_range(); }); - }); -#ifndef KOKKOS_IMPL_SYCL_USE_IN_ORDER_QUEUES - q.ext_oneapi_submit_barrier(std::vector{parallel_for_event}); -#endif + }; - return parallel_for_event; +#ifdef SYCL_EXT_ONEAPI_GRAPH + if constexpr (Policy::is_graph_kernel::value) { + sycl_attach_kernel_to_node(*this, cgh_lambda); + return {}; + } else +#endif + { + auto parallel_for_event = q.submit(cgh_lambda); + +#ifndef KOKKOS_IMPL_SYCL_USE_IN_ORDER_QUEUES + q.ext_oneapi_submit_barrier(std::vector{parallel_for_event}); +#endif + return parallel_for_event; + } } public: @@ -181,12 +191,6 @@ class Kokkos::Impl::ParallelFor, functor_wrapper.register_event(event); } - ParallelFor(const ParallelFor&) = delete; - ParallelFor(ParallelFor&&) = delete; - ParallelFor& operator=(const ParallelFor&) = delete; - ParallelFor& operator=(ParallelFor&&) = delete; - ~ParallelFor() = default; - ParallelFor(const FunctorType& arg_functor, const Policy& arg_policy) : m_functor(arg_functor), m_policy(arg_policy), diff --git a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelFor_Range.hpp b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelFor_Range.hpp index b4de7eb89f..8ef43d392c 100644 --- a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelFor_Range.hpp +++ b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelFor_Range.hpp @@ -17,11 +17,15 @@ #ifndef KOKKOS_SYCL_PARALLEL_FOR_RANGE_HPP_ #define KOKKOS_SYCL_PARALLEL_FOR_RANGE_HPP_ +#ifdef SYCL_EXT_ONEAPI_AUTO_LOCAL_RANGE +#include +#endif #ifndef KOKKOS_IMPL_SYCL_USE_IN_ORDER_QUEUES #include #endif namespace Kokkos::Impl { +#ifndef SYCL_EXT_ONEAPI_AUTO_LOCAL_RANGE template struct FunctorWrapperRangePolicyParallelFor { using WorkTag = typename Policy::work_tag; @@ -37,14 +41,15 @@ struct FunctorWrapperRangePolicyParallelFor { typename Policy::index_type m_begin; FunctorWrapper m_functor_wrapper; }; +#endif // Same as above but for a user-provided workgroup size template struct FunctorWrapperRangePolicyParallelForCustom { using WorkTag = typename Policy::work_tag; - void operator()(sycl::item<1> item) const { - const typename Policy::index_type id = item.get_linear_id(); + void operator()(sycl::nd_item<1> item) const { + const typename Policy::index_type id = item.get_global_linear_id(); if (id < m_work_size) { const auto shifted_id = id + m_begin; if constexpr (std::is_void_v) @@ -74,27 +79,47 @@ class Kokkos::Impl::ParallelFor, const Policy m_policy; template - static sycl::event sycl_direct_launch(const Policy& policy, - const Functor& functor, - const sycl::event& memcpy_event) { + sycl::event sycl_direct_launch(const Policy& policy, const Functor& functor, + const sycl::event& memcpy_event) const { // Convenience references const Kokkos::Experimental::SYCL& space = policy.space(); sycl::queue& q = space.sycl_queue(); desul::ensure_sycl_lock_arrays_on_device(q); - auto parallel_for_event = q.submit([&](sycl::handler& cgh) { + auto cgh_lambda = [&](sycl::handler& cgh) { #ifndef KOKKOS_IMPL_SYCL_USE_IN_ORDER_QUEUES cgh.depends_on(memcpy_event); #else (void)memcpy_event; #endif + if (policy.chunk_size() <= 1) { +#ifdef SYCL_EXT_ONEAPI_AUTO_LOCAL_RANGE + const auto actual_range = policy.end() - policy.begin(); + FunctorWrapperRangePolicyParallelForCustom f{ + policy.begin(), functor, actual_range}; + // Round the actual range up to the closest power of two not exceeding + // the maximum workgroup size + const auto max_wgroup_size = + q.get_device().get_info(); + const auto wgroup_size_multiple = Kokkos::bit_floor( + std::min(max_wgroup_size, actual_range)); + + const auto launch_range = (actual_range + wgroup_size_multiple - 1) / + wgroup_size_multiple * wgroup_size_multiple; + sycl::nd_range<1> range( + launch_range, sycl::ext::oneapi::experimental::auto_range<1>()); + cgh.parallel_for< + FunctorWrapperRangePolicyParallelForCustom>(range, + f); +#else FunctorWrapperRangePolicyParallelFor f{policy.begin(), functor}; sycl::range<1> range(policy.end() - policy.begin()); cgh.parallel_for>( range, f); +#endif } else { // Use the chunk size as workgroup size. We need to make sure that the // range the kernel is launched with is a multiple of the workgroup @@ -111,12 +136,22 @@ class Kokkos::Impl::ParallelFor, FunctorWrapperRangePolicyParallelForCustom>(range, f); } - }); -#ifndef KOKKOS_IMPL_SYCL_USE_IN_ORDER_QUEUES - q.ext_oneapi_submit_barrier(std::vector{parallel_for_event}); -#endif + }; - return parallel_for_event; +#ifdef SYCL_EXT_ONEAPI_GRAPH + if constexpr (Policy::is_graph_kernel::value) { + sycl_attach_kernel_to_node(*this, cgh_lambda); + return {}; + } else +#endif + { + auto parallel_for_event = q.submit(cgh_lambda); + +#ifndef KOKKOS_IMPL_SYCL_USE_IN_ORDER_QUEUES + q.ext_oneapi_submit_barrier(std::vector{parallel_for_event}); +#endif + return parallel_for_event; + } } public: @@ -137,12 +172,6 @@ class Kokkos::Impl::ParallelFor, functor_wrapper.register_event(event); } - ParallelFor(const ParallelFor&) = delete; - ParallelFor(ParallelFor&&) = delete; - ParallelFor& operator=(const ParallelFor&) = delete; - ParallelFor& operator=(ParallelFor&&) = delete; - ~ParallelFor() = default; - ParallelFor(const FunctorType& arg_functor, const Policy& arg_policy) : m_functor(arg_functor), m_policy(arg_policy) {} }; diff --git a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelFor_Team.hpp b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelFor_Team.hpp index ecb4a863da..cf7f582bc7 100644 --- a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelFor_Team.hpp +++ b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelFor_Team.hpp @@ -22,13 +22,14 @@ #include #include +#include #include template class Kokkos::Impl::ParallelFor, Kokkos::Experimental::SYCL> { public: - using Policy = TeamPolicyInternal; + using Policy = TeamPolicy; using functor_type = FunctorType; using size_type = ::Kokkos::Experimental::SYCL::size_type; @@ -44,24 +45,19 @@ class Kokkos::Impl::ParallelFor, size_type const m_vector_size; int m_shmem_begin; int m_shmem_size; - sycl::device_ptr m_global_scratch_ptr; size_t m_scratch_size[2]; - // Only let one ParallelFor instance at a time use the team scratch memory. - // The constructor acquires the mutex which is released in the destructor. - std::scoped_lock m_scratch_buffers_lock; - int m_scratch_pool_id = -1; template - sycl::event sycl_direct_launch(const Policy& policy, + sycl::event sycl_direct_launch(const sycl_device_ptr global_scratch_ptr, const FunctorWrapper& functor_wrapper, const sycl::event& memcpy_event) const { // Convenience references - const Kokkos::Experimental::SYCL& space = policy.space(); + const Kokkos::Experimental::SYCL& space = m_policy.space(); sycl::queue& q = space.sycl_queue(); desul::ensure_sycl_lock_arrays_on_device(q); - auto parallel_for_event = q.submit([&](sycl::handler& cgh) { + auto cgh_lambda = [&](sycl::handler& cgh) { // FIXME_SYCL accessors seem to need a size greater than zero at least for // host queues sycl::local_accessor team_scratch_memory_L0( @@ -72,7 +68,6 @@ class Kokkos::Impl::ParallelFor, // Avoid capturing *this since it might not be trivially copyable const auto shmem_begin = m_shmem_begin; const size_t scratch_size[2] = {m_scratch_size[0], m_scratch_size[1]}; - sycl::device_ptr const global_scratch_ptr = m_global_scratch_ptr; auto lambda = [=](sycl::nd_item<2> item) { const member_type team_member( @@ -114,28 +109,53 @@ class Kokkos::Impl::ParallelFor, sycl::range<2>(m_team_size, m_league_size * final_vector_size), sycl::range<2>(m_team_size, final_vector_size)), lambda); - }); -#ifndef KOKKOS_IMPL_SYCL_USE_IN_ORDER_QUEUES - q.ext_oneapi_submit_barrier(std::vector{parallel_for_event}); + }; + +#ifdef SYCL_EXT_ONEAPI_GRAPH + if constexpr (Policy::is_graph_kernel::value) { + sycl_attach_kernel_to_node(*this, cgh_lambda); + return {}; + } else #endif - return parallel_for_event; + { + auto parallel_for_event = q.submit(cgh_lambda); + +#ifndef KOKKOS_IMPL_SYCL_USE_IN_ORDER_QUEUES + q.ext_oneapi_submit_barrier(std::vector{parallel_for_event}); +#endif + return parallel_for_event; + } } public: inline void execute() const { if (m_league_size == 0) return; - auto& space = *m_policy.space().impl_internal_space_instance(); + auto& instance = *m_policy.space().impl_internal_space_instance(); + + // Only let one instance at a time resize the instance's scratch memory + // allocations. + std::scoped_lock team_scratch_lock( + instance.m_team_scratch_mutex); + + // Functor's reduce memory, team scan memory, and team shared memory depend + // upon team size. + int scratch_pool_id = instance.acquire_team_scratch_space(); + const sycl_device_ptr global_scratch_ptr = + static_cast>(instance.resize_team_scratch_space( + scratch_pool_id, + static_cast(m_scratch_size[1]) * m_league_size)); + Kokkos::Experimental::Impl::SYCLInternal::IndirectKernelMem& - indirectKernelMem = space.get_indirect_kernel_mem(); + indirectKernelMem = instance.get_indirect_kernel_mem(); auto functor_wrapper = Experimental::Impl::make_sycl_function_wrapper( m_functor, indirectKernelMem); - sycl::event event = sycl_direct_launch(m_policy, functor_wrapper, + sycl::event event = sycl_direct_launch(global_scratch_ptr, functor_wrapper, functor_wrapper.get_copy_event()); functor_wrapper.register_event(event); - space.register_team_scratch_event(m_scratch_pool_id, event); + instance.register_team_scratch_event(scratch_pool_id, event); } ParallelFor(FunctorType const& arg_functor, Policy const& arg_policy) @@ -143,10 +163,7 @@ class Kokkos::Impl::ParallelFor, m_policy(arg_policy), m_league_size(arg_policy.league_size()), m_team_size(arg_policy.team_size()), - m_vector_size(arg_policy.impl_vector_length()), - m_scratch_buffers_lock(arg_policy.space() - .impl_internal_space_instance() - ->m_team_scratch_mutex) { + m_vector_size(arg_policy.impl_vector_length()) { // FIXME_SYCL optimize if (m_team_size < 0) m_team_size = @@ -159,22 +176,14 @@ class Kokkos::Impl::ParallelFor, m_scratch_size[0] = m_shmem_size; m_scratch_size[1] = m_policy.scratch_size(1, m_team_size); - // Functor's reduce memory, team scan memory, and team shared memory depend - // upon team size. - auto& space = *m_policy.space().impl_internal_space_instance(); - m_scratch_pool_id = space.acquire_team_scratch_space(); - m_global_scratch_ptr = - static_cast>(space.resize_team_scratch_space( - m_scratch_pool_id, - static_cast(m_scratch_size[1]) * m_league_size)); - - if (static_cast(space.m_maxShmemPerBlock) < + const auto& instance = *m_policy.space().impl_internal_space_instance(); + if (static_cast(instance.m_maxShmemPerBlock) < m_shmem_size - m_shmem_begin) { std::stringstream out; out << "Kokkos::Impl::ParallelFor insufficient shared memory! " "Requested " << m_shmem_size - m_shmem_begin << " bytes but maximum is " - << space.m_maxShmemPerBlock << '\n'; + << instance.m_maxShmemPerBlock << '\n'; Kokkos::Impl::throw_runtime_exception(out.str()); } diff --git a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelReduce_MDRange.hpp b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelReduce_MDRange.hpp index f55280e22e..0774b24bca 100644 --- a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelReduce_MDRange.hpp +++ b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelReduce_MDRange.hpp @@ -77,9 +77,7 @@ class Kokkos::Impl::ParallelReduce::accessible), - m_scratch_buffers_lock( - m_space.impl_internal_space_instance()->m_mutexScratchSpace) {} + typename View::memory_space>::accessible) {} private: template @@ -94,10 +92,10 @@ class Kokkos::Impl::ParallelReduce results_ptr; + sycl_device_ptr results_ptr; auto host_result_ptr = (m_result_ptr && !m_result_ptr_device_accessible) - ? static_cast>( + ? static_cast>( instance.scratch_host(sizeof(value_type) * value_count)) : nullptr; @@ -108,13 +106,13 @@ class Kokkos::Impl::ParallelReduce>( + results_ptr = static_cast>( instance.scratch_space(sizeof(value_type) * value_count)); auto device_accessible_result_ptr = m_result_ptr_device_accessible @@ -129,12 +127,20 @@ class Kokkos::Impl::ParallelReduce{parallel_reduce_event}); + }; + +#ifdef SYCL_EXT_ONEAPI_GRAPH + if constexpr (Policy::is_graph_kernel::value) { + sycl_attach_kernel_to_node(*this, cgh_lambda); + } else #endif - last_reduction_event = parallel_reduce_event; + { + last_reduction_event = q.submit(cgh_lambda); +#ifndef KOKKOS_IMPL_SYCL_USE_IN_ORDER_QUEUES + q.ext_oneapi_submit_barrier( + std::vector{last_reduction_event}); +#endif + } } else { // Otherwise (when n_tiles is not zero), we perform a reduction on the // values in all workgroups separately, write the workgroup results back @@ -155,16 +161,16 @@ class Kokkos::Impl::ParallelReduce>( + results_ptr = static_cast>( instance.scratch_space(sizeof(value_type) * value_count * n_wgroups)); auto device_accessible_result_ptr = m_result_ptr_device_accessible ? static_cast>(m_result_ptr) : static_cast>(host_result_ptr); - auto scratch_flags = static_cast>( + auto scratch_flags = static_cast>( instance.scratch_flags(sizeof(unsigned int))); - auto parallel_reduce_event = q.submit([&](sycl::handler& cgh) { + auto cgh_lambda = [&](sycl::handler& cgh) { sycl::local_accessor local_mem( sycl::range<1>(wgroup_size) * value_count, cgh); sycl::local_accessor num_teams_done(1, cgh); @@ -298,12 +304,19 @@ class Kokkos::Impl::ParallelReduce{parallel_reduce_event}); + }; +#ifdef SYCL_EXT_ONEAPI_GRAPH + if constexpr (Policy::is_graph_kernel::value) { + sycl_attach_kernel_to_node(*this, cgh_lambda); + } else #endif - last_reduction_event = parallel_reduce_event; + { + last_reduction_event = q.submit(cgh_lambda); +#ifndef KOKKOS_IMPL_SYCL_USE_IN_ORDER_QUEUES + q.ext_oneapi_submit_barrier( + std::vector{last_reduction_event}); +#endif + } } // At this point, the reduced value is written to the entry in results_ptr @@ -311,6 +324,11 @@ class Kokkos::Impl::ParallelReduce::execute: result " "not device-accessible"); @@ -330,6 +348,12 @@ class Kokkos::Impl::ParallelReduce scratch_buffers_lock( + instance.m_mutexScratchSpace); + using IndirectKernelMem = Kokkos::Experimental::Impl::SYCLInternal::IndirectKernelMem; IndirectKernelMem& indirectKernelMem = instance.get_indirect_kernel_mem(); @@ -349,10 +373,6 @@ class Kokkos::Impl::ParallelReduce m_scratch_buffers_lock; }; #endif /* KOKKOS_SYCL_PARALLEL_REDUCE_MDRANGE_HPP */ diff --git a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelReduce_Range.hpp b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelReduce_Range.hpp index 5333e3c8a8..2d46ffc77d 100644 --- a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelReduce_Range.hpp +++ b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelReduce_Range.hpp @@ -50,9 +50,7 @@ class Kokkos::Impl::ParallelReduce::accessible), - m_scratch_buffers_lock( - p.space().impl_internal_space_instance()->m_mutexScratchSpace) {} + typename View::memory_space>::accessible) {} private: template @@ -69,10 +67,10 @@ class Kokkos::Impl::ParallelReduce results_ptr = nullptr; + sycl_device_ptr results_ptr = nullptr; auto host_result_ptr = (m_result_ptr && !m_result_ptr_device_accessible) - ? static_cast>( + ? static_cast>( instance.scratch_host(sizeof(value_type) * value_count)) : nullptr; auto device_accessible_result_ptr = @@ -88,10 +86,10 @@ class Kokkos::Impl::ParallelReduce>( + results_ptr = static_cast>( instance.scratch_space(sizeof(value_type) * value_count)); - auto parallel_reduce_event = q.submit([&](sycl::handler& cgh) { + auto cgh_lambda = [&](sycl::handler& cgh) { const auto begin = policy.begin(); #ifndef KOKKOS_IMPL_SYCL_USE_IN_ORDER_QUEUES cgh.depends_on(memcpy_event); @@ -114,24 +112,32 @@ class Kokkos::Impl::ParallelReduce{parallel_reduce_event}); + }; + +#ifdef SYCL_EXT_ONEAPI_GRAPH + if constexpr (Policy::is_graph_kernel::value) { + sycl_attach_kernel_to_node(*this, cgh_lambda); + } else #endif - last_reduction_event = parallel_reduce_event; + { + last_reduction_event = q.submit(cgh_lambda); +#ifndef KOKKOS_IMPL_SYCL_USE_IN_ORDER_QUEUES + q.ext_oneapi_submit_barrier( + std::vector{last_reduction_event}); +#endif + } } else { // Otherwise (when size > 1), we perform a reduction on the values in all // workgroups separately, write the workgroup results back to global // memory and recurse until only one workgroup does the reduction and thus // gets the final value. - auto scratch_flags = static_cast>( + auto scratch_flags = static_cast>( instance.scratch_flags(sizeof(unsigned int))); auto reduction_lambda_factory = [&](sycl::local_accessor local_mem, sycl::local_accessor num_teams_done, - sycl::device_ptr results_ptr, int values_per_thread) { + sycl_device_ptr results_ptr, int values_per_thread) { const auto begin = policy.begin(); auto lambda = [=](sycl::nd_item<1> item) { @@ -241,7 +247,7 @@ class Kokkos::Impl::ParallelReduce num_teams_done(1, cgh); auto dummy_reduction_lambda = @@ -302,7 +308,7 @@ class Kokkos::Impl::ParallelReduce>(instance.scratch_space( + static_cast>(instance.scratch_space( sizeof(value_type) * value_count * n_wgroups)); sycl::local_accessor local_mem( @@ -320,12 +326,20 @@ class Kokkos::Impl::ParallelReduce(n_wgroups * wgroup_size, wgroup_size), reduction_lambda); - }); -#ifndef KOKKOS_IMPL_SYCL_USE_IN_ORDER_QUEUES - q.ext_oneapi_submit_barrier( - std::vector{parallel_reduce_event}); + }; + +#ifdef SYCL_EXT_ONEAPI_GRAPH + if constexpr (Policy::is_graph_kernel::value) { + sycl_attach_kernel_to_node(*this, cgh_lambda); + } else #endif - last_reduction_event = parallel_reduce_event; + { + last_reduction_event = q.submit(cgh_lambda); +#ifndef KOKKOS_IMPL_SYCL_USE_IN_ORDER_QUEUES + q.ext_oneapi_submit_barrier( + std::vector{last_reduction_event}); +#endif + } } // At this point, the reduced value is written to the entry in results_ptr @@ -333,6 +347,11 @@ class Kokkos::Impl::ParallelReduce::execute: result " "not device-accessible"); @@ -347,6 +366,12 @@ class Kokkos::Impl::ParallelReduce scratch_buffers_lock( + instance.m_mutexScratchSpace); + using IndirectKernelMem = Kokkos::Experimental::Impl::SYCLInternal::IndirectKernelMem; IndirectKernelMem& indirectKernelMem = instance.get_indirect_kernel_mem(); @@ -366,10 +391,6 @@ class Kokkos::Impl::ParallelReduce m_scratch_buffers_lock; }; #endif /* KOKKOS_SYCL_PARALLEL_REDUCE_RANGE_HPP */ diff --git a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelReduce_Team.hpp b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelReduce_Team.hpp index 27165c59e3..b443bcbf90 100644 --- a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelReduce_Team.hpp +++ b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelReduce_Team.hpp @@ -23,6 +23,7 @@ #include #include +#include #include template @@ -30,7 +31,7 @@ class Kokkos::Impl::ParallelReduce, Kokkos::Experimental::SYCL> { public: - using Policy = TeamPolicyInternal; + using Policy = TeamPolicy; using FunctorType = typename CombinedFunctorReducerType::functor_type; using ReducerType = typename CombinedFunctorReducerType::reducer_type; @@ -54,24 +55,18 @@ class Kokkos::Impl::ParallelReduce m_global_scratch_ptr; size_t m_scratch_size[2]; const size_type m_league_size; int m_team_size; const size_type m_vector_size; - // Only let one ParallelReduce instance at a time use the team scratch memory - // and the host scratch memory. The constructor acquires the mutex which is - // released in the destructor. - std::scoped_lock m_scratch_buffers_lock; - int m_scratch_pool_id = -1; - template + template sycl::event sycl_direct_launch( - const PolicyType& policy, + const sycl_device_ptr global_scratch_ptr, const CombinedFunctorReducerWrapper& functor_reducer_wrapper, const sycl::event& memcpy_event) const { // Convenience references - const Kokkos::Experimental::SYCL& space = policy.space(); + const Kokkos::Experimental::SYCL& space = m_policy.space(); Kokkos::Experimental::Impl::SYCLInternal& instance = *space.impl_internal_space_instance(); sycl::queue& q = space.sycl_queue(); @@ -82,7 +77,7 @@ class Kokkos::Impl::ParallelReduce>( + ? static_cast>( instance.scratch_host(sizeof(value_type) * value_count)) : nullptr; @@ -95,14 +90,14 @@ class Kokkos::Impl::ParallelReduce>(instance.scratch_space( + static_cast>(instance.scratch_space( sizeof(value_type) * std::max(value_count, 1u))); auto device_accessible_result_ptr = m_result_ptr_device_accessible ? static_cast>(m_result_ptr) : static_cast>(host_result_ptr); - auto parallel_reduce_event = q.submit([&](sycl::handler& cgh) { + auto cgh_lambda = [&](sycl::handler& cgh) { // FIXME_SYCL accessors seem to need a size greater than zero at least // for host queues sycl::local_accessor team_scratch_memory_L0( @@ -113,7 +108,6 @@ class Kokkos::Impl::ParallelReduce const global_scratch_ptr = m_global_scratch_ptr; #ifndef KOKKOS_IMPL_SYCL_USE_IN_ORDER_QUEUES cgh.depends_on(memcpy_event); @@ -144,19 +138,26 @@ class Kokkos::Impl::ParallelReduce{parallel_reduce_event}); + }; +#ifdef SYCL_EXT_ONEAPI_GRAPH + if constexpr (Policy::is_graph_kernel::value) { + sycl_attach_kernel_to_node(*this, cgh_lambda); + } else #endif - last_reduction_event = parallel_reduce_event; + { + last_reduction_event = q.submit(cgh_lambda); +#ifndef KOKKOS_IMPL_SYCL_USE_IN_ORDER_QUEUES + q.ext_oneapi_submit_barrier( + std::vector{last_reduction_event}); +#endif + } } else { // Otherwise, (if the total range has more than one element) we perform a // reduction on the values in all workgroups separately, write the // workgroup results back to global memory and recurse until only one // workgroup does the reduction and thus gets the final value. - auto parallel_reduce_event = q.submit([&](sycl::handler& cgh) { - auto scratch_flags = static_cast>( + auto cgh_lambda = [&](sycl::handler& cgh) { + auto scratch_flags = static_cast>( instance.scratch_flags(sizeof(unsigned int))); // FIXME_SYCL accessors seem to need a size greater than zero at least @@ -170,12 +171,11 @@ class Kokkos::Impl::ParallelReduce const global_scratch_ptr = m_global_scratch_ptr; sycl::local_accessor num_teams_done(1, cgh); auto team_reduction_factory = [&](sycl::local_accessor local_mem, - sycl::device_ptr results_ptr) { + sycl_device_ptr results_ptr) { auto device_accessible_result_ptr = m_result_ptr_device_accessible ? static_cast>(m_result_ptr) @@ -331,7 +331,7 @@ class Kokkos::Impl::ParallelReduce((size + wgroup_size - 1) / wgroup_size, 1); results_ptr = - static_cast>(instance.scratch_space( + static_cast>(instance.scratch_space( sizeof(value_type) * std::max(value_count, 1u) * init_size)); size_t max_work_groups = @@ -359,12 +359,19 @@ class Kokkos::Impl::ParallelReduce(m_team_size, n_wgroups * m_vector_size), sycl::range<2>(m_team_size, m_vector_size)), reduction_lambda); - }); -#ifndef KOKKOS_IMPL_SYCL_USE_IN_ORDER_QUEUES - q.ext_oneapi_submit_barrier( - std::vector{parallel_reduce_event}); + }; +#ifdef SYCL_EXT_ONEAPI_GRAPH + if constexpr (Policy::is_graph_kernel::value) { + sycl_attach_kernel_to_node(*this, cgh_lambda); + } else #endif - last_reduction_event = parallel_reduce_event; + { + last_reduction_event = q.submit(cgh_lambda); +#ifndef KOKKOS_IMPL_SYCL_USE_IN_ORDER_QUEUES + q.ext_oneapi_submit_barrier( + std::vector{last_reduction_event}); +#endif + } } // At this point, the reduced value is written to the entry in results_ptr @@ -372,6 +379,11 @@ class Kokkos::Impl::ParallelReduce::execute: result not " "device-accessible"); @@ -386,6 +398,22 @@ class Kokkos::Impl::ParallelReduce scratch_buffers_lock( + instance.m_mutexScratchSpace); + std::scoped_lock team_scratch_lock( + instance.m_team_scratch_mutex); + + // Functor's reduce memory, team scan memory, and team shared memory depend + // upon team size. + int scratch_pool_id = instance.acquire_team_scratch_space(); + const sycl_device_ptr global_scratch_ptr = + static_cast>(instance.resize_team_scratch_space( + scratch_pool_id, + static_cast(m_scratch_size[1]) * m_league_size)); + using IndirectKernelMem = Kokkos::Experimental::Impl::SYCLInternal::IndirectKernelMem; IndirectKernelMem& indirectKernelMem = instance.get_indirect_kernel_mem(); @@ -395,14 +423,24 @@ class Kokkos::Impl::ParallelReduce + ParallelReduce(CombinedFunctorReducerType const& arg_functor_reducer, + Policy const& arg_policy, ViewType const& arg_result) + : m_functor_reducer(arg_functor_reducer), + m_policy(arg_policy), + m_result_ptr(arg_result.data()), + m_result_ptr_device_accessible( + MemorySpaceAccess::accessible), + m_league_size(arg_policy.league_size()), + m_team_size(arg_policy.team_size()), + m_vector_size(arg_policy.impl_vector_length()) { // FIXME_SYCL optimize if (m_team_size < 0) m_team_size = m_policy.team_size_recommended( @@ -423,22 +461,15 @@ class Kokkos::Impl::ParallelReduce>(space.resize_team_scratch_space( - m_scratch_pool_id, - static_cast(m_scratch_size[1]) * m_league_size)); - - if (static_cast(space.m_maxShmemPerBlock) < + const Kokkos::Experimental::Impl::SYCLInternal& instance = + *m_policy.space().impl_internal_space_instance(); + if (static_cast(instance.m_maxShmemPerBlock) < m_shmem_size - m_shmem_begin) { std::stringstream out; out << "Kokkos::Impl::ParallelFor insufficient shared memory! " "Requested " << m_shmem_size - m_shmem_begin << " bytes but maximum is " - << space.m_maxShmemPerBlock << '\n'; + << instance.m_maxShmemPerBlock << '\n'; Kokkos::Impl::throw_runtime_exception(out.str()); } @@ -448,25 +479,6 @@ class Kokkos::Impl::ParallelReduce requested too large team size."); } - - public: - template - ParallelReduce(CombinedFunctorReducerType const& arg_functor_reducer, - Policy const& arg_policy, ViewType const& arg_result) - : m_functor_reducer(arg_functor_reducer), - m_policy(arg_policy), - m_result_ptr(arg_result.data()), - m_result_ptr_device_accessible( - MemorySpaceAccess::accessible), - m_league_size(arg_policy.league_size()), - m_team_size(arg_policy.team_size()), - m_vector_size(arg_policy.impl_vector_length()), - m_scratch_buffers_lock(arg_policy.space() - .impl_internal_space_instance() - ->m_team_scratch_mutex) { - initialize(); - } }; #endif diff --git a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelScan_Range.hpp b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelScan_Range.hpp index 977b69bc9e..bdb5b88377 100644 --- a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelScan_Range.hpp +++ b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_ParallelScan_Range.hpp @@ -18,6 +18,7 @@ #define KOKKOS_SYCL_PARALLEL_SCAN_RANGE_HPP #include +#include #include #include @@ -35,20 +36,38 @@ void workgroup_scan(sycl::nd_item item, const FunctorType& final_reducer, auto sg = item.get_sub_group(); const int sg_group_id = sg.get_group_id()[0]; const int id_in_sg = sg.get_local_id()[0]; + const int local_range = std::min(sg.get_local_range()[0], global_range); - for (int stride = 1; stride < global_range; stride <<= 1) { - auto tmp = sg.shuffle_up(local_value, stride); +#if defined(KOKKOS_ARCH_INTEL_GPU) || defined(KOKKOS_IMPL_ARCH_NVIDIA_GPU) + auto shuffle_combine = [&](int stride) { + if (stride < local_range) { + auto tmp = Kokkos::Impl::SYCLReduction::shift_group_right(sg, local_value, + stride); + if (id_in_sg >= stride) final_reducer.join(&local_value, &tmp); + } + }; + shuffle_combine(1); + shuffle_combine(2); + shuffle_combine(4); + shuffle_combine(8); + shuffle_combine(16); + KOKKOS_ASSERT(local_range <= 32); +#else + for (int stride = 1; stride < local_range; stride <<= 1) { + auto tmp = + Kokkos::Impl::SYCLReduction::shift_group_right(sg, local_value, stride); if (id_in_sg >= stride) final_reducer.join(&local_value, &tmp); } +#endif const int max_subgroup_size = sg.get_max_local_range()[0]; const int n_active_subgroups = (global_range + max_subgroup_size - 1) / max_subgroup_size; - const int local_range = sg.get_local_range()[0]; if (id_in_sg == local_range - 1 && sg_group_id < n_active_subgroups) local_mem[sg_group_id] = local_value; - local_value = sg.shuffle_up(local_value, 1); + local_value = + Kokkos::Impl::SYCLReduction::shift_group_right(sg, local_value, 1); if (id_in_sg == 0) final_reducer.init(&local_value); sycl::group_barrier(item.get_group()); @@ -61,8 +80,29 @@ void workgroup_scan(sycl::nd_item item, const FunctorType& final_reducer, const auto upper_bound = std::min(local_range, n_active_subgroups - round * local_range); auto local_sg_value = local_mem[idx < n_active_subgroups ? idx : 0]; +#if defined(KOKKOS_ARCH_INTEL_GPU) || defined(KOKKOS_IMPL_ARCH_NVIDIA_GPU) + auto shuffle_combine_sg = [&](int stride) { + if (stride < upper_bound) { + auto tmp = Kokkos::Impl::SYCLReduction::shift_group_right( + sg, local_sg_value, stride); + if (id_in_sg >= stride) { + if (idx < n_active_subgroups) + final_reducer.join(&local_sg_value, &tmp); + else + local_sg_value = tmp; + } + } + }; + shuffle_combine_sg(1); + shuffle_combine_sg(2); + shuffle_combine_sg(4); + shuffle_combine_sg(8); + shuffle_combine_sg(16); + KOKKOS_ASSERT(upper_bound <= 32); +#else for (int stride = 1; stride < upper_bound; stride <<= 1) { - auto tmp = sg.shuffle_up(local_sg_value, stride); + auto tmp = Kokkos::Impl::SYCLReduction::shift_group_right( + sg, local_sg_value, stride); if (id_in_sg >= stride) { if (idx < n_active_subgroups) final_reducer.join(&local_sg_value, &tmp); @@ -70,6 +110,7 @@ void workgroup_scan(sycl::nd_item item, const FunctorType& final_reducer, local_sg_value = tmp; } } +#endif if (idx < n_active_subgroups) { local_mem[idx] = local_sg_value; if (round > 0) @@ -111,14 +152,10 @@ class ParallelScanSYCLBase { const CombinedFunctorReducer m_functor_reducer; const Policy m_policy; - sycl::host_ptr m_scratch_host = nullptr; + sycl_host_ptr m_scratch_host = nullptr; pointer_type m_result_ptr; const bool m_result_ptr_device_accessible; - // Only let one ParallelScan instance at a time use the host scratch memory. - // The constructor acquires the mutex which is released in the destructor. - std::scoped_lock m_scratch_buffers_lock; - private: template sycl::event sycl_direct_launch(const FunctorWrapper& functor_wrapper, @@ -131,95 +168,93 @@ class ParallelScanSYCLBase { const auto size = m_policy.end() - m_policy.begin(); - auto scratch_flags = static_cast>( + auto scratch_flags = static_cast>( instance.scratch_flags(sizeof(unsigned int))); const auto begin = m_policy.begin(); // Initialize global memory - auto scan_lambda_factory = - [&](sycl::local_accessor local_mem, - sycl::local_accessor num_teams_done, - sycl::device_ptr global_mem_, - sycl::device_ptr group_results_) { - auto lambda = [=](sycl::nd_item<1> item) { - auto global_mem = global_mem_; - auto group_results = group_results_; + auto scan_lambda_factory = [&](sycl::local_accessor local_mem, + sycl::local_accessor + num_teams_done, + sycl_device_ptr global_mem_, + sycl_device_ptr group_results_) { + auto lambda = [=](sycl::nd_item<1> item) { + auto global_mem = global_mem_; + auto group_results = group_results_; - const CombinedFunctorReducer< - FunctorType, typename Analysis::Reducer>& functor_reducer = - functor_wrapper.get_functor(); - const FunctorType& functor = functor_reducer.get_functor(); - const typename Analysis::Reducer& reducer = - functor_reducer.get_reducer(); + const CombinedFunctorReducer& + functor_reducer = functor_wrapper.get_functor(); + const FunctorType& functor = functor_reducer.get_functor(); + const typename Analysis::Reducer& reducer = + functor_reducer.get_reducer(); - const auto n_wgroups = item.get_group_range()[0]; - const int wgroup_size = item.get_local_range()[0]; + const auto n_wgroups = item.get_group_range()[0]; + const int wgroup_size = item.get_local_range()[0]; - const int local_id = item.get_local_linear_id(); - const index_type global_id = item.get_global_linear_id(); + const int local_id = item.get_local_linear_id(); + const index_type global_id = item.get_global_linear_id(); - // Initialize local memory - value_type local_value; - reducer.init(&local_value); - if (global_id < size) { - if constexpr (std::is_void::value) - functor(global_id + begin, local_value, false); - else - functor(WorkTag(), global_id + begin, local_value, false); + // Initialize local memory + value_type local_value; + reducer.init(&local_value); + if (global_id < size) { + if constexpr (std::is_void::value) + functor(global_id + begin, local_value, false); + else + functor(WorkTag(), global_id + begin, local_value, false); + } + + workgroup_scan<>(item, reducer, local_mem, local_value, wgroup_size); + + // Write results to global memory + if (global_id < size) global_mem[global_id] = local_value; + + if (local_id == wgroup_size - 1) { + group_results[item.get_group_linear_id()] = + local_mem[item.get_sub_group().get_group_range()[0] - 1]; + + sycl::atomic_ref + scratch_flags_ref(*scratch_flags); + num_teams_done[0] = ++scratch_flags_ref; + } + item.barrier(sycl::access::fence_space::global_space); + if (num_teams_done[0] == n_wgroups) { + if (local_id == 0) *scratch_flags = 0; + value_type total; + reducer.init(&total); + + for (unsigned int offset = 0; offset < n_wgroups; + offset += wgroup_size) { + index_type id = local_id + offset; + if (id < static_cast(n_wgroups)) + local_value = group_results[id]; + else + reducer.init(&local_value); + workgroup_scan<>( + item, reducer, local_mem, local_value, + std::min(n_wgroups - offset, wgroup_size)); + if (id < static_cast(n_wgroups)) { + reducer.join(&local_value, &total); + group_results[id] = local_value; } - - workgroup_scan<>(item, reducer, local_mem, local_value, - wgroup_size); - - // Write results to global memory - if (global_id < size) global_mem[global_id] = local_value; - - if (local_id == wgroup_size - 1) { - group_results[item.get_group_linear_id()] = - local_mem[item.get_sub_group().get_group_range()[0] - 1]; - - sycl::atomic_ref - scratch_flags_ref(*scratch_flags); - num_teams_done[0] = ++scratch_flags_ref; - } - item.barrier(sycl::access::fence_space::global_space); - if (num_teams_done[0] == n_wgroups) { - if (local_id == 0) *scratch_flags = 0; - value_type total; - reducer.init(&total); - - for (unsigned int offset = 0; offset < n_wgroups; - offset += wgroup_size) { - index_type id = local_id + offset; - if (id < static_cast(n_wgroups)) - local_value = group_results[id]; - else - reducer.init(&local_value); - workgroup_scan<>( - item, reducer, local_mem, local_value, - std::min(n_wgroups - offset, wgroup_size)); - if (id < static_cast(n_wgroups)) { - reducer.join(&local_value, &total); - group_results[id] = local_value; - } - reducer.join( - &total, - &local_mem[item.get_sub_group().get_group_range()[0] - 1]); - if (offset + wgroup_size < n_wgroups) - item.barrier(sycl::access::fence_space::global_space); - } - } - }; - return lambda; - }; + reducer.join( + &total, + &local_mem[item.get_sub_group().get_group_range()[0] - 1]); + if (offset + wgroup_size < n_wgroups) + item.barrier(sycl::access::fence_space::global_space); + } + } + }; + return lambda; + }; size_t wgroup_size; size_t n_wgroups; - sycl::device_ptr global_mem; - sycl::device_ptr group_results; + sycl_device_ptr global_mem; + sycl_device_ptr group_results; desul::ensure_sycl_lock_arrays_on_device(q); @@ -254,9 +289,9 @@ class ParallelScanSYCLBase { // FIXME_SYCL consider only storing one value per block and recreate // initial results in the end before doing the final pass global_mem = - static_cast>(instance.scratch_space( + static_cast>(instance.scratch_space( n_wgroups * (wgroup_size + 1) * sizeof(value_type))); - m_scratch_host = static_cast>( + m_scratch_host = static_cast>( instance.scratch_host(sizeof(value_type))); group_results = global_mem + n_wgroups * wgroup_size; @@ -334,6 +369,11 @@ class ParallelScanSYCLBase { auto& instance = *m_policy.space().impl_internal_space_instance(); + // Only let one instance at a time resize the instance's scratch memory + // allocations. + std::scoped_lock scratch_buffers_lock( + instance.m_mutexScratchSpace); + Kokkos::Experimental::Impl::SYCLInternal::IndirectKernelMem& indirectKernelMem = instance.get_indirect_kernel_mem(); @@ -352,10 +392,7 @@ class ParallelScanSYCLBase { : m_functor_reducer(arg_functor, typename Analysis::Reducer{arg_functor}), m_policy(arg_policy), m_result_ptr(arg_result_ptr), - m_result_ptr_device_accessible(arg_result_ptr_device_accessible), - m_scratch_buffers_lock(m_policy.space() - .impl_internal_space_instance() - ->m_mutexScratchSpace) {} + m_result_ptr_device_accessible(arg_result_ptr_device_accessible) {} }; } // namespace Kokkos::Impl diff --git a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_Space.cpp b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_Space.cpp index 9cc8008cdf..19fad29150 100644 --- a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_Space.cpp +++ b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_Space.cpp @@ -56,6 +56,23 @@ void DeepCopyAsyncSYCL(void* dst, const void* src, size_t n) { /*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/ +namespace { + +std::string_view get_memory_space_name(sycl::usm::alloc allocation_kind) { + switch (allocation_kind) { + case sycl::usm::alloc::host: + return Kokkos::Experimental::SYCLHostUSMSpace::name(); + case sycl::usm::alloc::device: + return Kokkos::Experimental::SYCLDeviceUSMSpace::name(); + case sycl::usm::alloc::shared: + return Kokkos::Experimental::SYCLSharedUSMSpace::name(); + default: + Kokkos::abort("bug: unknown sycl allocation type"); + return "unreachable"; + } +} + +} // namespace namespace Kokkos { namespace Experimental { @@ -75,17 +92,17 @@ SYCLHostUSMSpace::SYCLHostUSMSpace() SYCLHostUSMSpace::SYCLHostUSMSpace(sycl::queue queue) : m_queue(std::move(queue)) {} -void* allocate_sycl( - const char* arg_label, const size_t arg_alloc_size, - const size_t arg_logical_size, const Kokkos::Tools::SpaceHandle arg_handle, - const RawMemoryAllocationFailure::AllocationMechanism failure_tag, - const sycl::usm::alloc allocation_kind, const sycl::queue& queue) { +void* allocate_sycl(const char* arg_label, const size_t arg_alloc_size, + const size_t arg_logical_size, + const Kokkos::Tools::SpaceHandle arg_handle, + const sycl::usm::alloc allocation_kind, + const sycl::queue& queue) { void* const hostPtr = sycl::malloc(arg_alloc_size, queue, allocation_kind); - if (hostPtr == nullptr) - throw RawMemoryAllocationFailure( - arg_alloc_size, 1, RawMemoryAllocationFailure::FailureMode::Unknown, - failure_tag); + if (hostPtr == nullptr) { + Kokkos::Impl::throw_bad_alloc(get_memory_space_name(allocation_kind), + arg_alloc_size, arg_label); + } if (Kokkos::Profiling::profileLibraryLoaded()) { const size_t reported_size = @@ -106,12 +123,10 @@ void* SYCLDeviceUSMSpace::allocate(const Kokkos::Experimental::SYCL& exec_space, const char* arg_label, const size_t arg_alloc_size, const size_t arg_logical_size) const { - return allocate_sycl( - arg_label, arg_alloc_size, arg_logical_size, - Kokkos::Tools::make_space_handle(name()), - RawMemoryAllocationFailure::AllocationMechanism::SYCLMallocDevice, - sycl::usm::alloc::device, - *exec_space.impl_internal_space_instance()->m_queue); + return allocate_sycl(arg_label, arg_alloc_size, arg_logical_size, + Kokkos::Tools::make_space_handle(name()), + sycl::usm::alloc::device, + *exec_space.impl_internal_space_instance()->m_queue); } void* SYCLDeviceUSMSpace::allocate(const size_t arg_alloc_size) const { @@ -121,11 +136,9 @@ void* SYCLDeviceUSMSpace::allocate(const size_t arg_alloc_size) const { void* SYCLDeviceUSMSpace::allocate(const char* arg_label, const size_t arg_alloc_size, const size_t arg_logical_size) const { - return allocate_sycl( - arg_label, arg_alloc_size, arg_logical_size, - Kokkos::Tools::make_space_handle(name()), - RawMemoryAllocationFailure::AllocationMechanism::SYCLMallocDevice, - sycl::usm::alloc::device, m_queue); + return allocate_sycl(arg_label, arg_alloc_size, arg_logical_size, + Kokkos::Tools::make_space_handle(name()), + sycl::usm::alloc::device, m_queue); } void* SYCLSharedUSMSpace::allocate(const SYCL& exec_space, @@ -136,12 +149,10 @@ void* SYCLSharedUSMSpace::allocate(const SYCL& exec_space, const char* arg_label, const size_t arg_alloc_size, const size_t arg_logical_size) const { - return allocate_sycl( - arg_label, arg_alloc_size, arg_logical_size, - Kokkos::Tools::make_space_handle(name()), - RawMemoryAllocationFailure::AllocationMechanism::SYCLMallocShared, - sycl::usm::alloc::shared, - *exec_space.impl_internal_space_instance()->m_queue); + return allocate_sycl(arg_label, arg_alloc_size, arg_logical_size, + Kokkos::Tools::make_space_handle(name()), + sycl::usm::alloc::shared, + *exec_space.impl_internal_space_instance()->m_queue); } void* SYCLSharedUSMSpace::allocate(const size_t arg_alloc_size) const { @@ -150,11 +161,9 @@ void* SYCLSharedUSMSpace::allocate(const size_t arg_alloc_size) const { void* SYCLSharedUSMSpace::allocate(const char* arg_label, const size_t arg_alloc_size, const size_t arg_logical_size) const { - return allocate_sycl( - arg_label, arg_alloc_size, arg_logical_size, - Kokkos::Tools::make_space_handle(name()), - RawMemoryAllocationFailure::AllocationMechanism::SYCLMallocShared, - sycl::usm::alloc::shared, m_queue); + return allocate_sycl(arg_label, arg_alloc_size, arg_logical_size, + Kokkos::Tools::make_space_handle(name()), + sycl::usm::alloc::shared, m_queue); } void* SYCLHostUSMSpace::allocate(const SYCL& exec_space, @@ -164,12 +173,10 @@ void* SYCLHostUSMSpace::allocate(const SYCL& exec_space, void* SYCLHostUSMSpace::allocate(const SYCL& exec_space, const char* arg_label, const size_t arg_alloc_size, const size_t arg_logical_size) const { - return allocate_sycl( - arg_label, arg_alloc_size, arg_logical_size, - Kokkos::Tools::make_space_handle(name()), - RawMemoryAllocationFailure::AllocationMechanism::SYCLMallocHost, - sycl::usm::alloc::host, - *exec_space.impl_internal_space_instance()->m_queue); + return allocate_sycl(arg_label, arg_alloc_size, arg_logical_size, + Kokkos::Tools::make_space_handle(name()), + sycl::usm::alloc::host, + *exec_space.impl_internal_space_instance()->m_queue); } void* SYCLHostUSMSpace::allocate(const size_t arg_alloc_size) const { @@ -178,11 +185,9 @@ void* SYCLHostUSMSpace::allocate(const size_t arg_alloc_size) const { void* SYCLHostUSMSpace::allocate(const char* arg_label, const size_t arg_alloc_size, const size_t arg_logical_size) const { - return allocate_sycl( - arg_label, arg_alloc_size, arg_logical_size, - Kokkos::Tools::make_space_handle(name()), - RawMemoryAllocationFailure::AllocationMechanism::SYCLMallocHost, - sycl::usm::alloc::host, m_queue); + return allocate_sycl(arg_label, arg_alloc_size, arg_logical_size, + Kokkos::Tools::make_space_handle(name()), + sycl::usm::alloc::host, m_queue); } void sycl_deallocate(const char* arg_label, void* const arg_alloc_ptr, diff --git a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_Team.hpp b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_Team.hpp index dbba382758..1e42faa5a8 100644 --- a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_Team.hpp +++ b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_Team.hpp @@ -22,6 +22,7 @@ #ifdef KOKKOS_ENABLE_SYCL #include +#include //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- @@ -133,72 +134,71 @@ class SYCLTeamMember { const unsigned int team_rank_ = team_rank(); // First combine the values in the same subgroup +#if defined(KOKKOS_ARCH_INTEL_GPU) || defined(KOKKOS_IMPL_ARCH_NVIDIA_GPU) + auto shuffle_combine = [&](int shift) { + if (vector_range * shift < sub_group_range) { + const value_type tmp = Kokkos::Impl::SYCLReduction::shift_group_left( + sg, value, vector_range * shift); + if (team_rank_ + shift < team_size_) reducer.join(value, tmp); + } + }; + shuffle_combine(1); + shuffle_combine(2); + shuffle_combine(4); + shuffle_combine(8); + shuffle_combine(16); + KOKKOS_ASSERT(sub_group_range <= 32); +#else for (unsigned int shift = 1; vector_range * shift < sub_group_range; shift <<= 1) { - const value_type tmp = sg.shuffle_down(value, vector_range * shift); + auto tmp = Kokkos::Impl::SYCLReduction::shift_group_left( + sg, value, vector_range * shift); if (team_rank_ + shift < team_size_) reducer.join(value, tmp); } - value = sg.shuffle(value, 0); +#endif + value = Kokkos::Impl::SYCLReduction::select_from_group(sg, value, 0); - const auto n_subgroups = sg.get_group_range()[0]; + const int n_subgroups = sg.get_group_range()[0]; if (n_subgroups == 1) { reducer.reference() = value; return; } - // We need to chunk up the whole reduction because we might not have - // allocated enough memory. - const unsigned int maximum_work_range = - std::min(m_team_reduce_size / sizeof(value_type), n_subgroups); + // It was found experimentally that 16 is a good value for Intel PVC. + // Since there is a maximum number of 1024 threads with subgroup size 16, + // we have a maximum of 64 subgroups per workgroup which means 64/16=4 + // rounds for loading values into the reduction_array, and 16 redundant + // reduction steps executed by every thread. + constexpr int step_width = 16; + auto tmp_alloc = sycl::ext::oneapi::group_local_memory_for_overwrite< + value_type[step_width]>(m_item.get_group()); + auto& reduction_array = *tmp_alloc; const auto id_in_sg = sg.get_local_id()[0]; - auto reduction_array = - static_cast>(m_team_reduce); - // Load values into the first maximum_work_range values of the reduction + // Load values into the first step_width values of the reduction // array in chunks. This means that only sub groups with an id in the // corresponding chunk load values. - const auto group_id = sg.get_group_id()[0]; - if (id_in_sg == 0 && group_id < maximum_work_range) + const int group_id = sg.get_group_id()[0]; + if (id_in_sg == 0 && group_id < step_width) reduction_array[group_id] = value; sycl::group_barrier(m_item.get_group()); - for (unsigned int start = maximum_work_range; start < n_subgroups; - start += maximum_work_range) { + for (int start = step_width; start < n_subgroups; start += step_width) { if (id_in_sg == 0 && group_id >= start && - group_id < - std::min(start + maximum_work_range, n_subgroups)) + group_id < std::min(start + step_width, n_subgroups)) reducer.join(reduction_array[group_id - start], value); sycl::group_barrier(m_item.get_group()); } - // Let the first subgroup do the final reduction - if (group_id == 0) { - const auto local_range = sg.get_local_range()[0]; - auto result = - reduction_array[id_in_sg < maximum_work_range ? id_in_sg : 0]; - // In case the maximum_work_range is larger than the range of the first - // subgroup, we first combine the items with a higher index. - for (unsigned int offset = local_range; offset < maximum_work_range; - offset += local_range) - if (id_in_sg + offset < maximum_work_range) - reducer.join(result, reduction_array[id_in_sg + offset]); - sycl::group_barrier(sg); + // Do the final reduction for all threads redundantly + value = reduction_array[0]; + for (int i = 1; i < std::min(step_width, n_subgroups); ++i) + reducer.join(value, reduction_array[i]); - // Now do the actual subgroup reduction. - const auto min_range = - std::min(maximum_work_range, local_range); - for (unsigned int stride = 1; stride < min_range; stride <<= 1) { - const auto tmp = sg.shuffle_down(result, stride); - if (id_in_sg + stride < min_range) reducer.join(result, tmp); - } - if (id_in_sg == 0) reduction_array[0] = result; - } + reducer.reference() = value; + // Make sure that every thread is done using the reduction array. sycl::group_barrier(m_item.get_group()); - - reducer.reference() = reduction_array[0]; - // Make sure that the reduction array hasn't been modified in the meantime. - m_item.barrier(sycl::access::fence_space::local_space); } //-------------------------------------------------------------------------- @@ -223,7 +223,8 @@ class SYCLTeamMember { // First combine the values in the same subgroup for (unsigned int stride = 1; vector_range * stride < sub_group_range; stride <<= 1) { - auto tmp = sg.shuffle_up(value, vector_range * stride); + auto tmp = Kokkos::Impl::SYCLReduction::shift_group_right( + sg, value, vector_range * stride); if (id_in_sg >= vector_range * stride) value += tmp; } @@ -249,7 +250,8 @@ class SYCLTeamMember { sub_group_range, n_active_subgroups - round * sub_group_range); auto local_value = base_data[idx]; for (unsigned int stride = 1; stride < upper_bound; stride <<= 1) { - auto tmp = sg.shuffle_up(local_value, stride); + auto tmp = Kokkos::Impl::SYCLReduction::shift_group_right( + sg, local_value, stride); if (id_in_sg >= stride) { if (idx < n_active_subgroups) local_value += tmp; @@ -267,7 +269,8 @@ class SYCLTeamMember { } auto total = base_data[n_active_subgroups - 1]; - const auto update = sg.shuffle_up(value, vector_range); + const auto update = + Kokkos::Impl::SYCLReduction::shift_group_right(sg, value, vector_range); Type intermediate = (group_id > 0 ? base_data[group_id - 1] : 0) + (id_in_sg >= vector_range ? update : 0); @@ -320,7 +323,7 @@ class SYCLTeamMember { typename ReducerType::value_type tmp2 = tmp; for (int i = grange1; (i >>= 1);) { - tmp2 = sg.shuffle_down(tmp, i); + tmp2 = Kokkos::Impl::SYCLReduction::shift_group_left(sg, tmp, i); if (static_cast(tidx1) < i) { reducer.join(tmp, tmp2); } @@ -331,8 +334,9 @@ class SYCLTeamMember { // because floating point summation is not associative // and thus different threads could have different results. - tmp2 = sg.shuffle(tmp, (sg.get_local_id() / grange1) * grange1); - value = tmp2; + tmp2 = Kokkos::Impl::SYCLReduction::select_from_group( + sg, tmp, (sg.get_local_id() / grange1) * grange1); + value = tmp2; reducer.reference() = tmp2; } @@ -342,7 +346,7 @@ class SYCLTeamMember { KOKKOS_INLINE_FUNCTION SYCLTeamMember(sycl::local_ptr shared, const std::size_t shared_begin, const std::size_t shared_size, - sycl::device_ptr scratch_level_1_ptr, + sycl_device_ptr scratch_level_1_ptr, const std::size_t scratch_level_1_size, const sycl::nd_item<2> item, const int arg_league_rank, const int arg_league_size) @@ -839,7 +843,8 @@ parallel_scan(const Impl::ThreadVectorRangeBoundariesStruct< // [t] += [t-4] if t >= 4 // ... for (int j = 1; j < static_cast(grange1); j <<= 1) { - value_type tmp = sg.shuffle_up(val, j); + value_type tmp = + Kokkos::Impl::SYCLReduction::shift_group_right(sg, val, j); if (j <= static_cast(tidx1)) { reducer.join(val, tmp); } @@ -850,7 +855,8 @@ parallel_scan(const Impl::ThreadVectorRangeBoundariesStruct< // Update i's contribution into the val and add it to accum for next round if (i < loop_boundaries.end) closure(i, val, true); - accum = sg.shuffle(val, mask + vector_offset); + accum = Kokkos::Impl::SYCLReduction::select_from_group( + sg, val, mask + vector_offset); } reducer.reference() = accum; } @@ -927,7 +933,8 @@ KOKKOS_INLINE_FUNCTION void single( const auto grange1 = item.get_local_range(1); const auto sg = item.get_sub_group(); if (item.get_local_id(1) == 0) lambda(val); - val = sg.shuffle(val, (sg.get_local_id() / grange1) * grange1); + val = Kokkos::Impl::SYCLReduction::select_from_group( + sg, val, (sg.get_local_id() / grange1) * grange1); } template diff --git a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_WorkgroupReduction.hpp b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_WorkgroupReduction.hpp index c308384af0..abf0bd8f53 100644 --- a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_WorkgroupReduction.hpp +++ b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_WorkgroupReduction.hpp @@ -21,8 +21,53 @@ namespace Kokkos::Impl::SYCLReduction { -// FIXME_SYCL It appears that using shuffles is slower than going through local -// memory. +template +struct TrivialWrapper { + std::byte array[N]; +}; + +// shuffle down +template +T shift_group_left(sycl::sub_group sg, T x, + sycl::sub_group::linear_id_type delta) { + if constexpr (std::is_trivially_copyable_v) + return sycl::shift_group_left(sg, x, delta); + else { + auto tmp = sycl::shift_group_left( + sg, reinterpret_cast&>(x), delta); + return reinterpret_cast(tmp); + } +} + +// shuffle up +template +T shift_group_right(sycl::sub_group sg, T x, + sycl::sub_group::linear_id_type delta) { + if constexpr (std::is_trivially_copyable_v) + return sycl::shift_group_right(sg, x, delta); + else { + auto tmp = sycl::shift_group_right( + sg, reinterpret_cast&>(x), delta); + return reinterpret_cast(tmp); + } +} + +// shuffle +template +T select_from_group(sycl::sub_group sg, T x, + sycl::sub_group::id_type remote_local_id) { + if constexpr (std::is_trivially_copyable_v) + return sycl::select_from_group(sg, x, remote_local_id); + else { + auto tmp = sycl::select_from_group( + sg, reinterpret_cast&>(x), remote_local_id); + return reinterpret_cast(tmp); + } +} + +// FIXME_SYCL For some types, shuffle reductions are competitive with local +// memory reductions but they are significantly slower for the value type used +// in combined reductions with multiple double arguments. template inline constexpr bool use_shuffle_based_algorithm = false; // std::is_reference_v; @@ -30,7 +75,7 @@ inline constexpr bool use_shuffle_based_algorithm = false; template std::enable_if_t> workgroup_reduction( sycl::nd_item& item, sycl::local_accessor local_mem, - sycl::device_ptr results_ptr, + sycl_device_ptr results_ptr, sycl::global_ptr device_accessible_result_ptr, const unsigned int value_count_, const ReducerType& final_reducer, bool final, unsigned int max_size) { @@ -102,24 +147,40 @@ std::enable_if_t> workgroup_reduction( template std::enable_if_t> workgroup_reduction( sycl::nd_item& item, sycl::local_accessor local_mem, - ValueType local_value, sycl::device_ptr results_ptr, + ValueType local_value, sycl_device_ptr results_ptr, sycl::global_ptr device_accessible_result_ptr, const ReducerType& final_reducer, bool final, unsigned int max_size) { const auto local_id = item.get_local_linear_id(); // Perform the actual workgroup reduction in each subgroup // separately. - auto sg = item.get_sub_group(); - const int id_in_sg = sg.get_local_id()[0]; - const auto local_range = - std::min(sg.get_local_range()[0], max_size); + auto sg = item.get_sub_group(); + const int id_in_sg = sg.get_local_id()[0]; + const int local_range = std::min(sg.get_local_range()[0], max_size); const auto upper_stride_bound = - std::min(local_range - id_in_sg, max_size - local_id); + std::min(local_range - id_in_sg, max_size - local_id); +#if defined(KOKKOS_ARCH_INTEL_GPU) || defined(KOKKOS_IMPL_ARCH_NVIDIA_GPU) + auto shuffle_combine = [&](int stride) { + if (stride < local_range) { + auto tmp = Kokkos::Impl::SYCLReduction::shift_group_left(sg, local_value, + stride); + if (stride < upper_stride_bound) final_reducer.join(&local_value, &tmp); + } + }; + shuffle_combine(1); + shuffle_combine(2); + shuffle_combine(4); + shuffle_combine(8); + shuffle_combine(16); + KOKKOS_ASSERT(local_range <= 32); +#else for (unsigned int stride = 1; stride < local_range; stride <<= 1) { - auto tmp = sg.shuffle_down(local_value, stride); + auto tmp = + Kokkos::Impl::SYCLReduction::shift_group_left(sg, local_value, stride); if (stride < upper_stride_bound) final_reducer.join(&local_value, &tmp); } +#endif // Copy the subgroup results into the first positions of the // reduction array. @@ -140,7 +201,7 @@ std::enable_if_t> workgroup_reduction( // the first subgroup, we first combine the items with a higher // index. if (n_active_subgroups > local_range) { - for (unsigned int offset = local_range; offset < n_active_subgroups; + for (int offset = local_range; offset < n_active_subgroups; offset += local_range) if (id_in_sg + offset < n_active_subgroups) { final_reducer.join(&sg_value, &local_mem[(id_in_sg + offset)]); @@ -149,11 +210,29 @@ std::enable_if_t> workgroup_reduction( } // Then, we proceed as before. +#if defined(KOKKOS_ARCH_INTEL_GPU) || defined(KOKKOS_IMPL_ARCH_NVIDIA_GPU) + auto shuffle_combine_sg = [&](int stride) { + if (stride < local_range) { + auto tmp = + Kokkos::Impl::SYCLReduction::shift_group_left(sg, sg_value, stride); + if (id_in_sg + stride < n_active_subgroups) + final_reducer.join(&sg_value, &tmp); + } + }; + shuffle_combine_sg(1); + shuffle_combine_sg(2); + shuffle_combine_sg(4); + shuffle_combine_sg(8); + shuffle_combine_sg(16); + KOKKOS_ASSERT(local_range <= 32); +#else for (unsigned int stride = 1; stride < local_range; stride <<= 1) { - auto tmp = sg.shuffle_down(sg_value, stride); + auto tmp = + Kokkos::Impl::SYCLReduction::shift_group_left(sg, sg_value, stride); if (id_in_sg + stride < n_active_subgroups) final_reducer.join(&sg_value, &tmp); } +#endif // Finally, we copy the workgroup results back to global memory // to be used in the next iteration. If this is the last diff --git a/lib/kokkos/core/src/Serial/Kokkos_Serial.cpp b/lib/kokkos/core/src/Serial/Kokkos_Serial.cpp index 39b201976b..44d797f1cc 100644 --- a/lib/kokkos/core/src/Serial/Kokkos_Serial.cpp +++ b/lib/kokkos/core/src/Serial/Kokkos_Serial.cpp @@ -35,6 +35,9 @@ namespace Kokkos { namespace Impl { +std::vector SerialInternal::all_instances; +std::mutex SerialInternal::all_instances_mutex; + bool SerialInternal::is_initialized() { return m_is_initialized; } void SerialInternal::initialize() { @@ -43,6 +46,12 @@ void SerialInternal::initialize() { Impl::SharedAllocationRecord::tracking_enable(); m_is_initialized = true; + + // guard pushing to all_instances + { + std::scoped_lock lock(all_instances_mutex); + all_instances.push_back(this); + } } void SerialInternal::finalize() { @@ -59,6 +68,17 @@ void SerialInternal::finalize() { } m_is_initialized = false; + + // guard erasing from all_instances + { + std::scoped_lock lock(all_instances_mutex); + auto it = std::find(all_instances.begin(), all_instances.end(), this); + if (it == all_instances.end()) + Kokkos::abort( + "Execution space instance to be removed couldn't be found!"); + std::swap(*it, all_instances.back()); + all_instances.pop_back(); + } } SerialInternal& SerialInternal::singleton() { @@ -97,9 +117,12 @@ void SerialInternal::resize_thread_team_data(size_t pool_reduce_bytes, m_thread_team_data.disband_team(); m_thread_team_data.disband_pool(); - space.deallocate("Kokkos::Serial::scratch_mem", - m_thread_team_data.scratch_buffer(), - m_thread_team_data.scratch_bytes()); + // impl_deallocate doesn't fence which we try to avoid here since that + // interferes with the using the m_instance_mutex for ensuring proper + // kernel enqueuing + space.impl_deallocate("Kokkos::Serial::scratch_mem", + m_thread_team_data.scratch_buffer(), + m_thread_team_data.scratch_bytes()); } if (pool_reduce_bytes < old_pool_reduce) { @@ -119,13 +142,7 @@ void SerialInternal::resize_thread_team_data(size_t pool_reduce_bytes, HostThreadTeamData::scratch_size(pool_reduce_bytes, team_reduce_bytes, team_shared_bytes, thread_local_bytes); - void* ptr = nullptr; - try { - ptr = space.allocate("Kokkos::Serial::scratch_mem", alloc_bytes); - } catch (Kokkos::Experimental::RawMemoryAllocationFailure const& failure) { - // For now, just rethrow the error message the existing way - Kokkos::Impl::throw_runtime_exception(failure.get_error_message()); - } + void* ptr = space.allocate("Kokkos::Serial::scratch_mem", alloc_bytes); m_thread_team_data.scratch_assign(static_cast(ptr), alloc_bytes, pool_reduce_bytes, team_reduce_bytes, @@ -147,7 +164,9 @@ Serial::Serial(NewInstance) : m_space_instance(new Impl::SerialInternal, [](Impl::SerialInternal* ptr) { ptr->finalize(); delete ptr; - }) {} + }) { + m_space_instance->initialize(); +} void Serial::print_configuration(std::ostream& os, bool /*verbose*/) const { os << "Host Serial Execution Space:\n"; diff --git a/lib/kokkos/core/src/Serial/Kokkos_Serial.hpp b/lib/kokkos/core/src/Serial/Kokkos_Serial.hpp index 43eb4992ed..81d43b31b3 100644 --- a/lib/kokkos/core/src/Serial/Kokkos_Serial.hpp +++ b/lib/kokkos/core/src/Serial/Kokkos_Serial.hpp @@ -60,7 +60,10 @@ class SerialInternal { static SerialInternal& singleton(); - std::mutex m_thread_team_data_mutex; + std::mutex m_instance_mutex; + + static std::vector all_instances; + static std::mutex all_instances_mutex; // Resize thread team data scratch memory void resize_thread_team_data(size_t pool_reduce_bytes, @@ -113,7 +116,15 @@ class Serial { Serial(); - Serial(NewInstance); + explicit Serial(NewInstance); + +#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4 + template + KOKKOS_DEPRECATED_WITH_COMMENT( + "Serial execution space should be constructed explicitly.") + Serial(NewInstance) + : Serial(NewInstance{}) {} +#endif /// \brief True if and only if this method is being called in a /// thread-parallel function. @@ -137,7 +148,14 @@ class Serial { name, Kokkos::Tools::Experimental::SpecialSynchronizationCases:: GlobalDeviceSynchronization, - []() {}); // TODO: correct device ID + []() { + std::lock_guard lock_all_instances( + Impl::SerialInternal::all_instances_mutex); + for (auto* instance_ptr : Impl::SerialInternal::all_instances) { + std::lock_guard lock_instance( + instance_ptr->m_instance_mutex); + } + }); // TODO: correct device ID Kokkos::memory_fence(); } @@ -145,7 +163,10 @@ class Serial { "Kokkos::Serial::fence: Unnamed Instance Fence") const { Kokkos::Tools::Experimental::Impl::profile_fence_event( name, Kokkos::Tools::Experimental::Impl::DirectFenceIDHandle{1}, - []() {}); // TODO: correct device ID + [this]() { + auto* internal_instance = this->impl_internal_space_instance(); + std::lock_guard lock(internal_instance->m_instance_mutex); + }); // TODO: correct device ID Kokkos::memory_fence(); } diff --git a/lib/kokkos/core/src/Serial/Kokkos_Serial_Parallel_MDRange.hpp b/lib/kokkos/core/src/Serial/Kokkos_Serial_Parallel_MDRange.hpp index 67978aa3e9..34e115eca9 100644 --- a/lib/kokkos/core/src/Serial/Kokkos_Serial_Parallel_MDRange.hpp +++ b/lib/kokkos/core/src/Serial/Kokkos_Serial_Parallel_MDRange.hpp @@ -43,7 +43,14 @@ class ParallelFor, } public: - inline void execute() const { this->exec(); } + inline void execute() const { + // Make sure kernels are running sequentially even when using multiple + // threads + auto* internal_instance = + m_iter.m_rp.space().impl_internal_space_instance(); + std::lock_guard lock(internal_instance->m_instance_mutex); + this->exec(); + } template static int max_tile_size_product(const Policy&, const Functor&) { /** @@ -104,9 +111,11 @@ class ParallelReduce lock( - internal_instance->m_thread_team_data_mutex); + + // Make sure kernels are running sequentially even when using multiple + // threads, lock resize_thread_team_data + std::lock_guard instance_lock( + internal_instance->m_instance_mutex); internal_instance->resize_thread_team_data( pool_reduce_size, team_reduce_size, team_shared_size, thread_local_size); diff --git a/lib/kokkos/core/src/Serial/Kokkos_Serial_Parallel_Range.hpp b/lib/kokkos/core/src/Serial/Kokkos_Serial_Parallel_Range.hpp index 91b4c56711..80faec9041 100644 --- a/lib/kokkos/core/src/Serial/Kokkos_Serial_Parallel_Range.hpp +++ b/lib/kokkos/core/src/Serial/Kokkos_Serial_Parallel_Range.hpp @@ -49,6 +49,10 @@ class ParallelFor, Kokkos::Serial> { public: inline void execute() const { + // Make sure kernels are running sequentially even when using multiple + // threads + auto* internal_instance = m_policy.space().impl_internal_space_instance(); + std::lock_guard lock(internal_instance->m_instance_mutex); this->template exec(); } @@ -103,9 +107,11 @@ class ParallelReduce, const size_t thread_local_size = 0; // Never shrinks auto* internal_instance = m_policy.space().impl_internal_space_instance(); - // Need to lock resize_thread_team_data - std::lock_guard lock( - internal_instance->m_thread_team_data_mutex); + + // Make sure kernels are running sequentially even when using multiple + // threads, lock resize_thread_team_data + std::lock_guard instance_lock( + internal_instance->m_instance_mutex); internal_instance->resize_thread_team_data( pool_reduce_size, team_reduce_size, team_shared_size, thread_local_size); @@ -187,10 +193,12 @@ class ParallelScan, const size_t team_shared_size = 0; // Never shrinks const size_t thread_local_size = 0; // Never shrinks - // Need to lock resize_thread_team_data auto* internal_instance = m_policy.space().impl_internal_space_instance(); - std::lock_guard lock( - internal_instance->m_thread_team_data_mutex); + // Make sure kernels are running sequentially even when using multiple + // threads, lock resize_thread_team_data + std::lock_guard instance_lock( + internal_instance->m_instance_mutex); + internal_instance->resize_thread_team_data( pool_reduce_size, team_reduce_size, team_shared_size, thread_local_size); @@ -253,10 +261,12 @@ class ParallelScanWithTotal, const size_t team_shared_size = 0; // Never shrinks const size_t thread_local_size = 0; // Never shrinks - // Need to lock resize_thread_team_data auto* internal_instance = m_policy.space().impl_internal_space_instance(); - std::lock_guard lock( - internal_instance->m_thread_team_data_mutex); + // Make sure kernels are running sequentially even when using multiple + // threads, lock resize_thread_team_data + std::lock_guard instance_lock( + internal_instance->m_instance_mutex); + internal_instance->resize_thread_team_data( pool_reduce_size, team_reduce_size, team_shared_size, thread_local_size); diff --git a/lib/kokkos/core/src/Serial/Kokkos_Serial_Parallel_Team.hpp b/lib/kokkos/core/src/Serial/Kokkos_Serial_Parallel_Team.hpp index a25b51496e..a523cc86c9 100644 --- a/lib/kokkos/core/src/Serial/Kokkos_Serial_Parallel_Team.hpp +++ b/lib/kokkos/core/src/Serial/Kokkos_Serial_Parallel_Team.hpp @@ -247,9 +247,11 @@ class ParallelFor, const size_t thread_local_size = 0; // Never shrinks auto* internal_instance = m_policy.space().impl_internal_space_instance(); - // Need to lock resize_thread_team_data - std::lock_guard lock( - internal_instance->m_thread_team_data_mutex); + // Make sure kernels are running sequentially even when using multiple + // threads, lock resize_thread_team_data + std::lock_guard instance_lock( + internal_instance->m_instance_mutex); + internal_instance->resize_thread_team_data( pool_reduce_size, team_reduce_size, team_shared_size, thread_local_size); @@ -319,9 +321,11 @@ class ParallelReduce lock( - internal_instance->m_thread_team_data_mutex); + // Make sure kernels are running sequentially even when using multiple + // threads, lock resize_thread_team_data + std::lock_guard instance_lock( + internal_instance->m_instance_mutex); + internal_instance->resize_thread_team_data( pool_reduce_size, team_reduce_size, team_shared_size, thread_local_size); diff --git a/lib/kokkos/core/src/Threads/Kokkos_Threads_Team.hpp b/lib/kokkos/core/src/Threads/Kokkos_Threads_Team.hpp index fd0f221365..a3501a437d 100644 --- a/lib/kokkos/core/src/Threads/Kokkos_Threads_Team.hpp +++ b/lib/kokkos/core/src/Threads/Kokkos_Threads_Team.hpp @@ -188,8 +188,6 @@ class ThreadsExecTeamMember { using type = typename if_c::type; - if (m_instance == nullptr) return value; - if (team_rank() != team_size() - 1) * ((volatile type*)m_instance->scratch_memory()) = value; @@ -229,8 +227,6 @@ class ThreadsExecTeamMember { using type = typename if_c::type; - if (m_instance == nullptr) return; - type* const local_value = ((type*)m_instance->scratch_memory()); // Set this thread's contribution @@ -285,8 +281,6 @@ class ThreadsExecTeamMember { using type = typename if_c::type; - if (m_instance == nullptr) return type(0); - volatile type* const work_value = ((type*)m_instance->scratch_memory()); *work_value = value; @@ -358,6 +352,7 @@ class ThreadsExecTeamMember { m_chunk_size(team.chunk_size()), m_league_chunk_end(0), m_team_alloc(team.team_alloc()) { + KOKKOS_ASSERT(m_instance != nullptr); if (team.league_size()) { // Execution is using device-team interface: diff --git a/lib/kokkos/core/src/View/Kokkos_ViewAlloc.hpp b/lib/kokkos/core/src/View/Kokkos_ViewAlloc.hpp new file mode 100644 index 0000000000..95cb6f619c --- /dev/null +++ b/lib/kokkos/core/src/View/Kokkos_ViewAlloc.hpp @@ -0,0 +1,318 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#ifndef KOKKOS_IMPL_PUBLIC_INCLUDE +#include +static_assert(false, + "Including non-public Kokkos header files is not allowed."); +#endif + +#ifndef KOKKOS_VIEW_ALLOC_HPP +#define KOKKOS_VIEW_ALLOC_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace Kokkos::Impl { + +template +bool is_zero_byte(const T& x) { + constexpr std::byte all_zeroes[sizeof(T)] = {}; + return std::memcmp(&x, all_zeroes, sizeof(T)) == 0; +} + +//---------------------------------------------------------------------------- + +/* + * The construction, assignment to default, and destruction + * are merged into a single functor. + * Primarily to work around an unresolved CUDA back-end bug + * that would lose the destruction cuda device function when + * called from the shared memory tracking destruction. + * Secondarily to have two fewer partial specializations. + */ +template ::value> +struct ViewValueFunctor; + +template +struct ViewValueFunctor { + using ExecSpace = typename DeviceType::execution_space; + + struct DestroyTag {}; + struct ConstructTag {}; + + ExecSpace space; + ValueType* ptr; + size_t n; + std::string name; + bool default_exec_space; + + template + KOKKOS_INLINE_FUNCTION + std::enable_if_t::value> + operator()(ConstructTag const&, const size_t i) const { + new (ptr + i) ValueType(); + } + + KOKKOS_INLINE_FUNCTION void operator()(DestroyTag const&, + const size_t i) const { + (ptr + i)->~ValueType(); + } + + ViewValueFunctor() = default; + ViewValueFunctor(const ViewValueFunctor&) = default; + ViewValueFunctor& operator=(const ViewValueFunctor&) = default; + + ViewValueFunctor(ExecSpace const& arg_space, ValueType* const arg_ptr, + size_t const arg_n, std::string arg_name) + : space(arg_space), + ptr(arg_ptr), + n(arg_n), + name(std::move(arg_name)), + default_exec_space(false) { + functor_instantiate_workaround(); + } + + ViewValueFunctor(ValueType* const arg_ptr, size_t const arg_n, + std::string arg_name) + : space(ExecSpace{}), + ptr(arg_ptr), + n(arg_n), + name(std::move(arg_name)), + default_exec_space(true) { + functor_instantiate_workaround(); + } + + template + std::enable_if_t::value && + std::is_trivially_copy_assignable::value> + construct_dispatch() { + ValueType value{}; +// On A64FX memset seems to do the wrong thing with regards to first touch +// leading to the significant performance issues +#ifndef KOKKOS_ARCH_A64FX + if (Impl::is_zero_byte(value)) { + uint64_t kpID = 0; + if (Kokkos::Profiling::profileLibraryLoaded()) { + // We are not really using parallel_for here but using beginParallelFor + // instead of begin_parallel_for (and adding "via memset") is the best + // we can do to indicate that this is not supposed to be tunable (and + // doesn't really execute a parallel_for). + Kokkos::Profiling::beginParallelFor( + "Kokkos::View::initialization [" + name + "] via memset", + Kokkos::Profiling::Experimental::device_id(space), &kpID); + } + (void)ZeroMemset( + space, Kokkos::View>(ptr, n)); + + if (Kokkos::Profiling::profileLibraryLoaded()) { + Kokkos::Profiling::endParallelFor(kpID); + } + if (default_exec_space) + space.fence("Kokkos::Impl::ViewValueFunctor: View init/destroy fence"); + } else { +#endif + parallel_for_implementation(); +#ifndef KOKKOS_ARCH_A64FX + } +#endif + } + + template + std::enable_if_t::value && + std::is_trivially_copy_assignable::value)> + construct_dispatch() { + parallel_for_implementation(); + } + + template + void parallel_for_implementation() { + using PolicyType = + Kokkos::RangePolicy, Tag>; + PolicyType policy(space, 0, n); + uint64_t kpID = 0; + if (Kokkos::Profiling::profileLibraryLoaded()) { + const std::string functor_name = + (std::is_same_v + ? "Kokkos::View::destruction [" + name + "]" + : "Kokkos::View::initialization [" + name + "]"); + Kokkos::Profiling::beginParallelFor( + functor_name, Kokkos::Profiling::Experimental::device_id(space), + &kpID); + } + +#ifdef KOKKOS_ENABLE_CUDA + if (std::is_same::value) { + Kokkos::Impl::cuda_prefetch_pointer(space, ptr, sizeof(ValueType) * n, + true); + } +#endif + const Kokkos::Impl::ParallelFor closure( + *this, policy); + closure.execute(); + if (default_exec_space || std::is_same_v) + space.fence("Kokkos::Impl::ViewValueFunctor: View init/destroy fence"); + if (Kokkos::Profiling::profileLibraryLoaded()) { + Kokkos::Profiling::endParallelFor(kpID); + } + } + + void construct_shared_allocation() { construct_dispatch(); } + + void destroy_shared_allocation() { +#ifdef KOKKOS_ENABLE_IMPL_VIEW_OF_VIEWS_DESTRUCTOR_PRECONDITION_VIOLATION_WORKAROUND + if constexpr (std::is_same_v) + for (size_t i = 0; i < n; ++i) (ptr + i)->~ValueType(); + else +#endif + { + parallel_for_implementation(); + } + } + + // This function is to ensure that the functor with DestroyTag is instantiated + // This is a workaround to avoid "cudaErrorInvalidDeviceFunction" error later + // when the function is queried with cudaFuncGetAttributes + void functor_instantiate_workaround() { +#if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP) || \ + defined(KOKKOS_ENABLE_SYCL) || defined(KOKKOS_ENABLE_OPENMPTARGET) + if (false) { + parallel_for_implementation(); + } +#endif + } +}; + +template +struct ViewValueFunctor { + using ExecSpace = typename DeviceType::execution_space; + using PolicyType = Kokkos::RangePolicy>; + + ExecSpace space; + ValueType* ptr; + size_t n; + std::string name; + bool default_exec_space; + + KOKKOS_INLINE_FUNCTION + void operator()(const size_t i) const { ptr[i] = ValueType(); } + + ViewValueFunctor() = default; + ViewValueFunctor(const ViewValueFunctor&) = default; + ViewValueFunctor& operator=(const ViewValueFunctor&) = default; + + ViewValueFunctor(ExecSpace const& arg_space, ValueType* const arg_ptr, + size_t const arg_n, std::string arg_name) + : space(arg_space), + ptr(arg_ptr), + n(arg_n), + name(std::move(arg_name)), + default_exec_space(false) {} + + ViewValueFunctor(ValueType* const arg_ptr, size_t const arg_n, + std::string arg_name) + : space(ExecSpace{}), + ptr(arg_ptr), + n(arg_n), + name(std::move(arg_name)), + default_exec_space(true) {} + + template + std::enable_if_t::value && + std::is_trivially_copy_assignable::value> + construct_shared_allocation() { + // Shortcut for zero initialization +// On A64FX memset seems to do the wrong thing with regards to first touch +// leading to the significant performance issues +#ifndef KOKKOS_ARCH_A64FX + ValueType value{}; + if (Impl::is_zero_byte(value)) { + uint64_t kpID = 0; + if (Kokkos::Profiling::profileLibraryLoaded()) { + // We are not really using parallel_for here but using beginParallelFor + // instead of begin_parallel_for (and adding "via memset") is the best + // we can do to indicate that this is not supposed to be tunable (and + // doesn't really execute a parallel_for). + Kokkos::Profiling::beginParallelFor( + "Kokkos::View::initialization [" + name + "] via memset", + Kokkos::Profiling::Experimental::device_id(space), &kpID); + } + + (void)ZeroMemset( + space, Kokkos::View>(ptr, n)); + + if (Kokkos::Profiling::profileLibraryLoaded()) { + Kokkos::Profiling::endParallelFor(kpID); + } + if (default_exec_space) + space.fence("Kokkos::Impl::ViewValueFunctor: View init/destroy fence"); + } else { +#endif + parallel_for_implementation(); +#ifndef KOKKOS_ARCH_A64FX + } +#endif + } + + template + std::enable_if_t::value && + std::is_trivially_copy_assignable::value)> + construct_shared_allocation() { + parallel_for_implementation(); + } + + void parallel_for_implementation() { + PolicyType policy(space, 0, n); + uint64_t kpID = 0; + if (Kokkos::Profiling::profileLibraryLoaded()) { + Kokkos::Profiling::beginParallelFor( + "Kokkos::View::initialization [" + name + "]", + Kokkos::Profiling::Experimental::device_id(space), &kpID); + } +#ifdef KOKKOS_ENABLE_CUDA + if (std::is_same::value) { + Kokkos::Impl::cuda_prefetch_pointer(space, ptr, sizeof(ValueType) * n, + true); + } +#endif + const Kokkos::Impl::ParallelFor closure( + *this, policy); + closure.execute(); + if (default_exec_space) + space.fence( + "Kokkos::Impl::ViewValueFunctor: Fence after setting values in " + "view"); + if (Kokkos::Profiling::profileLibraryLoaded()) { + Kokkos::Profiling::endParallelFor(kpID); + } + } + + void destroy_shared_allocation() {} +}; +} // namespace Kokkos::Impl + +#endif // KOKKOS_VIEW_ALLOC_HPP diff --git a/lib/kokkos/core/src/View/MDSpan/Kokkos_MDSpan_Accessor.hpp b/lib/kokkos/core/src/View/MDSpan/Kokkos_MDSpan_Accessor.hpp new file mode 100644 index 0000000000..8814cc015e --- /dev/null +++ b/lib/kokkos/core/src/View/MDSpan/Kokkos_MDSpan_Accessor.hpp @@ -0,0 +1,220 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#ifndef KOKKOS_IMPL_PUBLIC_INCLUDE +static_assert(false, + "Including non-public Kokkos header files is not allowed."); +#endif + +#ifndef KOKKOS_MDSPAN_ACCESSOR_HPP +#define KOKKOS_MDSPAN_ACCESSOR_HPP + +#include +#include +#include +#include + +namespace Kokkos { + +// For now use the accessors in Impl namespace, as an +// implementation detail for rebasing View on mdspan +namespace Impl { + +template +struct SpaceAwareAccessor { + // Part of Accessor Requirements + using element_type = typename NestedAccessor::element_type; + using reference = typename NestedAccessor::reference; + using data_handle_type = typename NestedAccessor::data_handle_type; + using offset_policy = + SpaceAwareAccessor; + + // Specific to SpaceAwareAccessor + using memory_space = MemorySpace; + using nested_accessor_type = NestedAccessor; + + static_assert(is_memory_space_v); + + KOKKOS_DEFAULTED_FUNCTION + constexpr SpaceAwareAccessor() = default; + + template < + class OtherMemorySpace, class OtherNestedAccessorType, + std::enable_if_t< + MemorySpaceAccess::assignable && + std::is_constructible_v, + int> = 0> + KOKKOS_FUNCTION constexpr SpaceAwareAccessor( + const SpaceAwareAccessor& + other) noexcept + : nested_acc(other.nested_acc) {} + + KOKKOS_FUNCTION + SpaceAwareAccessor(const NestedAccessor& acc) : nested_acc(acc) {} + + KOKKOS_FUNCTION + explicit operator NestedAccessor() const { return nested_acc; } + + KOKKOS_FUNCTION + constexpr reference access(data_handle_type p, size_t i) const noexcept { + Kokkos::Impl::runtime_check_memory_access_violation( + "Kokkos::SpaceAwareAccessor ERROR: attempt to access inaccessible " + "memory space"); + return nested_acc.access(p, i); + } + + KOKKOS_FUNCTION + constexpr typename offset_policy::data_handle_type offset(data_handle_type p, + size_t i) const + noexcept { + return nested_acc.offset(p, i); + } + + // Canonical way for accessing nested accessor see ISO C++ + // [linalg.scaled.scaledaccessor] + KOKKOS_FUNCTION + constexpr const NestedAccessor& nested_accessor() const noexcept { + return nested_acc; + } + + private: +// We either compile with our custom mdspan impl +// in which case we discover inside it whether no_unique_address +// works, or we use C++23 in which case it better be available +#ifdef _MDSPAN_NO_UNIQUE_ADDRESS + _MDSPAN_NO_UNIQUE_ADDRESS +#else + [[no_unique_address]] +#endif + NestedAccessor nested_acc; + template + friend struct SpaceAwareAccessor; +}; + +template +struct SpaceAwareAccessor { + // Part of Accessor Requirements + using element_type = typename NestedAccessor::element_type; + using reference = typename NestedAccessor::reference; + using data_handle_type = typename NestedAccessor::data_handle_type; + + using offset_policy = + SpaceAwareAccessor; + + // Specific to SpaceAwareAccessor + using memory_space = AnonymousSpace; + using nested_accessor_type = NestedAccessor; + + KOKKOS_DEFAULTED_FUNCTION + constexpr SpaceAwareAccessor() = default; + + template , + int> = 0> + KOKKOS_FUNCTION constexpr SpaceAwareAccessor( + const SpaceAwareAccessor& + other) noexcept + : nested_acc(other.nested_acc) {} + + KOKKOS_FUNCTION + SpaceAwareAccessor(const NestedAccessor& acc) : nested_acc(acc) {} + + KOKKOS_FUNCTION + explicit operator NestedAccessor() const { return nested_acc; } + + KOKKOS_FUNCTION + constexpr reference access(data_handle_type p, size_t i) const noexcept { + return nested_acc.access(p, i); + } + + KOKKOS_FUNCTION + constexpr typename offset_policy::data_handle_type offset(data_handle_type p, + size_t i) const + noexcept { + return nested_acc.offset(p, i); + } + + // Canonical way for accessing nested accessor see ISO C++ + // [linalg.scaled.scaledaccessor] + KOKKOS_FUNCTION + constexpr const NestedAccessor& nested_accessor() const noexcept { + return nested_acc; + } + + private: +// We either compile with our custom mdspan impl +// in which case we discover inside it whether no_unique_address +// works, or we use C++23 in which case it better be available +#ifdef _MDSPAN_NO_UNIQUE_ADDRESS + _MDSPAN_NO_UNIQUE_ADDRESS +#else + [[no_unique_address]] +#endif + NestedAccessor nested_acc; + template + friend struct SpaceAwareAccessor; +}; + +// Like atomic_accessor_relaxed proposed for ISO C++26 but with +// defaulted memory scope - similar to how desul's AtomicRef has a memory scope +template +struct AtomicAccessorRelaxed { + using element_type = ElementType; + using reference = + desul::AtomicRef; + using data_handle_type = ElementType*; + using offset_policy = AtomicAccessorRelaxed; + + KOKKOS_DEFAULTED_FUNCTION + AtomicAccessorRelaxed() = default; + + // Conversions from non-const to const element type + template >* = nullptr> + KOKKOS_FUNCTION constexpr AtomicAccessorRelaxed( + Kokkos::default_accessor) noexcept {} + + template >* = nullptr> + KOKKOS_FUNCTION constexpr AtomicAccessorRelaxed( + AtomicAccessorRelaxed) noexcept {} + + template >* = nullptr> + KOKKOS_FUNCTION explicit operator default_accessor() const { + return default_accessor{}; + } + + KOKKOS_FUNCTION + reference access(data_handle_type p, size_t i) const noexcept { + return reference(p[i]); + } + + KOKKOS_FUNCTION + data_handle_type offset(data_handle_type p, size_t i) const noexcept { + return p + i; + } +}; + +} // namespace Impl +} // namespace Kokkos + +#endif diff --git a/lib/kokkos/core/src/View/MDSpan/Kokkos_MDSpan_Extents.hpp b/lib/kokkos/core/src/View/MDSpan/Kokkos_MDSpan_Extents.hpp index 3846b52d23..29d1e00adf 100644 --- a/lib/kokkos/core/src/View/MDSpan/Kokkos_MDSpan_Extents.hpp +++ b/lib/kokkos/core/src/View/MDSpan/Kokkos_MDSpan_Extents.hpp @@ -37,9 +37,6 @@ struct ViewDimension; template struct ViewDataType; -} // namespace Kokkos::Impl - -namespace Kokkos::Experimental::Impl { // A few things to note -- // - mdspan allows for 0-rank extents similarly to View, so we don't need @@ -106,6 +103,20 @@ struct DataTypeFromExtents { // Will cause a compile error if it is malformed (i.e. dynamic after static) using type = typename ::Kokkos::Impl::ViewDataType::type; }; -} // namespace Kokkos::Experimental::Impl + +template +constexpr KOKKOS_INLINE_FUNCTION auto extents_from_view_mapping_impl( + const VM &view_mapping, std::index_sequence) { + return Extents{view_mapping.extent(Indices)...}; +} + +template +constexpr KOKKOS_INLINE_FUNCTION auto extents_from_view_mapping( + const VM &view_mapping) { + static_assert(Extents::rank() == VM::Rank); + return extents_from_view_mapping_impl( + view_mapping, std::make_index_sequence{}); +} +} // namespace Kokkos::Impl #endif // KOKKOS_EXPERIMENTAL_MDSPAN_EXTENTS_HPP diff --git a/lib/kokkos/core/src/View/MDSpan/Kokkos_MDSpan_Layout.hpp b/lib/kokkos/core/src/View/MDSpan/Kokkos_MDSpan_Layout.hpp new file mode 100644 index 0000000000..089628137d --- /dev/null +++ b/lib/kokkos/core/src/View/MDSpan/Kokkos_MDSpan_Layout.hpp @@ -0,0 +1,156 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#ifndef KOKKOS_IMPL_PUBLIC_INCLUDE +static_assert(false, + "Including non-public Kokkos header files is not allowed."); +#endif + +#ifndef KOKKOS_EXPERIMENTAL_MDSPAN_LAYOUT_HPP +#define KOKKOS_EXPERIMENTAL_MDSPAN_LAYOUT_HPP + +#include "Kokkos_MDSpan_Extents.hpp" +#include + +namespace Kokkos::Impl { + +template +struct LayoutFromArrayLayout; + +template <> +struct LayoutFromArrayLayout { + using type = Kokkos::Experimental::layout_left_padded; +}; + +template <> +struct LayoutFromArrayLayout { + using type = Kokkos::Experimental::layout_right_padded; +}; + +template <> +struct LayoutFromArrayLayout { + using type = layout_stride; +}; + +template +KOKKOS_INLINE_FUNCTION auto array_layout_from_mapping( + const typename MDSpanType::mapping_type &mapping) { + using mapping_type = typename MDSpanType::mapping_type; + using extents_type = typename mapping_type::extents_type; + + constexpr auto rank = extents_type::rank(); + const auto &ext = mapping.extents(); + + static_assert(rank <= ARRAY_LAYOUT_MAX_RANK, + "Unsupported rank for mdspan (must be <= 8)"); + + if constexpr (std::is_same_v) { + return Kokkos::LayoutStride{ + rank > 0 ? ext.extent(0) : KOKKOS_IMPL_CTOR_DEFAULT_ARG, + rank > 0 ? mapping.stride(0) : 0, + rank > 1 ? ext.extent(1) : KOKKOS_IMPL_CTOR_DEFAULT_ARG, + rank > 1 ? mapping.stride(1) : 0, + rank > 2 ? ext.extent(2) : KOKKOS_IMPL_CTOR_DEFAULT_ARG, + rank > 2 ? mapping.stride(2) : 0, + rank > 3 ? ext.extent(3) : KOKKOS_IMPL_CTOR_DEFAULT_ARG, + rank > 3 ? mapping.stride(3) : 0, + rank > 4 ? ext.extent(4) : KOKKOS_IMPL_CTOR_DEFAULT_ARG, + rank > 4 ? mapping.stride(4) : 0, + rank > 5 ? ext.extent(5) : KOKKOS_IMPL_CTOR_DEFAULT_ARG, + rank > 5 ? mapping.stride(5) : 0, + rank > 6 ? ext.extent(6) : KOKKOS_IMPL_CTOR_DEFAULT_ARG, + rank > 6 ? mapping.stride(6) : 0, + rank > 7 ? ext.extent(7) : KOKKOS_IMPL_CTOR_DEFAULT_ARG, + rank > 7 ? mapping.stride(7) : 0, + }; + } else { + // FIXME: Kokkos Layouts don't store stride (it's in the mapping) + // We could conceivably fix this by adding an extra ViewCtorProp for + // an abritrary padding. For now we will check for this. + if constexpr (rank > 1 && + (std::is_same_v> || + std::is_same_v>)) { + [[maybe_unused]] constexpr size_t strided_index = + std::is_same_v< + typename mapping_type::layout_type, + Kokkos::Experimental::layout_left_padded> + ? 1 + : rank - 2; + [[maybe_unused]] constexpr size_t extent_index = + std::is_same_v< + typename mapping_type::layout_type, + Kokkos::Experimental::layout_left_padded> + ? 0 + : rank - 1; + KOKKOS_ASSERT(mapping.stride(strided_index) == ext.extent(extent_index)); + } + + return ArrayLayout{rank > 0 ? ext.extent(0) : KOKKOS_IMPL_CTOR_DEFAULT_ARG, + rank > 1 ? ext.extent(1) : KOKKOS_IMPL_CTOR_DEFAULT_ARG, + rank > 2 ? ext.extent(2) : KOKKOS_IMPL_CTOR_DEFAULT_ARG, + rank > 3 ? ext.extent(3) : KOKKOS_IMPL_CTOR_DEFAULT_ARG, + rank > 4 ? ext.extent(4) : KOKKOS_IMPL_CTOR_DEFAULT_ARG, + rank > 5 ? ext.extent(5) : KOKKOS_IMPL_CTOR_DEFAULT_ARG, + rank > 6 ? ext.extent(6) : KOKKOS_IMPL_CTOR_DEFAULT_ARG, + rank > 7 ? ext.extent(7) : KOKKOS_IMPL_CTOR_DEFAULT_ARG}; + } +#ifdef KOKKOS_COMPILER_INTEL + __builtin_unreachable(); +#endif +} + +template +KOKKOS_INLINE_FUNCTION auto mapping_from_view_mapping(const VM &view_mapping) { + using mapping_type = typename MDSpanType::mapping_type; + using extents_type = typename mapping_type::extents_type; + + // std::span is not available in C++17 (our current requirements), + // so we need to use the std::array constructor for layout mappings. + // FIXME When C++20 is available, we can use std::span here instead + std::size_t strides[VM::Rank]; + view_mapping.stride_fill(&strides[0]); + if constexpr (std::is_same_v) { + return mapping_type(Kokkos::mdspan_non_standard, + extents_from_view_mapping(view_mapping), + strides); + } else if constexpr (VM::Rank > 1 && + std::is_same_v>) { + return mapping_type(extents_from_view_mapping(view_mapping), + strides[1]); + } else if constexpr (VM::Rank > 1 && + std::is_same_v>) { + return mapping_type(extents_from_view_mapping(view_mapping), + strides[VM::Rank - 2]); + } else { + return mapping_type(extents_from_view_mapping(view_mapping)); + } +#ifdef KOKKOS_COMPILER_INTEL + __builtin_unreachable(); +#endif +} + +} // namespace Kokkos::Impl + +#endif // KOKKOS_EXPERIMENTAL_MDSPAN_LAYOUT_HPP diff --git a/lib/kokkos/core/src/decl/Kokkos_Declare_SYCL.hpp b/lib/kokkos/core/src/decl/Kokkos_Declare_SYCL.hpp index bd12c5c6a9..d13c90825c 100644 --- a/lib/kokkos/core/src/decl/Kokkos_Declare_SYCL.hpp +++ b/lib/kokkos/core/src/decl/Kokkos_Declare_SYCL.hpp @@ -19,6 +19,9 @@ #if defined(KOKKOS_ENABLE_SYCL) #include +#ifdef SYCL_EXT_ONEAPI_GRAPH +#include +#endif #include #include #include diff --git a/lib/kokkos/core/src/impl/Kokkos_Core.cpp b/lib/kokkos/core/src/impl/Kokkos_Core.cpp index c7addbe337..6f862718bc 100644 --- a/lib/kokkos/core/src/impl/Kokkos_Core.cpp +++ b/lib/kokkos/core/src/impl/Kokkos_Core.cpp @@ -91,6 +91,7 @@ void combine(Kokkos::InitializationSettings& out, KOKKOS_IMPL_COMBINE_SETTING(map_device_id_by); KOKKOS_IMPL_COMBINE_SETTING(device_id); KOKKOS_IMPL_COMBINE_SETTING(disable_warnings); + KOKKOS_IMPL_COMBINE_SETTING(print_configuration); KOKKOS_IMPL_COMBINE_SETTING(tune_internals); KOKKOS_IMPL_COMBINE_SETTING(tools_help); KOKKOS_IMPL_COMBINE_SETTING(tools_libs); @@ -610,6 +611,7 @@ void pre_initialize_internal(const Kokkos::InitializationSettings& settings) { #else declare_configuration_metadata("options", "KOKKOS_ENABLE_LIBDL", "no"); #endif + declare_configuration_metadata("architecture", "Default Device", typeid(Kokkos::DefaultExecutionSpace).name()); @@ -750,9 +752,6 @@ void pre_initialize_internal(const Kokkos::InitializationSettings& settings) { #elif defined(KOKKOS_ARCH_AMD_GFX1100) declare_configuration_metadata("architecture", "GPU architecture", "AMD_GFX1100"); -#elif defined(KOKKOS_ARCH_AMD_GFX1103) - declare_configuration_metadata("architecture", "GPU architecture", - "AMD_GFX1103"); #else declare_configuration_metadata("architecture", "GPU architecture", "none"); @@ -788,34 +787,18 @@ void initialize_internal(const Kokkos::InitializationSettings& settings) { post_initialize_internal(settings); } -void pre_finalize_internal() { - typename decltype(finalize_hooks)::size_type numSuccessfulCalls = 0; +// declared noexcept such that std::terminate is called if any of the registered +// function throws +void call_registered_finalize_hook_functions() noexcept { while (!finalize_hooks.empty()) { - auto f = finalize_hooks.top(); - try { - f(); - } catch (...) { - std::cerr << "Kokkos::finalize: A finalize hook (set via " - "Kokkos::push_finalize_hook) threw an exception that it did " - "not catch." - " Per std::atexit rules, this results in std::terminate. " - "This is " - "finalize hook number " - << numSuccessfulCalls - << " (1-based indexing) " - "out of " - << finalize_hooks.size() - << " to call. Remember that " - "Kokkos::finalize calls finalize hooks in reverse order " - "from how they " - "were pushed." - << std::endl; - std::terminate(); - } + auto const& func = finalize_hooks.top(); + func(); finalize_hooks.pop(); - ++numSuccessfulCalls; } +} +void pre_finalize_internal() { + call_registered_finalize_hook_functions(); Kokkos::Profiling::finalize(); } diff --git a/lib/kokkos/core/src/impl/Kokkos_Default_Graph_Impl.hpp b/lib/kokkos/core/src/impl/Kokkos_Default_Graph_Impl.hpp index 3693dff3d4..05d4854919 100644 --- a/lib/kokkos/core/src/impl/Kokkos_Default_Graph_Impl.hpp +++ b/lib/kokkos/core/src/impl/Kokkos_Default_Graph_Impl.hpp @@ -56,7 +56,7 @@ struct GraphImpl : private ExecutionSpaceInstanceStorage { //---------------------------------------------------------------------------- // {{{2 - // Not moveable or copyable; it spends its whole live as a shared_ptr in the + // Not movable or copyable; it spends its whole live as a shared_ptr in the // Graph object GraphImpl() = default; GraphImpl(GraphImpl const&) = delete; @@ -82,10 +82,7 @@ struct GraphImpl : private ExecutionSpaceInstanceStorage { template // requires NodeImplPtr is a shared_ptr to specialization of GraphNodeImpl void add_node(std::shared_ptr const& arg_node_ptr) { - static_assert( - NodeImpl::kernel_type::Policy::is_graph_kernel::value, - "Something has gone horribly wrong, but it's too complicated to " - "explain here. Buy Daisy a coffee and she'll explain it to you."); + static_assert(NodeImpl::kernel_type::Policy::is_graph_kernel::value); // Since this is always called before any calls to add_predecessor involving // it, we can treat this node as a sink until we discover otherwise. arg_node_ptr->node_details_t::set_kernel(arg_node_ptr->get_kernel()); diff --git a/lib/kokkos/core/src/Kokkos_Atomics_Desul_Config.hpp b/lib/kokkos/core/src/impl/Kokkos_DesulAtomicsConfig.hpp similarity index 72% rename from lib/kokkos/core/src/Kokkos_Atomics_Desul_Config.hpp rename to lib/kokkos/core/src/impl/Kokkos_DesulAtomicsConfig.hpp index 4cf170f5f1..02ab127d5c 100644 --- a/lib/kokkos/core/src/Kokkos_Atomics_Desul_Config.hpp +++ b/lib/kokkos/core/src/impl/Kokkos_DesulAtomicsConfig.hpp @@ -13,15 +13,9 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER -#ifndef KOKKOS_IMPL_PUBLIC_INCLUDE -#include -static_assert(false, - "Including non-public Kokkos header files is not allowed."); -#endif -#ifndef KOKKOS_ATOMICS_DESUL_CONFIG_HPP -#define KOKKOS_ATOMICS_DESUL_CONFIG_HPP -#include +#ifndef KOKKOS_DESUL_ATOMICS_CONFIG_HPP +#define KOKKOS_DESUL_ATOMICS_CONFIG_HPP #if defined(KOKKOS_ARCH_KEPLER) || defined(KOKKOS_ARCH_MAXWELL) #define DESUL_CUDA_ARCH_IS_PRE_PASCAL @@ -32,4 +26,4 @@ static_assert(false, #define DESUL_CUDA_ARCH_IS_PRE_VOLTA #endif -#endif // KOKKOS_ATOMICS_DESUL_CONFIG_HPP +#endif diff --git a/lib/kokkos/core/src/impl/Kokkos_Error.cpp b/lib/kokkos/core/src/impl/Kokkos_Error.cpp index de6e83ed1f..0dcd5d523d 100644 --- a/lib/kokkos/core/src/impl/Kokkos_Error.cpp +++ b/lib/kokkos/core/src/impl/Kokkos_Error.cpp @@ -18,133 +18,54 @@ #define KOKKOS_IMPL_PUBLIC_INCLUDE #endif -#include -#include - #include -#include #include +#include #include #include // show_warnings #include -#include -//---------------------------------------------------------------------------- -//---------------------------------------------------------------------------- - -namespace Kokkos { -namespace Impl { - -void throw_runtime_exception(const std::string &msg) { +void Kokkos::Impl::throw_runtime_exception(const std::string &msg) { throw std::runtime_error(msg); } -void log_warning(const std::string &msg) { +void Kokkos::Impl::throw_bad_alloc(std::string_view memory_space_name, + std::size_t size, std::string_view label) { + std::stringstream ss; + ss << "Kokkos ERROR: " << memory_space_name + << " memory space failed to allocate " << human_memory_size(size) + << " (label=\"" << label << "\")."; + throw std::runtime_error(ss.str()); +} + +void Kokkos::Impl::log_warning(const std::string &msg) { if (show_warnings()) { std::cerr << msg << std::flush; } } -std::string human_memory_size(size_t arg_bytes) { +std::string Kokkos::Impl::human_memory_size(size_t arg_bytes) { double bytes = arg_bytes; const double K = 1024; const double M = K * 1024; const double G = M * 1024; + const double T = G * 1024; std::ostringstream out; if (bytes < K) { out << std::setprecision(4) << bytes << " B"; } else if (bytes < M) { bytes /= K; - out << std::setprecision(4) << bytes << " K"; + out << std::setprecision(4) << bytes << " KiB"; } else if (bytes < G) { bytes /= M; - out << std::setprecision(4) << bytes << " M"; - } else { + out << std::setprecision(4) << bytes << " MiB"; + } else if (bytes < T) { bytes /= G; - out << std::setprecision(4) << bytes << " G"; + out << std::setprecision(4) << bytes << " GiB"; + } else { + bytes /= T; + out << std::setprecision(4) << bytes << " TiB"; } return out.str(); } - -} // namespace Impl - -void Experimental::RawMemoryAllocationFailure::print_error_message( - std::ostream &o) const { - o << "Allocation of size " - << ::Kokkos::Impl::human_memory_size(m_attempted_size); - o << " failed"; - switch (m_failure_mode) { - case FailureMode::OutOfMemoryError: - o << ", likely due to insufficient memory."; - break; - case FailureMode::AllocationNotAligned: - o << " because the allocation was improperly aligned."; - break; - case FailureMode::InvalidAllocationSize: - o << " because the requested allocation size is not a valid size for the" - " requested allocation mechanism (it's probably too large)."; - break; - // TODO move this to the subclass for Cuda-related things - case FailureMode::MaximumCudaUVMAllocationsExceeded: - o << " because the maximum Cuda UVM allocations was exceeded."; - break; - case FailureMode::Unknown: o << " because of an unknown error."; break; - } - o << " (The allocation mechanism was "; - switch (m_mechanism) { - case AllocationMechanism::StdMalloc: o << "standard malloc()."; break; - case AllocationMechanism::CudaMalloc: o << "cudaMalloc()."; break; - case AllocationMechanism::CudaMallocManaged: - o << "cudaMallocManaged()."; - break; - case AllocationMechanism::CudaHostAlloc: o << "cudaHostAlloc()."; break; - case AllocationMechanism::HIPMalloc: o << "hipMalloc()."; break; - case AllocationMechanism::HIPHostMalloc: o << "hipHostMalloc()."; break; - case AllocationMechanism::HIPMallocManaged: - o << "hipMallocManaged()."; - break; - case AllocationMechanism::SYCLMallocDevice: - o << "sycl::malloc_device()."; - break; - case AllocationMechanism::SYCLMallocShared: - o << "sycl::malloc_shared()."; - break; - case AllocationMechanism::SYCLMallocHost: - o << "sycl::malloc_host()."; - break; - default: o << "unsupported."; - } - append_additional_error_information(o); - o << ")" << std::endl; -} - -std::string Experimental::RawMemoryAllocationFailure::get_error_message() - const { - std::ostringstream out; - print_error_message(out); - return out.str(); -} - -} // namespace Kokkos - -//---------------------------------------------------------------------------- -//---------------------------------------------------------------------------- - -namespace Kokkos { - -#ifdef KOKKOS_ENABLE_CUDA -namespace Experimental { - -void CudaRawMemoryAllocationFailure::append_additional_error_information( - std::ostream &o) const { - if (m_error_code != cudaSuccess) { - o << " The Cuda allocation returned the error code \"" - << cudaGetErrorName(m_error_code) << "\"."; - } -} - -} // end namespace Experimental -#endif - -} // namespace Kokkos diff --git a/lib/kokkos/core/src/impl/Kokkos_Error.hpp b/lib/kokkos/core/src/impl/Kokkos_Error.hpp index 1058fd98db..9a80c7b31b 100644 --- a/lib/kokkos/core/src/impl/Kokkos_Error.hpp +++ b/lib/kokkos/core/src/impl/Kokkos_Error.hpp @@ -18,116 +18,19 @@ #define KOKKOS_IMPL_ERROR_HPP #include -#include #include #include #include -namespace Kokkos { -namespace Impl { +namespace Kokkos::Impl { [[noreturn]] void throw_runtime_exception(const std::string &msg); - +[[noreturn]] void throw_bad_alloc(std::string_view memory_space_name, + std::size_t size, std::string_view label); void log_warning(const std::string &msg); -std::string human_memory_size(size_t arg_bytes); +std::string human_memory_size(size_t bytes); -} // namespace Impl +} // namespace Kokkos::Impl -namespace Experimental { - -class RawMemoryAllocationFailure : public std::bad_alloc { - public: - enum class FailureMode { - OutOfMemoryError, - AllocationNotAligned, - InvalidAllocationSize, - MaximumCudaUVMAllocationsExceeded, - Unknown - }; - enum class AllocationMechanism { - StdMalloc, -#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4 - PosixMemAlign KOKKOS_DEPRECATED, - PosixMMap KOKKOS_DEPRECATED, - IntelMMAlloc KOKKOS_DEPRECATED, #endif - CudaMalloc, - CudaMallocManaged, - CudaHostAlloc, - HIPMalloc, - HIPHostMalloc, - HIPMallocManaged, - SYCLMallocDevice, - SYCLMallocShared, - SYCLMallocHost, - OpenACCMalloc, - }; - - private: - size_t m_attempted_size; - size_t m_attempted_alignment; - FailureMode m_failure_mode; - AllocationMechanism m_mechanism; - - public: - RawMemoryAllocationFailure( - size_t arg_attempted_size, size_t arg_attempted_alignment, - FailureMode arg_failure_mode = FailureMode::OutOfMemoryError, - AllocationMechanism arg_mechanism = - AllocationMechanism::StdMalloc) noexcept - : m_attempted_size(arg_attempted_size), - m_attempted_alignment(arg_attempted_alignment), - m_failure_mode(arg_failure_mode), - m_mechanism(arg_mechanism) {} - - RawMemoryAllocationFailure() noexcept = delete; - - RawMemoryAllocationFailure(RawMemoryAllocationFailure const &) noexcept = - default; - RawMemoryAllocationFailure(RawMemoryAllocationFailure &&) noexcept = default; - - RawMemoryAllocationFailure &operator =( - RawMemoryAllocationFailure const &) noexcept = default; - RawMemoryAllocationFailure &operator =( - RawMemoryAllocationFailure &&) noexcept = default; - - ~RawMemoryAllocationFailure() noexcept override = default; - - [[nodiscard]] const char *what() const noexcept override { - if (m_failure_mode == FailureMode::OutOfMemoryError) { - return "Memory allocation error: out of memory"; - } else if (m_failure_mode == FailureMode::AllocationNotAligned) { - return "Memory allocation error: allocation result was under-aligned"; - } - - return nullptr; // unreachable - } - - [[nodiscard]] size_t attempted_size() const noexcept { - return m_attempted_size; - } - - [[nodiscard]] size_t attempted_alignment() const noexcept { - return m_attempted_alignment; - } - - [[nodiscard]] AllocationMechanism allocation_mechanism() const noexcept { - return m_mechanism; - } - - [[nodiscard]] FailureMode failure_mode() const noexcept { - return m_failure_mode; - } - - void print_error_message(std::ostream &o) const; - [[nodiscard]] std::string get_error_message() const; - - virtual void append_additional_error_information(std::ostream &) const {} -}; - -} // end namespace Experimental - -} // namespace Kokkos - -#endif /* #ifndef KOKKOS_IMPL_ERROR_HPP */ diff --git a/lib/kokkos/core/src/impl/Kokkos_HostSpace.cpp b/lib/kokkos/core/src/impl/Kokkos_HostSpace.cpp index 1047b773d7..1c1fb67ff0 100644 --- a/lib/kokkos/core/src/impl/Kokkos_HostSpace.cpp +++ b/lib/kokkos/core/src/impl/Kokkos_HostSpace.cpp @@ -79,22 +79,9 @@ void *HostSpace::impl_allocate( ptr = operator new (arg_alloc_size, std::align_val_t(alignment), std::nothrow_t{}); - if ((ptr == nullptr) || (reinterpret_cast(ptr) == ~uintptr_t(0)) || + if (!ptr || (reinterpret_cast(ptr) == ~uintptr_t(0)) || (reinterpret_cast(ptr) & alignment_mask)) { - Experimental::RawMemoryAllocationFailure::FailureMode failure_mode = - Experimental::RawMemoryAllocationFailure::FailureMode:: - AllocationNotAligned; - if (ptr == nullptr) { - failure_mode = Experimental::RawMemoryAllocationFailure::FailureMode:: - OutOfMemoryError; - } - - Experimental::RawMemoryAllocationFailure::AllocationMechanism alloc_mec = - Experimental::RawMemoryAllocationFailure::AllocationMechanism:: - StdMalloc; - - throw Kokkos::Experimental::RawMemoryAllocationFailure( - arg_alloc_size, alignment, failure_mode, alloc_mec); + Impl::throw_bad_alloc(name(), arg_alloc_size, arg_label); } if (Kokkos::Profiling::profileLibraryLoaded()) { Kokkos::Profiling::allocateData(arg_handle, arg_label, ptr, reported_size); @@ -109,9 +96,8 @@ void HostSpace::deallocate(void *const arg_alloc_ptr, void HostSpace::deallocate(const char *arg_label, void *const arg_alloc_ptr, const size_t arg_alloc_size, - const size_t - - arg_logical_size) const { + const size_t arg_logical_size) const { + if (arg_alloc_ptr) Kokkos::fence("HostSpace::impl_deallocate before free"); impl_deallocate(arg_label, arg_alloc_ptr, arg_alloc_size, arg_logical_size); } void HostSpace::impl_deallocate( @@ -119,7 +105,6 @@ void HostSpace::impl_deallocate( const size_t arg_alloc_size, const size_t arg_logical_size, const Kokkos::Tools::SpaceHandle arg_handle) const { if (arg_alloc_ptr) { - Kokkos::fence("HostSpace::impl_deallocate before free"); size_t reported_size = (arg_logical_size > 0) ? arg_logical_size : arg_alloc_size; if (Kokkos::Profiling::profileLibraryLoaded()) { diff --git a/lib/kokkos/core/src/impl/Kokkos_HostThreadTeam.hpp b/lib/kokkos/core/src/impl/Kokkos_HostThreadTeam.hpp index 25f09b8286..3dc68a187b 100644 --- a/lib/kokkos/core/src/impl/Kokkos_HostThreadTeam.hpp +++ b/lib/kokkos/core/src/impl/Kokkos_HostThreadTeam.hpp @@ -106,7 +106,11 @@ class HostThreadTeamData { public: inline bool team_rendezvous() const noexcept { - int* ptr = reinterpret_cast(m_team_scratch + m_team_rendezvous); + // FIXME_OPENMP The tasking framework creates an instance with + // m_team_scratch == nullptr and m_team_rendezvous != 0: + int* ptr = m_team_scratch == nullptr + ? nullptr + : reinterpret_cast(m_team_scratch + m_team_rendezvous); HostBarrier::split_arrive(ptr, m_team_size, m_team_rendezvous_step); if (m_team_rank != 0) { HostBarrier::wait(ptr, m_team_size, m_team_rendezvous_step); @@ -130,9 +134,13 @@ class HostThreadTeamData { } inline void team_rendezvous_release() const noexcept { + // FIXME_OPENMP The tasking framework creates an instance with + // m_team_scratch == nullptr and m_team_rendezvous != 0: HostBarrier::split_release( - reinterpret_cast(m_team_scratch + m_team_rendezvous), m_team_size, - m_team_rendezvous_step); + (m_team_scratch == nullptr) + ? nullptr + : reinterpret_cast(m_team_scratch + m_team_rendezvous), + m_team_size, m_team_rendezvous_step); } inline int pool_rendezvous() const noexcept { @@ -271,6 +279,9 @@ class HostThreadTeamData { } int64_t* team_shared() const noexcept { + // FIXME_OPENMP The tasking framework creates an instance with + // m_team_scratch == nullptr and m_team_shared != 0 + if (m_team_scratch == nullptr) return nullptr; return m_team_scratch + m_team_shared; } @@ -400,8 +411,12 @@ class HostThreadTeamMember { int const m_league_size; public: + // FIXME_OPENMP The tasking framework creates an instance with + // m_team_scratch == nullptr and m_team_shared != 0: constexpr HostThreadTeamMember(HostThreadTeamData& arg_data) noexcept - : m_scratch(arg_data.team_shared(), arg_data.team_shared_bytes()), + : m_scratch(arg_data.team_shared(), (arg_data.team_shared() == nullptr) + ? 0 + : arg_data.team_shared_bytes()), m_data(arg_data), m_league_rank(arg_data.m_league_rank), m_league_size(arg_data.m_league_size) {} diff --git a/lib/kokkos/core/src/impl/Kokkos_Profiling.cpp b/lib/kokkos/core/src/impl/Kokkos_Profiling.cpp index bc6197753c..0b34653017 100644 --- a/lib/kokkos/core/src/impl/Kokkos_Profiling.cpp +++ b/lib/kokkos/core/src/impl/Kokkos_Profiling.cpp @@ -971,84 +971,6 @@ void set_callbacks(Kokkos::Tools::Experimental::EventSet new_events) { } // namespace Experimental } // namespace Tools -namespace Profiling { -bool profileLibraryLoaded() { return Kokkos::Tools::profileLibraryLoaded(); } - -void beginParallelFor(const std::string& kernelPrefix, const uint32_t devID, - uint64_t* kernelID) { - Kokkos::Tools::beginParallelFor(kernelPrefix, devID, kernelID); -} -void beginParallelReduce(const std::string& kernelPrefix, const uint32_t devID, - uint64_t* kernelID) { - Kokkos::Tools::beginParallelReduce(kernelPrefix, devID, kernelID); -} -void beginParallelScan(const std::string& kernelPrefix, const uint32_t devID, - uint64_t* kernelID) { - Kokkos::Tools::beginParallelScan(kernelPrefix, devID, kernelID); -} -void endParallelFor(const uint64_t kernelID) { - Kokkos::Tools::endParallelFor(kernelID); -} -void endParallelReduce(const uint64_t kernelID) { - Kokkos::Tools::endParallelReduce(kernelID); -} -void endParallelScan(const uint64_t kernelID) { - Kokkos::Tools::endParallelScan(kernelID); -} - -void pushRegion(const std::string& kName) { Kokkos::Tools::pushRegion(kName); } -void popRegion() { Kokkos::Tools::popRegion(); } - -void createProfileSection(const std::string& sectionName, uint32_t* secID) { - Kokkos::Tools::createProfileSection(sectionName, secID); -} -void destroyProfileSection(const uint32_t secID) { - Kokkos::Tools::destroyProfileSection(secID); -} - -void startSection(const uint32_t secID) { Kokkos::Tools::startSection(secID); } - -void stopSection(const uint32_t secID) { Kokkos::Tools::stopSection(secID); } - -void markEvent(const std::string& eventName) { - Kokkos::Tools::markEvent(eventName); -} -void allocateData(const SpaceHandle handle, const std::string name, - const void* data, const uint64_t size) { - Kokkos::Tools::allocateData(handle, name, data, size); -} -void deallocateData(const SpaceHandle space, const std::string label, - const void* ptr, const uint64_t size) { - Kokkos::Tools::deallocateData(space, label, ptr, size); -} - -void beginDeepCopy(const SpaceHandle dst_space, const std::string dst_label, - const void* dst_ptr, const SpaceHandle src_space, - const std::string src_label, const void* src_ptr, - const uint64_t size) { - Kokkos::Tools::beginDeepCopy(dst_space, dst_label, dst_ptr, src_space, - src_label, src_ptr, size); -} -void endDeepCopy() { Kokkos::Tools::endDeepCopy(); } - -void finalize() { Kokkos::Tools::finalize(); } -void initialize(const std::string& profileLibrary) { - Kokkos::Tools::initialize(profileLibrary); -} - -bool printHelp(const std::string& args) { - return Kokkos::Tools::printHelp(args); -} -void parseArgs(const std::string& args) { Kokkos::Tools::parseArgs(args); } -void parseArgs(int _argc, char** _argv) { - Kokkos::Tools::parseArgs(_argc, _argv); -} - -SpaceHandle make_space_handle(const char* space_name) { - return Kokkos::Tools::make_space_handle(space_name); -} -} // namespace Profiling - // Tuning namespace Tools { diff --git a/lib/kokkos/core/src/impl/Kokkos_Profiling.hpp b/lib/kokkos/core/src/impl/Kokkos_Profiling.hpp index 025d8d3d18..01a41d0c3f 100644 --- a/lib/kokkos/core/src/impl/Kokkos_Profiling.hpp +++ b/lib/kokkos/core/src/impl/Kokkos_Profiling.hpp @@ -263,40 +263,41 @@ size_t get_current_context_id(); } // namespace Tools namespace Profiling { -bool profileLibraryLoaded(); +// don't let ClangFormat reorder the using-declarations below +// clang-format off +using Kokkos::Tools::profileLibraryLoaded; -void beginParallelFor(const std::string& kernelPrefix, const uint32_t devID, - uint64_t* kernelID); -void beginParallelReduce(const std::string& kernelPrefix, const uint32_t devID, - uint64_t* kernelID); -void beginParallelScan(const std::string& kernelPrefix, const uint32_t devID, - uint64_t* kernelID); -void endParallelFor(const uint64_t kernelID); -void endParallelReduce(const uint64_t kernelID); -void endParallelScan(const uint64_t kernelID); -void pushRegion(const std::string& kName); -void popRegion(); +using Kokkos::Tools::printHelp; +using Kokkos::Tools::parseArgs; -void createProfileSection(const std::string& sectionName, uint32_t* secID); -void destroyProfileSection(const uint32_t secID); -void startSection(const uint32_t secID); +using Kokkos::Tools::initialize; +using Kokkos::Tools::finalize; -void stopSection(const uint32_t secID); +using Kokkos::Tools::beginParallelFor; +using Kokkos::Tools::beginParallelReduce; +using Kokkos::Tools::beginParallelScan; +using Kokkos::Tools::endParallelFor; +using Kokkos::Tools::endParallelReduce; +using Kokkos::Tools::endParallelScan; -void markEvent(const std::string& eventName); -void allocateData(const SpaceHandle handle, const std::string name, - const void* data, const uint64_t size); -void deallocateData(const SpaceHandle space, const std::string label, - const void* ptr, const uint64_t size); -void beginDeepCopy(const SpaceHandle dst_space, const std::string dst_label, - const void* dst_ptr, const SpaceHandle src_space, - const std::string src_label, const void* src_ptr, - const uint64_t size); -void endDeepCopy(); -void finalize(); -void initialize(const std::string& = {}); +using Kokkos::Tools::allocateData; +using Kokkos::Tools::deallocateData; -SpaceHandle make_space_handle(const char* space_name); +using Kokkos::Tools::beginDeepCopy; +using Kokkos::Tools::endDeepCopy; + +using Kokkos::Tools::pushRegion; +using Kokkos::Tools::popRegion; + +using Kokkos::Tools::createProfileSection; +using Kokkos::Tools::destroyProfileSection; +using Kokkos::Tools::startSection; +using Kokkos::Tools::stopSection; + +using Kokkos::Tools::markEvent; + +using Kokkos::Tools::make_space_handle; +// clang-format on namespace Experimental { using Kokkos::Tools::Experimental::set_allocate_data_callback; diff --git a/lib/kokkos/core/src/impl/Kokkos_Profiling_C_Interface.h b/lib/kokkos/core/src/impl/Kokkos_Profiling_C_Interface.h index 15c466b27e..8c3194e43b 100644 --- a/lib/kokkos/core/src/impl/Kokkos_Profiling_C_Interface.h +++ b/lib/kokkos/core/src/impl/Kokkos_Profiling_C_Interface.h @@ -32,6 +32,10 @@ // Profiling +#ifdef __cplusplus +extern "C" { +#endif + struct Kokkos_Profiling_KokkosPDeviceInfo { size_t deviceID; }; @@ -267,4 +271,8 @@ struct Kokkos_Profiling_EventSet { // changing struct layout }; +#ifdef __cplusplus +} +#endif + #endif // KOKKOS_PROFILING_C_INTERFACE_HPP diff --git a/lib/kokkos/core/src/impl/Kokkos_SharedAlloc.cpp b/lib/kokkos/core/src/impl/Kokkos_SharedAlloc.cpp index 0bc3814b3a..ccf3c47a1e 100644 --- a/lib/kokkos/core/src/impl/Kokkos_SharedAlloc.cpp +++ b/lib/kokkos/core/src/impl/Kokkos_SharedAlloc.cpp @@ -323,41 +323,6 @@ void SharedAllocationRecord::print_host_accessible_records( } #endif -void safe_throw_allocation_with_header_failure( - std::string const& space_name, std::string const& label, - Kokkos::Experimental::RawMemoryAllocationFailure const& failure) { - auto generate_failure_message = [&](std::ostream& o) { - o << "Kokkos failed to allocate memory for label \"" << label - << "\". Allocation using MemorySpace named \"" << space_name - << "\" failed with the following error: "; - failure.print_error_message(o); - if (failure.failure_mode() == - Kokkos::Experimental::RawMemoryAllocationFailure::FailureMode:: - AllocationNotAligned) { - // TODO: delete the misaligned memory? - o << "Warning: Allocation failed due to misalignment; memory may " - "be leaked.\n"; - } - o.flush(); - }; - try { - std::ostringstream sstr; - generate_failure_message(sstr); - Kokkos::Impl::throw_runtime_exception(sstr.str()); - } catch (std::bad_alloc const&) { - // Probably failed to allocate the string because we're so close to out - // of memory. Try printing to std::cerr instead - try { - generate_failure_message(std::cerr); - } catch (std::bad_alloc const&) { - // oh well, we tried... - } - Kokkos::Impl::throw_runtime_exception( - "Kokkos encountered an allocation failure, then another allocation " - "failure while trying to create the error message."); - } -} - void fill_host_accessible_header_info( SharedAllocationRecord* arg_record, SharedAllocationHeader& arg_header, std::string const& arg_label) { diff --git a/lib/kokkos/core/src/impl/Kokkos_SharedAlloc.hpp b/lib/kokkos/core/src/impl/Kokkos_SharedAlloc.hpp index 99ab660213..da03cc4983 100644 --- a/lib/kokkos/core/src/impl/Kokkos_SharedAlloc.hpp +++ b/lib/kokkos/core/src/impl/Kokkos_SharedAlloc.hpp @@ -196,36 +196,21 @@ class SharedAllocationRecord { const SharedAllocationRecord* const root, const bool detail); }; -void safe_throw_allocation_with_header_failure( - std::string const& space_name, std::string const& label, - Kokkos::Experimental::RawMemoryAllocationFailure const& failure); - template SharedAllocationHeader* checked_allocation_with_header(MemorySpace const& space, std::string const& label, size_t alloc_size) { - try { - return reinterpret_cast(space.allocate( - label.c_str(), alloc_size + sizeof(SharedAllocationHeader), - alloc_size)); - } catch (Kokkos::Experimental::RawMemoryAllocationFailure const& failure) { - safe_throw_allocation_with_header_failure(space.name(), label, failure); - } - return nullptr; // unreachable + return reinterpret_cast(space.allocate( + label.c_str(), alloc_size + sizeof(SharedAllocationHeader), alloc_size)); } template SharedAllocationHeader* checked_allocation_with_header( ExecutionSpace const& exec_space, MemorySpace const& space, std::string const& label, size_t alloc_size) { - try { - return reinterpret_cast(space.allocate( - exec_space, label.c_str(), alloc_size + sizeof(SharedAllocationHeader), - alloc_size)); - } catch (Kokkos::Experimental::RawMemoryAllocationFailure const& failure) { - safe_throw_allocation_with_header_failure(space.name(), label, failure); - } - return nullptr; // unreachable + return reinterpret_cast( + space.allocate(exec_space, label.c_str(), + alloc_size + sizeof(SharedAllocationHeader), alloc_size)); } void fill_host_accessible_header_info(SharedAllocationHeader& arg_header, @@ -385,11 +370,9 @@ SharedAllocationRecord template class Kokkos::Impl::HostInaccessibleSharedAllocationRecordCommon< \ MEMORY_SPACE> -namespace { - /* Taking the address of this function so make sure it is unique */ template -void deallocate(SharedAllocationRecord* record_ptr) { +inline void deallocate(SharedAllocationRecord* record_ptr) { using base_type = SharedAllocationRecord; using this_type = SharedAllocationRecord; @@ -401,8 +384,6 @@ void deallocate(SharedAllocationRecord* record_ptr) { delete ptr; } -} // namespace - /* * Memory space specialization of SharedAllocationRecord< Space , void > * requires : @@ -487,15 +468,21 @@ union SharedAllocationTracker { // pressure on compiler optimization by reducing // number of symbols and inline functions. -#define KOKKOS_IMPL_SHARED_ALLOCATION_TRACKER_INCREMENT \ - KOKKOS_IF_ON_HOST((if (!(m_record_bits & DO_NOT_DEREF_FLAG)) { \ - Record::increment(m_record); \ - })) +#ifdef KOKKOS_ENABLE_IMPL_REF_COUNT_BRANCH_UNLIKELY +#define KOKKOS_IMPL_BRANCH_PROB KOKKOS_IMPL_ATTRIBUTE_UNLIKELY +#else +#define KOKKOS_IMPL_BRANCH_PROB +#endif -#define KOKKOS_IMPL_SHARED_ALLOCATION_TRACKER_DECREMENT \ - KOKKOS_IF_ON_HOST((if (!(m_record_bits & DO_NOT_DEREF_FLAG)) { \ - Record::decrement(m_record); \ - })) +#define KOKKOS_IMPL_SHARED_ALLOCATION_TRACKER_INCREMENT \ + KOKKOS_IF_ON_HOST( \ + (if (!(m_record_bits & DO_NOT_DEREF_FLAG)) \ + KOKKOS_IMPL_BRANCH_PROB { Record::increment(m_record); })) + +#define KOKKOS_IMPL_SHARED_ALLOCATION_TRACKER_DECREMENT \ + KOKKOS_IF_ON_HOST( \ + (if (!(m_record_bits & DO_NOT_DEREF_FLAG)) \ + KOKKOS_IMPL_BRANCH_PROB { Record::decrement(m_record); })) #define KOKKOS_IMPL_SHARED_ALLOCATION_CARRY_RECORD_BITS(rhs, \ override_tracking) \ @@ -642,8 +629,41 @@ union SharedAllocationTracker { #undef KOKKOS_IMPL_SHARED_ALLOCATION_TRACKER_INCREMENT #undef KOKKOS_IMPL_SHARED_ALLOCATION_TRACKER_DECREMENT +#undef KOKKOS_IMPL_BRANCH_PROB }; +struct SharedAllocationDisableTrackingGuard { + SharedAllocationDisableTrackingGuard() { + KOKKOS_ASSERT( + (Kokkos::Impl::SharedAllocationRecord::tracking_enabled())); + Kokkos::Impl::SharedAllocationRecord::tracking_disable(); + } + + SharedAllocationDisableTrackingGuard( + const SharedAllocationDisableTrackingGuard&) = delete; + SharedAllocationDisableTrackingGuard(SharedAllocationDisableTrackingGuard&&) = + delete; + + ~SharedAllocationDisableTrackingGuard() { + KOKKOS_ASSERT(( + !Kokkos::Impl::SharedAllocationRecord::tracking_enabled())); + Kokkos::Impl::SharedAllocationRecord::tracking_enable(); + } + // clang-format off + // The old version of clang format we use is particularly egregious here + SharedAllocationDisableTrackingGuard& operator=( + const SharedAllocationDisableTrackingGuard&) = delete; + SharedAllocationDisableTrackingGuard& operator=( + SharedAllocationDisableTrackingGuard&&) = delete; + // clang-format on +}; + +template +inline FunctorType construct_with_shared_allocation_tracking_disabled( + Args&&... args) { + [[maybe_unused]] auto guard = SharedAllocationDisableTrackingGuard{}; + return {std::forward(args)...}; +} } /* namespace Impl */ } /* namespace Kokkos */ #endif diff --git a/lib/kokkos/core/src/impl/Kokkos_ViewArray.hpp b/lib/kokkos/core/src/impl/Kokkos_ViewArray.hpp deleted file mode 100644 index fe43b63018..0000000000 --- a/lib/kokkos/core/src/impl/Kokkos_ViewArray.hpp +++ /dev/null @@ -1,622 +0,0 @@ -//@HEADER -// ************************************************************************ -// -// Kokkos v. 4.0 -// Copyright (2022) National Technology & Engineering -// Solutions of Sandia, LLC (NTESS). -// -// Under the terms of Contract DE-NA0003525 with NTESS, -// the U.S. Government retains certain rights in this software. -// -// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. -// See https://kokkos.org/LICENSE for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//@HEADER - -#ifndef KOKKOS_EXPERIMENTAL_VIEW_ARRAY_MAPPING_HPP -#define KOKKOS_EXPERIMENTAL_VIEW_ARRAY_MAPPING_HPP - -#include - -namespace Kokkos { -namespace Impl { - -template -struct ViewDataAnalysis> { - private: - using array_analysis = ViewArrayAnalysis; - - static_assert(std::is_void