From fdc75dcf35f1a2331cc0cd7568b45564d5841a02 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 26 Jul 2021 18:32:06 -0400 Subject: [PATCH 1/4] add lammps_gather_bonds() function to the C library interface --- doc/src/Library_scatter.rst | 5 ++ src/library.cpp | 107 ++++++++++++++++++++++++++++++++++++ src/library.h | 2 + tools/swig/lammps.i | 2 + 4 files changed, 116 insertions(+) diff --git a/doc/src/Library_scatter.rst b/doc/src/Library_scatter.rst index b45f45f6fb..a01610bdce 100644 --- a/doc/src/Library_scatter.rst +++ b/doc/src/Library_scatter.rst @@ -17,6 +17,7 @@ It documents the following functions: - :cpp:func:`lammps_gather_atoms_subset` - :cpp:func:`lammps_scatter_atoms` - :cpp:func:`lammps_scatter_atoms_subset` +- :cpp:func:`lammps_gather_bonds` - :cpp:func:`lammps_gather` - :cpp:func:`lammps_gather_concat` - :cpp:func:`lammps_gather_subset` @@ -51,6 +52,10 @@ It documents the following functions: ----------------------- +.. doxygenfunction:: lammps_gather_bonds + :project: progguide + +----------------------- .. doxygenfunction:: lammps_gather :project: progguide diff --git a/src/library.cpp b/src/library.cpp index 4847c705af..d745e05755 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -2686,6 +2686,113 @@ void lammps_scatter_atoms_subset(void *handle, char *name, int type, int count, END_CAPTURE } +/** Gather type and constituent atom info for all bonds + * +\verbatim embed:rst + +This function copies the list of all bonds into a buffer provided by +the calling code. The buffer will be filled with bond type, bond atom 1, +bond atom 2 for each bond. Thus the buffer has to be allocated to the +dimension of 3 times the **total** number of bonds times the size of +the LAMMPS "tagint" type, which is either 4 or 8 bytes depending on + whether they are stored in 32-bit or 64-bit integers, respectively. +This size depends on the compile time settings used when compiling +the LAMMPS library and can be queried by calling + :cpp:func:`lammps_extract_setting()` with the keyword "tagint". + +When running in parallel, the data buffer must be allocated on **all** +MPI ranks and will be filled with the information for **all** bonds +in the system. + +Below is a brief C code demonstrating accessing this collected bond information. + +.. code-block:: c + + #include + #include + #include + #include "library.h" + + int main(int argc, char **argv) + { + int tagintsize; + int64_t i, nbonds; + void *handle, *bonds; + + handle = lammps_open_no_mpi(0, NULL, NULL); + lammps_file(handle, "in.some_input"); + + tagintsize = lammps_extract_setting(handle, "tagint"); + if (tagintsize == 4) + nbonds = *(int32_t *)lammps_extract_global(handle, "nbonds"); + else + nbonds = *(int64_t *)lammps_extract_global(handle, "nbonds"); + bonds = malloc(nbonds * 3 * tagintsize); + + lammps_gather_bonds(handle, bonds); + + if (lammps_extract_setting(handle, "world_rank") == 0) { + if (tagintsize == 4) { + int32_t *bonds_real = (int32_t *)bonds; + for (i = 0; i < nbonds; ++i) { + printf("bond % 4ld: type = %d, atoms: % 4d % 4d\n",i, + bonds_real[3*i], bonds_real[3*i+1], bonds_real[3*i+2]); + } + } else { + int64_t *bonds_real = (int64_t *)bonds; + for (i = 0; i < nbonds; ++i) { + printf("bond % 4ld: type = %ld, atoms: % 4ld % 4ld\n",i, + bonds_real[3*i], bonds_real[3*i+1], bonds_real[3*i+2]); + } + } + } + + lammps_close(handle); + lammps_mpi_finalize(); + free(bonds); + return 0; + } + +\endverbatim + * + * \param handle pointer to a previously created LAMMPS instance + * \param data pointer to data to copy the result to */ + +void lammps_gather_bonds(void *handle, void *data) +{ + LAMMPS *lmp = (LAMMPS *)handle; + BEGIN_CAPTURE { + void *val = lammps_extract_global(handle,"nbonds"); + bigint nbonds = *(bigint *)val; + + // no bonds + if (nbonds == 0) return; + + // count per MPI rank bonds, determine offsets and allocate local buffers + int localbonds = lmp->atom->avec->pack_bond(nullptr); + int nprocs = lmp->comm->nprocs; + int *bufsizes = new int[nprocs]; + int *bufoffsets = new int[nprocs]; + MPI_Allgather(&localbonds, 1, MPI_INT, bufsizes, 1, MPI_INT, lmp->world); + bufoffsets[0] = 0; + bufsizes[0] *= 3; // 3 items per bond: type, atom1, atom2 + for (int i = 1; i < nprocs; ++i) { + bufoffsets[i] = bufoffsets[i-1] + bufsizes[i-1]; + bufsizes[i] *= 3; // 3 items per bond: type, atom1, atom2 + } + + tagint **bonds; + lmp->memory->create(bonds, localbonds, 3, "library:gather_bonds:localbonds"); + lmp->atom->avec->pack_bond(bonds); + MPI_Allgatherv(&bonds[0][0], 3*localbonds, MPI_LMP_TAGINT, data, bufsizes, + bufoffsets, MPI_LMP_TAGINT, lmp->world); + lmp->memory->destroy(bonds); + delete[] bufsizes; + delete[] bufoffsets; + } + END_CAPTURE +} + /* ---------------------------------------------------------------------- Contributing author: Thomas Swinburne (CNRS & CINaM, Marseille, France) gather the named atom-based entity for all atoms diff --git a/src/library.h b/src/library.h index 654eda38fa..91146e8526 100644 --- a/src/library.h +++ b/src/library.h @@ -152,6 +152,8 @@ void lammps_scatter_atoms(void *handle, char *name, int type, int count, void *d void lammps_scatter_atoms_subset(void *handle, char *name, int type, int count, int ndata, int *ids, void *data); +void lammps_gather_bonds(void *handle, void *data); + void lammps_gather(void *handle, char *name, int type, int count, void *data); void lammps_gather_concat(void *handle, char *name, int type, int count, void *data); void lammps_gather_subset(void *handle, char *name, int type, int count, int ndata, int *ids, diff --git a/tools/swig/lammps.i b/tools/swig/lammps.i index 5bf47f2463..2767e4c068 100644 --- a/tools/swig/lammps.i +++ b/tools/swig/lammps.i @@ -91,6 +91,7 @@ extern void lammps_gather_atoms_concat(void *, char *, int, int, void *); extern void lammps_gather_atoms_subset(void *, char *, int, int, int, int *, void *); extern void lammps_scatter_atoms(void *, char *, int, int, void *); extern void lammps_scatter_atoms_subset(void *, char *, int, int, int, int *, void *); +extern void lammps_gather_bonds(void *handle, void *data); extern void lammps_gather(void *, char *, int, int, void *); extern void lammps_gather_concat(void *, char *, int, int, void *); extern void lammps_gather_subset(void *, char *, int, int, int, int *, void *); @@ -225,6 +226,7 @@ extern void lammps_gather_atoms_concat(void *, char *, int, int, void *); extern void lammps_gather_atoms_subset(void *, char *, int, int, int, int *, void *); extern void lammps_scatter_atoms(void *, char *, int, int, void *); extern void lammps_scatter_atoms_subset(void *, char *, int, int, int, int *, void *); +extern void lammps_gather_bonds(void *handle, void *data); extern void lammps_gather(void *, char *, int, int, void *); extern void lammps_gather_concat(void *, char *, int, int, void *); extern void lammps_gather_subset(void *, char *, int, int, int, int *, void *); From cc87e7eee3ba9c842ff8166971183daedd317f08 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 26 Jul 2021 20:59:14 -0400 Subject: [PATCH 2/4] add unit test for c-library interface function lammps_gather_bonds() --- unittest/c-library/CMakeLists.txt | 6 + unittest/c-library/test_library_external.cpp | 2 +- .../c-library/test_library_scatter_gather.cpp | 133 ++++++++++++++++++ 3 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 unittest/c-library/test_library_scatter_gather.cpp diff --git a/unittest/c-library/CMakeLists.txt b/unittest/c-library/CMakeLists.txt index b01cd64677..ee7f323c0f 100644 --- a/unittest/c-library/CMakeLists.txt +++ b/unittest/c-library/CMakeLists.txt @@ -17,6 +17,12 @@ target_compile_definitions(test_library_properties PRIVATE -DTEST_INPUT_FOLDER=$ add_test(LibraryProperties test_library_properties) set_tests_properties(LibraryProperties PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR}") +add_executable(test_library_scatter_gather test_library_scatter_gather.cpp test_main.cpp) +target_link_libraries(test_library_scatter_gather PRIVATE lammps GTest::GTest GTest::GMock) +target_compile_definitions(test_library_scatter_gather PRIVATE -DTEST_INPUT_FOLDER=${CMAKE_CURRENT_SOURCE_DIR}) +add_test(LibraryScatterGather test_library_scatter_gather) +set_tests_properties(LibraryScatterGather PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR}") + set(TEST_CONFIG_DEFS "-DTEST_INPUT_FOLDER=${CMAKE_CURRENT_SOURCE_DIR};-DLAMMPS_${LAMMPS_SIZES}") set(PKG_COUNT 0) foreach(PKG ${STANDARD_PACKAGES} ${SUFFIX_PACKAGES}) diff --git a/unittest/c-library/test_library_external.cpp b/unittest/c-library/test_library_external.cpp index 005b31fcab..15edd5f113 100644 --- a/unittest/c-library/test_library_external.cpp +++ b/unittest/c-library/test_library_external.cpp @@ -1,4 +1,4 @@ -// unit tests creating LAMMPS instances via the library interface +// unit tests for interfacing with fix external via the library interface #include "library.h" diff --git a/unittest/c-library/test_library_scatter_gather.cpp b/unittest/c-library/test_library_scatter_gather.cpp new file mode 100644 index 0000000000..34022f617e --- /dev/null +++ b/unittest/c-library/test_library_scatter_gather.cpp @@ -0,0 +1,133 @@ +// unit tests for testing scatter/gather operations through the library interface + +#include "lammps.h" +#include "library.h" +#include "lmptype.h" +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "test_main.h" + +#define STRINGIFY(val) XSTR(val) +#define XSTR(val) #val + +using ::LAMMPS_NS::bigint; +using ::LAMMPS_NS::tagint; +using ::testing::HasSubstr; +using ::testing::StartsWith; + +class GatherProperties : public ::testing::Test { +protected: + void *lmp; + std::string INPUT_DIR = STRINGIFY(TEST_INPUT_FOLDER); + + GatherProperties(){}; + ~GatherProperties() override{}; + + void SetUp() override + { + const char *args[] = {"LAMMPS_test", "-log", "none", + "-echo", "screen", "-nocite", + "-var", "input_dir", STRINGIFY(TEST_INPUT_FOLDER)}; + + char **argv = (char **)args; + int argc = sizeof(args) / sizeof(char *); + + ::testing::internal::CaptureStdout(); + lmp = lammps_open_no_mpi(argc, argv, NULL); + std::string output = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << output; + EXPECT_THAT(output, StartsWith("LAMMPS (")); + } + void TearDown() override + { + ::testing::internal::CaptureStdout(); + lammps_close(lmp); + std::string output = ::testing::internal::GetCapturedStdout(); + EXPECT_THAT(output, HasSubstr("Total wall time:")); + if (verbose) std::cout << output; + lmp = nullptr; + } +}; + +TEST_F(GatherProperties, gather_bonds_newton_on) +{ + if (!lammps_has_style(lmp, "atom", "full")) GTEST_SKIP(); + std::string input = INPUT_DIR + PATH_SEP + "in.fourmol"; + if (!verbose) ::testing::internal::CaptureStdout(); + lammps_command(lmp, "newton on on"); + lammps_file(lmp, input.c_str()); + if (!verbose) ::testing::internal::GetCapturedStdout(); + + bigint nbonds = *(bigint *)lammps_extract_global(lmp, "nbonds"); + EXPECT_EQ(nbonds, 24); + + tagint *bonds = new tagint[3 * nbonds]; + lammps_gather_bonds(lmp, bonds); + +#define CHECK_BOND(idx, type, atom1, atom2) \ + if (((bonds[3 * idx + 1] == atom1) && (bonds[3 * idx + 2] == atom2)) || \ + ((bonds[3 * idx + 1] == atom2) && (bonds[3 * idx + 2] == atom1))) { \ + EXPECT_EQ(bonds[3 * idx], type); \ + ++count; \ + } + + // check validity of a few bonds by comparing the bond type and counting the matches. + int count = 0; + for (bigint i = 0; i < nbonds; ++i) { + CHECK_BOND(i, 5, 1, 2); + CHECK_BOND(i, 3, 1, 3); + CHECK_BOND(i, 2, 3, 4); + CHECK_BOND(i, 2, 3, 5); + CHECK_BOND(i, 1, 3, 6); + CHECK_BOND(i, 3, 6, 8); + CHECK_BOND(i, 4, 6, 7); + CHECK_BOND(i, 5, 8, 9); + CHECK_BOND(i, 5, 27, 28); + CHECK_BOND(i, 5, 27, 29); + } + EXPECT_EQ(count, 10); + delete[] bonds; +}; + +TEST_F(GatherProperties, gather_bonds_newton_off) +{ + if (!lammps_has_style(lmp, "atom", "full")) GTEST_SKIP(); + std::string input = INPUT_DIR + PATH_SEP + "in.fourmol"; + if (!verbose) ::testing::internal::CaptureStdout(); + lammps_command(lmp, "newton off off"); + lammps_file(lmp, input.c_str()); + if (!verbose) ::testing::internal::GetCapturedStdout(); + + bigint nbonds = *(bigint *)lammps_extract_global(lmp, "nbonds"); + EXPECT_EQ(nbonds, 24); + + tagint *bonds = new tagint[3 * nbonds]; + lammps_gather_bonds(lmp, bonds); + +#define CHECK_BOND(idx, type, atom1, atom2) \ + if (((bonds[3 * idx + 1] == atom1) && (bonds[3 * idx + 2] == atom2)) || \ + ((bonds[3 * idx + 1] == atom2) && (bonds[3 * idx + 2] == atom1))) { \ + EXPECT_EQ(bonds[3 * idx], type); \ + ++count; \ + } + + // check validity of a few bonds by comparing the bond type and counting the matches. + int count = 0; + for (bigint i = 0; i < nbonds; ++i) { + CHECK_BOND(i, 5, 1, 2); + CHECK_BOND(i, 3, 1, 3); + CHECK_BOND(i, 2, 3, 4); + CHECK_BOND(i, 2, 3, 5); + CHECK_BOND(i, 1, 3, 6); + CHECK_BOND(i, 3, 6, 8); + CHECK_BOND(i, 4, 6, 7); + CHECK_BOND(i, 5, 8, 9); + CHECK_BOND(i, 5, 27, 28); + CHECK_BOND(i, 5, 27, 29); + } + EXPECT_EQ(count, 10); + delete[] bonds; +}; From 458253b452aff5a906de890bcf775a868b6a4b5c Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 26 Jul 2021 22:36:00 -0400 Subject: [PATCH 3/4] add python interface with optional numpy support for lammps_gather_bonds() unit tests are included --- doc/src/Library_scatter.rst | 1 + python/lammps/core.py | 29 ++++ python/lammps/numpy_wrapper.py | 18 ++ src/library.cpp | 6 +- unittest/python/CMakeLists.txt | 7 + unittest/python/data.fourmol | 210 +++++++++++++++++++++++ unittest/python/in.fourmol | 25 +++ unittest/python/python-numpy.py | 63 +++++++ unittest/python/python-scatter-gather.py | 90 ++++++++++ 9 files changed, 447 insertions(+), 2 deletions(-) create mode 100644 unittest/python/data.fourmol create mode 100644 unittest/python/in.fourmol create mode 100644 unittest/python/python-scatter-gather.py diff --git a/doc/src/Library_scatter.rst b/doc/src/Library_scatter.rst index a01610bdce..8380ef91bd 100644 --- a/doc/src/Library_scatter.rst +++ b/doc/src/Library_scatter.rst @@ -56,6 +56,7 @@ It documents the following functions: :project: progguide ----------------------- + .. doxygenfunction:: lammps_gather :project: progguide diff --git a/python/lammps/core.py b/python/lammps/core.py index d981243503..03378f471d 100644 --- a/python/lammps/core.py +++ b/python/lammps/core.py @@ -201,6 +201,9 @@ class lammps(object): [c_void_p,c_char_p,c_int,c_int,c_int,POINTER(c_int),c_void_p] self.lib.lammps_scatter_atoms_subset.restype = None + self.lib.lammps_gather_bonds.argtypes = [c_void_p,c_void_p] + self.lib.lammps_gather_bonds.restype = None + self.lib.lammps_gather.argtypes = \ [c_void_p,c_char_p,c_int,c_int,c_void_p] self.lib.lammps_gather.restype = None @@ -1206,6 +1209,32 @@ class lammps(object): with ExceptionCheck(self): self.lib.lammps_scatter_atoms_subset(self.lmp,name,dtype,count,ndata,ids,data) + + # ------------------------------------------------------------------------- + + def gather_bonds(self): + """Retrieve global list of bonds + + This is a wrapper around the :cpp:func:`lammps_gather_bonds` + function of the C-library interface. + + This function returns a tuple with the number of bonds and a + flat list of ctypes integer values with the bond type, bond atom1, + bond atom2 for each bond. + + .. versionadded:: 28Jul2021 + + :return: a tuple with the number of bonds and a list of c_int or c_long + :rtype: (int, 3*nbonds*c_tagint) + """ + nbonds = self.extract_global("nbonds") + with ExceptionCheck(self): + data = ((3*nbonds)*self.c_tagint)() + self.lib.lammps_gather_bonds(self.lmp,data) + return nbonds,data + + # ------------------------------------------------------------------------- + # return vector of atom/compute/fix properties gathered across procs # 3 variants to match src/library.cpp # name = atom property recognized by LAMMPS in atom->extract() diff --git a/python/lammps/numpy_wrapper.py b/python/lammps/numpy_wrapper.py index 6f4503a9c8..a6cde93b95 100644 --- a/python/lammps/numpy_wrapper.py +++ b/python/lammps/numpy_wrapper.py @@ -248,6 +248,24 @@ class numpy_wrapper: # ------------------------------------------------------------------------- + def gather_bonds(self): + """Retrieve global list of bonds as NumPy array + + This is a wrapper around :py:meth:`lammps.gather_bonds() ` + It behaves the same as the original method, but returns a NumPy array instead + of a ``ctypes`` list. + + .. versionadded:: 28Jul2021 + + :return: the requested data as a 2d-integer numpy array + :rtype: numpy.array(nbonds,3) + """ + import numpy as np + nbonds, value = self.lmp.gather_bonds() + return np.ctypeslib.as_array(value).reshape(nbonds,3) + + # ------------------------------------------------------------------------- + def fix_external_get_force(self, fix_id): """Get access to the array with per-atom forces of a fix external instance with a given fix ID. diff --git a/src/library.cpp b/src/library.cpp index d745e05755..5cb101d6a4 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -2695,15 +2695,17 @@ the calling code. The buffer will be filled with bond type, bond atom 1, bond atom 2 for each bond. Thus the buffer has to be allocated to the dimension of 3 times the **total** number of bonds times the size of the LAMMPS "tagint" type, which is either 4 or 8 bytes depending on - whether they are stored in 32-bit or 64-bit integers, respectively. +whether they are stored in 32-bit or 64-bit integers, respectively. This size depends on the compile time settings used when compiling the LAMMPS library and can be queried by calling - :cpp:func:`lammps_extract_setting()` with the keyword "tagint". +:cpp:func:`lammps_extract_setting()` with the keyword "tagint". When running in parallel, the data buffer must be allocated on **all** MPI ranks and will be filled with the information for **all** bonds in the system. +.. versionadded:: 28Jul2021 + Below is a brief C code demonstrating accessing this collected bond information. .. code-block:: c diff --git a/unittest/python/CMakeLists.txt b/unittest/python/CMakeLists.txt index 9c9b7832ad..6832f7e028 100644 --- a/unittest/python/CMakeLists.txt +++ b/unittest/python/CMakeLists.txt @@ -29,6 +29,7 @@ if(Python_EXECUTABLE) # prepare to augment the environment so that the LAMMPS python module and the shared library is found. set(PYTHON_TEST_ENVIRONMENT PYTHONPATH=${LAMMPS_PYTHON_DIR}:$ENV{PYTHONPATH}) list(APPEND PYTHON_TEST_ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR}") + list(APPEND PYTHON_TEST_ENVIRONMENT "TEST_INPUT_DIR=${CMAKE_CURRENT_SOURCE_DIR}") if(APPLE) list(APPEND PYTHON_TEST_ENVIRONMENT "DYLD_LIBRARY_PATH=${CMAKE_BINARY_DIR}:$ENV{DYLD_LIBRARY_PATH};LAMMPS_CMAKE_CACHE=${CMAKE_BINARY_DIR}/CMakeCache.txt") else() @@ -90,6 +91,12 @@ if(Python_EXECUTABLE) COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-fix-external.py -v WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) set_tests_properties(PythonFixExternal PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + + add_test(NAME PythonScatterGather + COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-scatter-gather.py -v + WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) + set_tests_properties(PythonScatterGather PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + else() message(STATUS "Skipping Tests for the LAMMPS Python Module: no suitable Python interpreter") endif() diff --git a/unittest/python/data.fourmol b/unittest/python/data.fourmol new file mode 100644 index 0000000000..386ad81c49 --- /dev/null +++ b/unittest/python/data.fourmol @@ -0,0 +1,210 @@ +LAMMPS data file via write_data, version 5 May 2020, timestep = 0 + +29 atoms +5 atom types +24 bonds +5 bond types +30 angles +4 angle types +31 dihedrals +5 dihedral types +2 impropers +2 improper types + + -6.024572 8.975428 xlo xhi + -7.692866 7.307134 ylo yhi + -8.086924 6.913076 zlo zhi + +Masses + +1 12.0107 +2 4.00794 +3 14.0067 +4 15.9994 +5 15.9994 + +Pair Coeffs # zero + +1 +2 +3 +4 +5 + +Bond Coeffs # zero + +1 1.5 +2 1.1 +3 1.3 +4 1.2 +5 1 + +Angle Coeffs # zero + +1 110.1 +2 111 +3 120 +4 108.5 + +Atoms # full + +10 2 1 7.0000000000000007e-02 2.0185283555536988e+00 -1.4283966846517357e+00 -9.6733527271133024e-01 0 0 0 +11 2 2 8.9999999999999997e-02 1.7929780509347666e+00 -1.9871047540768743e+00 -1.8840626643185674e+00 0 0 0 +12 2 1 -2.7000000000000002e-01 3.0030247876861225e+00 -4.8923319967572748e-01 -1.6188658531537248e+00 0 0 0 +13 2 2 8.9999999999999997e-02 4.0447273787895934e+00 -9.0131998547446246e-01 -1.6384447268320836e+00 0 0 0 +14 2 2 8.9999999999999997e-02 2.6033152817257075e+00 -4.0789761505963579e-01 -2.6554413538823063e+00 0 0 0 +2 1 2 3.1000000000000000e-01 3.0197083955402204e-01 2.9515239068888608e+00 -8.5689735572907566e-01 0 0 0 +3 1 1 -2.0000000000000000e-02 -6.9435377880558602e-01 1.2440473127136711e+00 -6.2233801468892025e-01 0 0 0 +4 1 2 8.9999999999999997e-02 -1.5771614164685133e+00 1.4915333140468066e+00 -1.2487126845040522e+00 0 0 0 +6 1 1 5.1000000000000001e-01 2.9412607937706009e-01 2.2719282656652909e-01 -1.2843094067857870e+00 0 0 0 +7 1 4 -5.1000000000000001e-01 3.4019871062879609e-01 -9.1277350075786561e-03 -2.4633113224304561e+00 0 0 0 +19 3 2 4.2359999999999998e-01 1.5349125211132961e+00 2.6315969880333707e+00 -4.2472859440220647e+00 0 0 0 +15 2 2 8.9999999999999997e-02 2.9756315249791303e+00 5.6334269722969288e-01 -1.2437650754599008e+00 0 0 0 +18 3 4 -8.4719999999999995e-01 2.1384791188033843e+00 3.0177261773770208e+00 -3.5160827596876225e+00 0 0 0 +20 3 2 4.2359999999999998e-01 2.7641167828863153e+00 3.6833419064000221e+00 -3.9380850623312638e+00 0 0 0 +8 2 3 -4.6999999999999997e-01 1.1641187171852805e+00 -4.8375305955385234e-01 -6.7659823767368688e-01 0 0 0 +9 2 2 3.1000000000000000e-01 1.3777459838125838e+00 -2.5366338669522998e-01 2.6877644730326306e-01 0 0 0 +16 2 1 5.1000000000000001e-01 2.6517554244980306e+00 -2.3957110424978438e+00 3.2908335999178327e-02 0 0 0 +17 2 4 -5.1000000000000001e-01 2.2309964792710639e+00 -2.1022918943319384e+00 1.1491948328949437e+00 0 0 0 +1 1 3 -4.6999999999999997e-01 -2.7993683669226832e-01 2.4726588069312840e+00 -1.7200860244148433e-01 0 0 0 +5 1 2 8.9999999999999997e-02 -8.9501761359359255e-01 9.3568128743071344e-01 4.0227731871484346e-01 0 0 0 +21 4 5 -8.4719999999999995e-01 4.9064454390208301e+00 -4.0751205255383196e+00 -3.6215576073601046e+00 0 0 0 +22 4 2 4.2359999999999998e-01 4.3687453488627543e+00 -4.2054270536772504e+00 -4.4651491269372565e+00 0 0 0 +23 4 2 4.2359999999999998e-01 5.7374928154769504e+00 -3.5763355905184966e+00 -3.8820297194230728e+00 0 0 0 +24 5 5 -8.4719999999999995e-01 2.0684115301174013e+00 3.1518221747664397e+00 3.1554242678474576e+00 0 0 0 +25 5 2 4.2359999999999998e-01 1.2998381073113014e+00 3.2755513587518097e+00 2.5092990173114837e+00 0 0 0 +26 5 2 4.2359999999999998e-01 2.5807438597688113e+00 4.0120175892854135e+00 3.2133398379059099e+00 0 0 0 +27 6 5 -8.4719999999999995e-01 -1.9613581876744359e+00 -4.3556300596085160e+00 2.1101467673534788e+00 0 0 0 +28 6 2 4.2359999999999998e-01 -2.7406520384725965e+00 -4.0207251278130975e+00 1.5828689861678511e+00 0 0 0 +29 6 2 4.2359999999999998e-01 -1.3108232656499081e+00 -3.5992986322410760e+00 2.2680459788743503e+00 0 0 0 + +Velocities + +1 7.7867804888392077e-04 5.8970331623292821e-04 -2.2179517633030531e-04 +2 2.7129529964126462e-03 4.6286427111164284e-03 3.5805549693846352e-03 +3 -1.2736791029204805e-03 1.6108674226414498e-03 -3.3618185901550799e-04 +4 -9.2828595122009308e-04 -1.2537885319521818e-03 -4.1204974953432108e-03 +5 -1.1800848061603740e-03 7.5424401975844038e-04 6.9023177964912290e-05 +6 -3.0914004879905335e-04 1.2755385764678133e-03 7.9574303350202582e-04 +7 -1.1037894966874103e-04 -7.6764845099077425e-04 -7.7217630460203659e-04 +8 3.9060281273221989e-04 -8.1444231918053418e-04 1.5134641148324972e-04 +9 1.2475530960659720e-03 -2.6608454451432528e-03 1.1117602907112732e-03 +10 4.5008983776042893e-04 4.9530197647538077e-04 -2.3336234361093645e-04 +11 -3.6977669078869707e-04 -1.5289071951960539e-03 -2.9176389881837113e-03 +12 1.0850834530183159e-03 -6.4965897903201833e-04 -1.2971152622619948e-03 +13 4.0754559196230639e-03 3.5043502394946119e-03 -7.8324487687854666e-04 +14 -1.3837220448746613e-04 -4.0656048637594394e-03 -3.9333461173944500e-03 +15 -4.3301707382721859e-03 -3.1802661664634938e-03 3.2037919043360571e-03 +16 -9.6715751018414326e-05 -5.0016572678960377e-04 1.4945658875149626e-03 +17 6.5692180538157174e-04 3.6635779995305095e-04 8.3495414466050911e-04 +18 -6.0936815808025862e-04 -9.3774557532468582e-04 -3.3558072507805731e-04 +19 -6.9919768291957119e-04 -3.6060777270430031e-03 4.2833405289822791e-03 +20 4.7777805013736515e-03 5.1003745845520452e-03 1.8002873923729241e-03 +21 -9.5568188553430398e-04 1.6594630943762931e-04 -1.8199788009966615e-04 +22 -3.3137518957653462e-03 -2.8683968287936054e-03 3.6384389958326871e-03 +23 2.4209481134686401e-04 -4.5457709985051130e-03 2.7663581642115042e-03 +24 2.5447450568861086e-04 4.8412447786110117e-04 -4.8021914527341357e-04 +25 4.3722771097312743e-03 -4.5184411669545515e-03 2.5200952006556795e-03 +26 -1.9250110555001179e-03 -3.0342169883610837e-03 3.5062814567984532e-03 +27 -2.6510179146429716e-04 3.6306203629019116e-04 -5.6235585400647747e-04 +28 -2.3068708109787484e-04 -8.5663070212203200e-04 2.1302563179109169e-03 +29 -2.5054744388303732e-03 -1.6773997805290820e-04 2.8436699761004796e-03 + +Bonds + +1 5 1 2 +2 3 1 3 +3 2 3 4 +4 2 3 5 +5 1 3 6 +6 3 6 8 +7 4 6 7 +8 5 8 9 +9 3 8 10 +10 2 10 11 +11 1 10 12 +12 1 10 16 +13 2 12 13 +14 2 12 14 +15 2 12 15 +16 4 16 17 +17 5 18 19 +18 5 18 20 +19 5 21 22 +20 5 21 23 +21 5 24 25 +22 5 24 26 +23 5 27 28 +24 5 27 29 + +Angles + +1 4 2 1 3 +2 4 1 3 5 +3 4 1 3 4 +4 4 1 3 6 +5 4 4 3 5 +6 2 5 3 6 +7 2 4 3 6 +8 3 3 6 7 +9 3 3 6 8 +10 3 7 6 8 +11 2 6 8 9 +12 2 9 8 10 +13 3 6 8 10 +14 2 8 10 11 +15 3 8 10 16 +16 2 11 10 12 +17 1 12 10 16 +18 1 8 10 12 +19 2 11 10 16 +20 2 10 12 15 +21 2 10 12 14 +22 2 10 12 13 +23 4 13 12 15 +24 4 13 12 14 +25 4 14 12 15 +26 4 10 16 17 +27 1 19 18 20 +28 1 22 21 23 +29 1 25 24 26 +30 1 28 27 29 + +Dihedrals + +1 2 2 1 3 6 +2 2 2 1 3 4 +3 3 2 1 3 5 +4 1 1 3 6 8 +5 1 1 3 6 7 +6 5 4 3 6 8 +7 5 4 3 6 7 +8 5 5 3 6 8 +9 5 5 3 6 7 +10 4 3 6 8 9 +11 3 3 6 8 10 +12 3 7 6 8 9 +13 4 7 6 8 10 +14 2 6 8 10 12 +15 2 6 8 10 16 +16 2 6 8 10 11 +17 2 9 8 10 12 +18 4 9 8 10 16 +19 5 9 8 10 11 +20 5 8 10 12 13 +21 1 8 10 12 14 +22 5 8 10 12 15 +23 4 8 10 16 17 +24 5 11 10 12 13 +25 5 11 10 12 14 +26 5 11 10 12 15 +27 2 11 10 16 17 +28 2 12 10 16 17 +29 5 16 10 12 13 +30 5 16 10 12 14 +31 5 16 10 12 15 + +Impropers + +1 1 6 3 8 7 +2 2 8 6 10 9 diff --git a/unittest/python/in.fourmol b/unittest/python/in.fourmol new file mode 100644 index 0000000000..e0c3dfc7c1 --- /dev/null +++ b/unittest/python/in.fourmol @@ -0,0 +1,25 @@ +variable units index real +variable input_dir index . +variable data_file index ${input_dir}/data.fourmol +variable pair_style index 'zero 8.0' +variable bond_style index zero +variable angle_style index zero +variable dihedral_style index zero +variable improper_style index zero + +atom_style full +atom_modify map array +neigh_modify delay 2 every 2 check no +units ${units} +timestep 0.1 + +pair_style ${pair_style} +bond_style ${bond_style} +angle_style ${angle_style} +dihedral_style ${dihedral_style} +improper_style ${improper_style} + +read_data ${data_file} +dihedral_coeff * +improper_coeff * + diff --git a/unittest/python/python-numpy.py b/unittest/python/python-numpy.py index dc121691ab..010255a81e 100644 --- a/unittest/python/python-numpy.py +++ b/unittest/python/python-numpy.py @@ -15,6 +15,17 @@ try: except: pass +has_full=False +try: + machine=None + if 'LAMMPS_MACHINE_NAME' in os.environ: + machine=os.environ['LAMMPS_MACHINE_NAME'] + lmp=lammps(name=machine) + has_full = lmp.has_style("atom","full") + lmp.close() +except: + pass + try: import numpy NUMPY_INSTALLED = True @@ -32,6 +43,14 @@ class PythonNumpy(unittest.TestCase): def tearDown(self): del self.lmp + def checkBond(self, vals, btype, batom1, batom2): + if ((vals[1] == batom1 and vals[2] == batom2) + or (vals[1] == batom2 and vals[2] == batom1)): + self.assertEqual(vals[0], btype) + return 1 + else: + return 0 + def testLammpsPointer(self): self.assertEqual(type(self.lmp.lmp), c_void_p) @@ -148,6 +167,50 @@ class PythonNumpy(unittest.TestCase): self.assertTrue((x[1] == (1.0, 1.0, 1.5)).all()) self.assertEqual(len(v), 2) + @unittest.skipIf(not has_full,"Gather bonds test") + def testGatherBond_newton_on(self): + self.lmp.command('shell cd ' + os.environ['TEST_INPUT_DIR']) + self.lmp.command("newton on on") + self.lmp.file("in.fourmol") + self.lmp.command("run 0 post no") + bonds = self.lmp.numpy.gather_bonds() + self.assertEqual(len(bonds),24) + count = 0 + for bond in bonds: + count += self.checkBond(bond, 5, 1, 2) + count += self.checkBond(bond, 3, 1, 3) + count += self.checkBond(bond, 2, 3, 4) + count += self.checkBond(bond, 2, 3, 5) + count += self.checkBond(bond, 1, 6, 3) + count += self.checkBond(bond, 3, 6, 8) + count += self.checkBond(bond, 4, 6, 7) + count += self.checkBond(bond, 5, 8, 9) + count += self.checkBond(bond, 5, 27, 28) + count += self.checkBond(bond, 5, 29, 27) + self.assertEqual(count,10) + + @unittest.skipIf(not has_full,"Gather bonds test") + def testGatherBond_newton_off(self): + self.lmp.command('shell cd ' + os.environ['TEST_INPUT_DIR']) + self.lmp.command("newton off off") + self.lmp.file("in.fourmol") + self.lmp.command("run 0 post no") + bonds = self.lmp.numpy.gather_bonds() + self.assertEqual(len(bonds),24) + count = 0 + for bond in bonds: + count += self.checkBond(bond, 5, 1, 2) + count += self.checkBond(bond, 3, 1, 3) + count += self.checkBond(bond, 2, 3, 4) + count += self.checkBond(bond, 2, 3, 5) + count += self.checkBond(bond, 1, 6, 3) + count += self.checkBond(bond, 3, 6, 8) + count += self.checkBond(bond, 4, 6, 7) + count += self.checkBond(bond, 5, 8, 9) + count += self.checkBond(bond, 5, 27, 28) + count += self.checkBond(bond, 5, 29, 27) + self.assertEqual(count,10) + def testNeighborListSimple(self): self.lmp.commands_string(""" units lj diff --git a/unittest/python/python-scatter-gather.py b/unittest/python/python-scatter-gather.py new file mode 100644 index 0000000000..cd606c2992 --- /dev/null +++ b/unittest/python/python-scatter-gather.py @@ -0,0 +1,90 @@ + +import sys,os,unittest +from lammps import lammps + +has_full=False +try: + machine=None + if 'LAMMPS_MACHINE_NAME' in os.environ: + machine=os.environ['LAMMPS_MACHINE_NAME'] + lmp=lammps(name=machine) + has_full = lmp.has_style("atom","full") + lmp.close() +except: + pass + +class PythonGather(unittest.TestCase): + + def setUp(self): + machine=None + if 'LAMMPS_MACHINE_NAME' in os.environ: + machine=os.environ['LAMMPS_MACHINE_NAME'] + self.lmp=lammps(name=machine, + cmdargs=['-nocite', + '-log','none', + '-echo','screen']) + self.lmp.command('shell cd ' + os.environ['TEST_INPUT_DIR']) + + # clean up temporary files + def tearDown(self): + self.lmp.close() + + # bond data comparison + def checkBond(self, vals, btype, batom1, batom2): + if ((vals[1] == batom1 and vals[2] == batom2) + or (vals[1] == batom2 and vals[2] == batom1)): + self.assertEqual(vals[0], btype) + return 1 + else: + return 0 + + ############################## + @unittest.skipIf(not has_full, "Gather_bonds test") + def testGatherBond_newton_on(self): + """Test gather_bonds() with newton on""" + self.lmp.command("newton on on") + self.lmp.file("in.fourmol") + self.lmp.command("run 0 post no") + nbonds, bonds = self.lmp.gather_bonds() + self.assertEqual(nbonds, 24) + self.assertEqual(len(bonds), 3*24) + count = 0; + for i in range(0,nbonds): + count += self.checkBond(bonds[3*i:3*i+3], 5, 1, 2) + count += self.checkBond(bonds[3*i:3*i+3], 3, 1, 3) + count += self.checkBond(bonds[3*i:3*i+3], 2, 3, 4) + count += self.checkBond(bonds[3*i:3*i+3], 2, 3, 5) + count += self.checkBond(bonds[3*i:3*i+3], 1, 6, 3) + count += self.checkBond(bonds[3*i:3*i+3], 3, 6, 8) + count += self.checkBond(bonds[3*i:3*i+3], 4, 6, 7) + count += self.checkBond(bonds[3*i:3*i+3], 5, 8, 9) + count += self.checkBond(bonds[3*i:3*i+3], 5, 27, 28) + count += self.checkBond(bonds[3*i:3*i+3], 5, 29, 27) + self.assertEqual(count,10) + + @unittest.skipIf(not has_full, "Gather_bonds test") + def testGatherBond_newton_off(self): + """Test gather_bonds() with newton off""" + self.lmp.command("newton off off") + self.lmp.file("in.fourmol") + self.lmp.command("run 0 post no") + nbonds, bonds = self.lmp.gather_bonds() + self.assertEqual(nbonds, 24) + self.assertEqual(len(bonds), 3*24) + count = 0; + for i in range(0,nbonds): + count += self.checkBond(bonds[3*i:3*i+3], 5, 1, 2) + count += self.checkBond(bonds[3*i:3*i+3], 3, 1, 3) + count += self.checkBond(bonds[3*i:3*i+3], 2, 3, 4) + count += self.checkBond(bonds[3*i:3*i+3], 2, 3, 5) + count += self.checkBond(bonds[3*i:3*i+3], 1, 3, 6) + count += self.checkBond(bonds[3*i:3*i+3], 3, 6, 8) + count += self.checkBond(bonds[3*i:3*i+3], 4, 6, 7) + count += self.checkBond(bonds[3*i:3*i+3], 5, 8, 9) + count += self.checkBond(bonds[3*i:3*i+3], 5, 27, 28) + count += self.checkBond(bonds[3*i:3*i+3], 5, 27, 29) + self.assertEqual(count,10) + +############################## +if __name__ == "__main__": + unittest.main() From a4ffffad3364123479642134d2f8adb4963d1113 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 26 Jul 2021 23:10:24 -0400 Subject: [PATCH 4/4] update some version strings for library and python APIs --- python/lammps/core.py | 16 ++++++++++++++++ python/lammps/numpy_wrapper.py | 6 ++++++ src/library.cpp | 18 +++++++++++++++++- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/python/lammps/core.py b/python/lammps/core.py index 03378f471d..c89c115d1f 100644 --- a/python/lammps/core.py +++ b/python/lammps/core.py @@ -1781,6 +1781,8 @@ class lammps(object): - x is a 2d NumPy array of doubles of the coordinates of the local atoms - f is a 2d NumPy array of doubles of the forces on the local atoms that will be added + .. versionchanged:: 28Jul2021 + :param fix_id: Fix-ID of a fix external instance :type: string :param callback: Python function that will be called from fix external @@ -1811,6 +1813,8 @@ class lammps(object): This is a wrapper around the :cpp:func:`lammps_fix_external_get_force` function of the C-library interface. + .. versionadded:: 28Jul2021 + :param fix_id: Fix-ID of a fix external instance :type: string :return: requested data @@ -1829,6 +1833,8 @@ class lammps(object): This is a wrapper around the :cpp:func:`lammps_fix_external_set_energy_global` function of the C-library interface. + .. versionadded:: 28Jul2021 + :param fix_id: Fix-ID of a fix external instance :type: string :param eng: potential energy value to be added by fix external @@ -1846,6 +1852,8 @@ class lammps(object): This is a wrapper around the :cpp:func:`lammps_fix_external_set_virial_global` function of the C-library interface. + .. versionadded:: 28Jul2021 + :param fix_id: Fix-ID of a fix external instance :type: string :param eng: list of 6 floating point numbers with the virial to be added by fix external @@ -1864,6 +1872,8 @@ class lammps(object): This is a wrapper around the :cpp:func:`lammps_fix_external_set_energy_peratom` function of the C-library interface. + .. versionadded:: 28Jul2021 + :param fix_id: Fix-ID of a fix external instance :type: string :param eatom: list of potential energy values for local atoms to be added by fix external @@ -1885,6 +1895,8 @@ class lammps(object): This is a wrapper around the :cpp:func:`lammps_fix_external_set_virial_peratom` function of the C-library interface. + .. versionadded:: 28Jul2021 + :param fix_id: Fix-ID of a fix external instance :type: string :param vatom: list of natoms lists with 6 floating point numbers to be added by fix external @@ -1915,6 +1927,8 @@ class lammps(object): This is a wrapper around the :cpp:func:`lammps_fix_external_set_vector_length` function of the C-library interface. + .. versionadded:: 28Jul2021 + :param fix_id: Fix-ID of a fix external instance :type: string :param length: length of the global vector @@ -1931,6 +1945,8 @@ class lammps(object): This is a wrapper around the :cpp:func:`lammps_fix_external_set_vector` function of the C-library interface. + .. versionadded:: 28Jul2021 + :param fix_id: Fix-ID of a fix external instance :type: string :param idx: 1-based index of the value in the global vector diff --git a/python/lammps/numpy_wrapper.py b/python/lammps/numpy_wrapper.py index a6cde93b95..2f0f74594e 100644 --- a/python/lammps/numpy_wrapper.py +++ b/python/lammps/numpy_wrapper.py @@ -274,6 +274,8 @@ class numpy_wrapper: method. It behaves the same as the original method, but returns a NumPy array instead of a ``ctypes`` pointer. + .. versionchanged:: 28Jul2021 + :param fix_id: Fix-ID of a fix external instance :type: string :return: requested data @@ -294,6 +296,8 @@ class numpy_wrapper: method. It behaves the same as the original method, but accepts a NumPy array instead of a list as argument. + .. versionadded:: 28Jul2021 + :param fix_id: Fix-ID of a fix external instance :type: string :param eatom: per-atom potential energy @@ -319,6 +323,8 @@ class numpy_wrapper: method. It behaves the same as the original method, but accepts a NumPy array instead of a list as argument. + .. versionadded:: 28Jul2021 + :param fix_id: Fix-ID of a fix external instance :type: string :param eatom: per-atom potential energy diff --git a/src/library.cpp b/src/library.cpp index 5cb101d6a4..f534af0925 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -367,7 +367,7 @@ must be explicitly shut down after any LAMMPS instance using it is closed (to release associated resources). After calling this function no Kokkos functionality may be used. -.. versionadded:: TBD +.. versionadded:: 2Jul2021 \endverbatim */ @@ -4945,6 +4945,8 @@ Please see the documentation for :doc:`fix external ` for more information about how to use the fix and how to couple it with an external code. +.. versionchanged:: 28Jul2021 + \endverbatim * * \param handle pointer to a previously created LAMMPS instance cast to ``void *``. @@ -5008,6 +5010,8 @@ Please see the documentation for :doc:`fix external ` for more information about how to use the fix and how to couple it with an external code. +.. versionadded:: 28Jul2021 + \endverbatim * * \param handle pointer to a previously created LAMMPS instance cast to ``void *``. @@ -5056,6 +5060,8 @@ Please see the documentation for :doc:`fix external ` for more information about how to use the fix and how to couple it with an external code. +.. versionadded:: 28Jul2021 + \endverbatim * * \param handle pointer to a previously created LAMMPS instance cast to ``void *``. @@ -5105,6 +5111,8 @@ Please see the documentation for :doc:`fix external ` for more information about how to use the fix and how to couple it with an external code. +.. versionadded:: 28Jul2021 + \endverbatim * * \param handle pointer to a previously created LAMMPS instance cast to ``void *``. @@ -5154,6 +5162,8 @@ Please see the documentation for :doc:`fix external ` for more information about how to use the fix and how to couple it with an external code. +.. versionadded:: 28Jul2021 + \endverbatim * * \param handle pointer to a previously created LAMMPS instance cast to ``void *``. @@ -5206,6 +5216,8 @@ Please see the documentation for :doc:`fix external ` for more information about how to use the fix and how to couple it with an external code. +.. versionadded:: 28Jul2021 + \endverbatim * * \param handle pointer to a previously created LAMMPS instance cast to ``void *``. @@ -5251,6 +5263,8 @@ Please see the documentation for :doc:`fix external ` for more information about how to use the fix and how to couple it with an external code. +.. versionadded:: 28Jul2021 + \endverbatim * * \param handle pointer to a previously created LAMMPS instance cast to ``void *``. @@ -5305,6 +5319,8 @@ Please see the documentation for :doc:`fix external ` for more information about how to use the fix and how to couple it with an external code. +.. versionadded:: 28Jul2021 + \endverbatim * * \param handle pointer to a previously created LAMMPS instance cast to ``void *``.