From 88e363c0bbf4bc46c1465034dd3d900f6142b149 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 16 Jul 2021 14:51:04 -0400 Subject: [PATCH 01/19] document and add unit tests for lammps_set_fix_external_callback() --- src/library.cpp | 47 +++++++++---- src/library.h | 2 +- unittest/c-library/CMakeLists.txt | 4 ++ unittest/c-library/test_library_external.cpp | 73 ++++++++++++++++++++ unittest/python/CMakeLists.txt | 5 ++ unittest/python/python-fix-external.py | 42 +++++++++++ 6 files changed, 159 insertions(+), 14 deletions(-) create mode 100644 unittest/c-library/test_library_external.cpp create mode 100644 unittest/python/python-fix-external.py diff --git a/src/library.cpp b/src/library.cpp index c0dcb4f328..f0d747f258 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -4804,15 +4804,38 @@ void lammps_decode_image_flags(imageint image, int *flags) flags[2] = (image >> IMG2BITS) - IMGMAX; } -/* ---------------------------------------------------------------------- - find fix external with given ID and set the callback function - and caller pointer -------------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ -void lammps_set_fix_external_callback(void *handle, char *id, FixExternalFnPtr callback_ptr, void * caller) +/** Set the callback function for a fix external instance with given ID. + Optionally also set the pointer to the calling object. +\verbatim embed:rst + +Fix :doc:`external ` allows programs that are running LAMMPS through +its library interface to modify certain LAMMPS properties on specific +timesteps, similar to the way other fixes do. + +This function sets the callback function which has to have C language +bindings with the prototype: + +.. code-block:: c + + void func(void *ptr, bigint timestep, int nlocal, tagint *ids, double **x, double **fexternal); + +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. + +\endverbatim + * + * \param handle pointer to a previously created LAMMPS instance cast to ``void *``. + * \param id fix ID of fix external instance + * \param funcptr pointer to callback function + * \param ptr pointer to object in calling code, passed to callback function as first argument */ + +void lammps_set_fix_external_callback(void *handle, char *id, FixExternalFnPtr funcptr, void *ptr) { LAMMPS *lmp = (LAMMPS *) handle; - FixExternal::FnPtr callback = (FixExternal::FnPtr) callback_ptr; + FixExternal::FnPtr callback = (FixExternal::FnPtr) funcptr; BEGIN_CAPTURE { @@ -4823,18 +4846,16 @@ void lammps_set_fix_external_callback(void *handle, char *id, FixExternalFnPtr c Fix *fix = lmp->modify->fix[ifix]; if (strcmp("external",fix->style) != 0) - lmp->error->all(FLERR,"Fix '{}' is not of style " - "external!", id); + lmp->error->all(FLERR,"Fix '{}' is not of style 'external'", id); - FixExternal * fext = (FixExternal*) fix; - fext->set_callback(callback, caller); + FixExternal *fext = (FixExternal *) fix; + fext->set_callback(callback, ptr); } END_CAPTURE } /* set global energy contribution from fix external */ -void lammps_fix_external_set_energy_global(void *handle, char *id, - double energy) +void lammps_fix_external_set_energy_global(void *handle, char *id, double energy) { LAMMPS *lmp = (LAMMPS *) handle; @@ -4849,7 +4870,7 @@ void lammps_fix_external_set_energy_global(void *handle, char *id, if (strcmp("external",fix->style) != 0) lmp->error->all(FLERR,"Fix '{}' is not of style external!", id); - FixExternal * fext = (FixExternal*) fix; + FixExternal *fext = (FixExternal*) fix; fext->set_energy_global(energy); } END_CAPTURE diff --git a/src/library.h b/src/library.h index a337d7b510..2732314771 100644 --- a/src/library.h +++ b/src/library.h @@ -226,7 +226,7 @@ void lammps_decode_image_flags(int64_t image, int *flags); #if defined(LAMMPS_BIGBIG) typedef void (*FixExternalFnPtr)(void *, int64_t, int, int64_t *, double **, double **); -void lammps_set_fix_external_callback(void *, char *, FixExternalFnPtr, void *); +void lammps_set_fix_external_callback(void *handle, char *id, FixExternalFnPtr funcptr, void *ptr); #elif defined(LAMMPS_SMALLBIG) typedef void (*FixExternalFnPtr)(void *, int64_t, int, int *, double **, double **); void lammps_set_fix_external_callback(void *, char *, FixExternalFnPtr, void *); diff --git a/unittest/c-library/CMakeLists.txt b/unittest/c-library/CMakeLists.txt index 3c24cdcff4..b01cd64677 100644 --- a/unittest/c-library/CMakeLists.txt +++ b/unittest/c-library/CMakeLists.txt @@ -7,6 +7,10 @@ add_executable(test_library_commands test_library_commands.cpp test_main.cpp) target_link_libraries(test_library_commands PRIVATE lammps GTest::GTest GTest::GMock) add_test(LibraryCommands test_library_commands) +add_executable(test_library_external test_library_external.cpp test_main.cpp) +target_link_libraries(test_library_external PRIVATE lammps GTest::GTest GTest::GMock) +add_test(LibraryExternal test_library_external) + add_executable(test_library_properties test_library_properties.cpp test_main.cpp) target_link_libraries(test_library_properties PRIVATE lammps GTest::GTest GTest::GMock) target_compile_definitions(test_library_properties PRIVATE -DTEST_INPUT_FOLDER=${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/unittest/c-library/test_library_external.cpp b/unittest/c-library/test_library_external.cpp new file mode 100644 index 0000000000..78ed91195c --- /dev/null +++ b/unittest/c-library/test_library_external.cpp @@ -0,0 +1,73 @@ +// unit tests creating LAMMPS instances via the library interface + +#include "library.h" + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "test_main.h" + +using ::testing::HasSubstr; +using ::testing::StartsWith; + +extern "C" { +#ifdef LAMMPS_SMALLSMALL +typedef int32_t step_t; +typedef int32_t tag_t; +#elif LAMMPS_SMALLBIG +typedef int64_t step_t; +typedef int32_t tag_t; +#else +typedef int64_t step_t; +typedef int64_t tag_t; +#endif +static void callback_one(void *lmp, step_t timestep, int nlocal, tag_t *ids, double **x, double **f) +{ + for (int i = 0; i < nlocal; ++i) + f[i][0] = f[i][1] = f[i][2] = (double)timestep; +} +} + +TEST(lammps_external_pf, null_args) +{ + const char *args[] = {"liblammps", "-log", "none", "-nocite"}; + char **argv = (char **)args; + int argc = sizeof(args) / sizeof(char *); + + ::testing::internal::CaptureStdout(); + void *handle = lammps_open_no_mpi(argc, argv, NULL); + std::string output = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << output; + + ::testing::internal::CaptureStdout(); + lammps_commands_string(handle, "lattice sc 1.0\n" + "region box block -1 1 -1 1 -1 1\n" + "create_box 1 box\n" + "create_atoms 1 box\n" + "mass 1 1.0\n" + "pair_style zero 0.1\n" + "pair_coeff 1 1\n" + "velocity all set 0.1 0.0 -0.1\n" + "thermo 5\n" + "fix 1 all nve\n" + "fix ext all external pf/callback 5 1\n"); + + output = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << output; + + ::testing::internal::CaptureStdout(); + lammps_set_fix_external_callback(handle, (char *)"ext", &callback_one, handle); + lammps_command(handle, "run 10 post no"); + double temp = lammps_get_thermo(handle,"temp"); + output = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << output; + EXPECT_DOUBLE_EQ(temp,1.0/30.0); + + ::testing::internal::CaptureStdout(); + lammps_close(handle); + output = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << output; +} diff --git a/unittest/python/CMakeLists.txt b/unittest/python/CMakeLists.txt index b51d6e340a..9c9b7832ad 100644 --- a/unittest/python/CMakeLists.txt +++ b/unittest/python/CMakeLists.txt @@ -85,6 +85,11 @@ if(Python_EXECUTABLE) COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-formats.py -v WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) set_tests_properties(PythonFormats PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + + add_test(NAME PythonFixExternal + 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}") else() message(STATUS "Skipping Tests for the LAMMPS Python Module: no suitable Python interpreter") endif() diff --git a/unittest/python/python-fix-external.py b/unittest/python/python-fix-external.py new file mode 100644 index 0000000000..badc9e5731 --- /dev/null +++ b/unittest/python/python-fix-external.py @@ -0,0 +1,42 @@ +import sys,os,unittest +from ctypes import * +from lammps import lammps + +# add timestep dependent force +def callback_one(lmp, ntimestep, nlocal, tag, x, f): + for i in range(nlocal): + f[i][0] = float(ntimestep) + f[i][1] = float(ntimestep) + f[i][2] = float(ntimestep) + +class PythonExternal(unittest.TestCase): + def testExternalCallback(self): + """Test fix external from Python with pf/callback""" + + machine=None + if 'LAMMPS_MACHINE_NAME' in os.environ: + machine=os.environ['LAMMPS_MACHINE_NAME'] + lmp=lammps(name=machine, cmdargs=['-nocite', '-log','none', '-echo', 'screen']) + + # a few commands to set up simple system + basic_system="""lattice sc 1.0 + region box block -1 1 -1 1 -1 1 + create_box 1 box + create_atoms 1 box + mass 1 1.0 + pair_style zero 0.1 + pair_coeff 1 1 + velocity all set 0.1 0.0 -0.1 + thermo 5 + fix 1 all nve + fix ext all external pf/callback 5 1 +""" + lmp.commands_string(basic_system) + lmp.set_fix_external_callback("ext",callback_one,lmp) + lmp.command("run 10 post no") + self.assertAlmostEqual(lmp.get_thermo("temp"),1.0/30.0,14) + +############################## +if __name__ == "__main__": + unittest.main() + From 8b1dedf04a3c16a6d21673734c64fb1ce9e90c79 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 16 Jul 2021 17:21:57 -0400 Subject: [PATCH 02/19] add extract method to fix external --- src/fix_external.cpp | 15 ++++++++++++++- src/fix_external.h | 2 ++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/fix_external.cpp b/src/fix_external.cpp index 8bede4ee19..4bc1e7eebc 100644 --- a/src/fix_external.cpp +++ b/src/fix_external.cpp @@ -238,7 +238,7 @@ void FixExternal::set_virial_peratom(double **caller_virial) void FixExternal::set_vector_length(int n) { - delete [] caller_vector; + delete[] caller_vector; vector_flag = 1; size_vector = n; @@ -342,3 +342,16 @@ void FixExternal::set_callback(FnPtr caller_callback, void *caller_ptr) callback = caller_callback; ptr_caller = caller_ptr; } + +/* ---------------------------------------------------------------------- + get access to internal data structures +------------------------------------------------------------------------- */ + +void *FixExternal::extract(const char *str, int &dim) +{ + if (strcmp(str, "fexternal") == 0) { + dim = 2; + return (void *) fexternal; + } + return nullptr; +} diff --git a/src/fix_external.h b/src/fix_external.h index 0ace978f99..f0f46cd4c5 100644 --- a/src/fix_external.h +++ b/src/fix_external.h @@ -57,6 +57,8 @@ class FixExternal : public Fix { typedef void (*FnPtr)(void *, bigint, int, tagint *, double **, double **); void set_callback(FnPtr, void *); + void *extract(const char *, int &); + private: int mode, ncall, napply, eflag_caller; FnPtr callback; From f3dc13c9dd72960daa80b4738697de71c4fa0ef0 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 16 Jul 2021 17:25:47 -0400 Subject: [PATCH 03/19] expand library interface for fix external functionality --- doc/src/Library_utility.rst | 2 +- python/lammps/core.py | 59 +++++- src/library.cpp | 185 ++++++++++++++++++- src/library.h | 15 +- unittest/c-library/test_library_external.cpp | 77 +++++++- unittest/python/python-fix-external.py | 41 ++++ 6 files changed, 350 insertions(+), 29 deletions(-) diff --git a/doc/src/Library_utility.rst b/doc/src/Library_utility.rst index b2f3666f88..2748d418b6 100644 --- a/doc/src/Library_utility.rst +++ b/doc/src/Library_utility.rst @@ -33,7 +33,7 @@ where such memory buffers were allocated that require the use of ----------------------- -.. doxygenfunction:: lammps_set_fix_external_callback(void *, char *, FixExternalFnPtr, void*) +.. doxygenfunction:: lammps_set_fix_external_callback(void *, const char *, FixExternalFnPtr, void*) :project: progguide ----------------------- diff --git a/python/lammps/core.py b/python/lammps/core.py index 2f101f4eab..5079828ba8 100644 --- a/python/lammps/core.py +++ b/python/lammps/core.py @@ -295,9 +295,13 @@ class lammps(object): self.lib.lammps_extract_variable.argtypes = [c_void_p, c_char_p, c_char_p] - # TODO: NOT IMPLEMENTED IN PYTHON WRAPPER - self.lib.lammps_fix_external_set_energy_global = [c_void_p, c_char_p, c_double] - self.lib.lammps_fix_external_set_virial_global = [c_void_p, c_char_p, POINTER(c_double)] + self.lib.lammps_fix_external_get_force.argtypes = [c_void_p, c_char_p] + self.lib.lammps_fix_external_get_force.restype = POINTER(POINTER(c_double)) + + self.lib.lammps_fix_external_set_energy_global.argtypes = [c_void_p, c_char_p, c_double] + self.lib.lammps_fix_external_set_virial_global.argtypes = [c_void_p, c_char_p, POINTER(c_double)] + self.lib.lammps_fix_external_set_energy_peratom.argtypes = [c_void_p, c_char_p, POINTER(c_double)] + self.lib.lammps_fix_external_set_virial_peratom.argtypes = [c_void_p, c_char_p, POINTER(POINTER(c_double))] # detect if Python is using a version of mpi4py that can pass communicators # only needed if LAMMPS has been compiled with MPI support. @@ -1725,7 +1729,33 @@ class lammps(object): # ------------------------------------------------------------------------- - def set_fix_external_callback(self, fix_name, callback, caller=None): + def set_fix_external_callback(self, fix_id, callback, caller=None): + """Set the callback function for a fix external instance with a given fix ID. + + Optionally also set a reference to the calling object. + + This is a wrapper around the :cpp:func:`lammps_set_fix_external_callback` function + of the C-library interface. However this is set up to call a Python function with + the following arguments. + + .. code-block: python + + def func(object, ntimestep, nlocal, tag, x, f): + + - object is the value of the "caller" argument + - ntimestep is the current timestep + - nlocal is the number of local atoms on the current MPI process + - tag is a 1d NumPy array of integers representing the atom IDs of the local atoms + - x is a 2d NumPy array of floating point numbers of the coordinates of the local atoms + - f is a 2d NumPy array of floating point numbers of the forces on the local atoms that will be added + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param callback: Python function that will be called from fix external + :type: function + :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): @@ -1737,10 +1767,27 @@ class lammps(object): cFunc = self.FIX_EXTERNAL_CALLBACK_FUNC(callback_wrapper) cCaller = caller - self.callback[fix_name] = { 'function': cFunc, 'caller': caller } + self.callback[fix_id] = { 'function': cFunc, 'caller': caller } with ExceptionCheck(self): - self.lib.lammps_set_fix_external_callback(self.lmp, fix_name.encode(), cFunc, cCaller) + self.lib.lammps_set_fix_external_callback(self.lmp, fix_id.encode(), cFunc, cCaller) + # ------------------------------------------------------------------------- + + def fix_external_get_force(self, fix_id): + """Get access to that array with per-atom forces of a fix external instance with a given fix ID. + + This is a wrapper around the :cpp:func:`lammps_fix_external_get_force` function + of the C-library interface. + + :param fix_id: Fix-ID of a fix external instance + :type: string + :return: requested data + :rtype: ctypes.POINTER(ctypes.POINTER(ctypes.double)) + """ + + with ExceptionCheck(self): + return self.lib.lammps_fix_external_get_force(self.lmp, fix_id.encode()) + return None # ------------------------------------------------------------------------- diff --git a/src/library.cpp b/src/library.cpp index f0d747f258..4488114579 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -4806,7 +4806,7 @@ void lammps_decode_image_flags(imageint image, int *flags) /* ---------------------------------------------------------------------- */ -/** Set the callback function for a fix external instance with given ID. +/** Set the callback function for a fix external instance with the given ID. Optionally also set the pointer to the calling object. \verbatim embed:rst @@ -4814,13 +4814,15 @@ Fix :doc:`external ` allows programs that are running LAMMPS throu its library interface to modify certain LAMMPS properties on specific timesteps, similar to the way other fixes do. -This function sets the callback function which has to have C language -bindings with the prototype: +This function sets the callback function for use with the "pf/callback" +mode. The function has to have C language bindings with the prototype: .. code-block:: c void func(void *ptr, bigint timestep, int nlocal, tagint *ids, double **x, double **fexternal); +This is an alternative to the array mechanism set up by :cpp:func:`lammps_fix_external_set_force`. + 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. @@ -4832,7 +4834,7 @@ external code. * \param funcptr pointer to callback function * \param ptr pointer to object in calling code, passed to callback function as first argument */ -void lammps_set_fix_external_callback(void *handle, char *id, FixExternalFnPtr funcptr, void *ptr) +void lammps_set_fix_external_callback(void *handle, const char *id, FixExternalFnPtr funcptr, void *ptr) { LAMMPS *lmp = (LAMMPS *) handle; FixExternal::FnPtr callback = (FixExternal::FnPtr) funcptr; @@ -4854,8 +4856,77 @@ void lammps_set_fix_external_callback(void *handle, char *id, FixExternalFnPtr f END_CAPTURE } -/* set global energy contribution from fix external */ -void lammps_fix_external_set_energy_global(void *handle, char *id, double energy) +/** Get pointer to the force array storage in a fix external instance with the given ID. + +\verbatim embed:rst + +Fix :doc:`external ` allows programs that are running LAMMPS through +its library interface to add or modify certain LAMMPS properties on specific +timesteps, similar to the way other fixes do. + +This function provides access to the per-atom force storage in the fix +to be added to the individual atoms when using the "pf/array" mode. The +*fexternal* array can be accessed similar to the "native" per-atom +*arrays accessible via the :cpp:func:`lammps_extract_atom` function. +Because the underlying data structures can change as atoms migrate +between MPI processes, this function should be always called immediately +before the forces are going to be set. + +This is an alternative to the callback mechanism set up by +:cpp:func:`lammps_set_fix_external_callback` with out using the callback +mechanism to call out to the external program. + +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. + +\endverbatim + * + * \param handle pointer to a previously created LAMMPS instance cast to ``void *``. + * \param id fix ID of fix external instance + * \return a pointer to the per-atom force array allocated by the fix */ + +double **lammps_fix_external_get_force(void *handle, const char *id) +{ + LAMMPS *lmp = (LAMMPS *) handle; + double **fexternal = nullptr; + + BEGIN_CAPTURE + { + int ifix = lmp->modify->find_fix(id); + if (ifix < 0) + lmp->error->all(FLERR,"Can not find fix with ID '{}'!", id); + + Fix *fix = lmp->modify->fix[ifix]; + + if (strcmp("external",fix->style) != 0) + lmp->error->all(FLERR,"Fix '{}' is not of style external!", id); + + fexternal = (double **)fix->extract("fexternal",ifix); + } + END_CAPTURE + return fexternal; +} + +/** Set the global energy contribution for a fix external instance with the given ID. + +\verbatim embed:rst + +This is a companion function to :cpp:func:`lammps_set_fix_external_callback` and +:cpp:func:`lammps_fix_external_set_force` to also set the contribution +to the global energy from the external code. + +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. + +\endverbatim + * + * \param handle pointer to a previously created LAMMPS instance cast to ``void *``. + * \param id fix ID of fix external instance + * \param eng energy to be added to the global energy */ + +void lammps_fix_external_set_energy_global(void *handle, const char *id, double eng) { LAMMPS *lmp = (LAMMPS *) handle; @@ -4871,14 +4942,30 @@ void lammps_fix_external_set_energy_global(void *handle, char *id, double energy lmp->error->all(FLERR,"Fix '{}' is not of style external!", id); FixExternal *fext = (FixExternal*) fix; - fext->set_energy_global(energy); + fext->set_energy_global(eng); } END_CAPTURE } -/* set global virial contribution from fix external */ -void lammps_fix_external_set_virial_global(void *handle, char *id, - double *virial) +/** Set the global virial contribution for a fix external instance with the given ID. + +\verbatim embed:rst + +This is a companion function to :cpp:func:`lammps_set_fix_external_callback` and +:cpp:func:`lammps_fix_external_set_force` to also set the contribution +to the global virial from the external code. + +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. + +\endverbatim + * + * \param handle pointer to a previously created LAMMPS instance cast to ``void *``. + * \param id fix ID of fix external instance + * \param virial the 6 global stress tensor components to be added to the global virial */ + +void lammps_fix_external_set_virial_global(void *handle, const char *id, double *virial) { LAMMPS *lmp = (LAMMPS *) handle; @@ -4899,6 +4986,84 @@ void lammps_fix_external_set_virial_global(void *handle, char *id, END_CAPTURE } +/** Set the per-atom energy contribution for a fix external instance with the given ID. + +\verbatim embed:rst + +This is a companion function to :cpp:func:`lammps_set_fix_external_callback` and +:cpp:func:`lammps_fix_external_set_force` to also set the contribution +to the per-atom energy from the external code. + +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. + +\endverbatim + * + * \param handle pointer to a previously created LAMMPS instance cast to ``void *``. + * \param id fix ID of fix external instance + * \param eng energy to be added to the per-atom energy */ + +void lammps_fix_external_set_energy_peratom(void *handle, const char *id, double *eng) +{ + LAMMPS *lmp = (LAMMPS *) handle; + + BEGIN_CAPTURE + { + int ifix = lmp->modify->find_fix(id); + if (ifix < 0) + lmp->error->all(FLERR,"Can not find fix with ID '{}'!", id); + + Fix *fix = lmp->modify->fix[ifix]; + + if (strcmp("external",fix->style) != 0) + lmp->error->all(FLERR,"Fix '{}' is not of style external!", id); + + FixExternal *fext = (FixExternal*) fix; + fext->set_energy_peratom(eng); + } + END_CAPTURE +} + +/** Set the per-atom virial contribution for a fix external instance with the given ID. + +\verbatim embed:rst + +This is a companion function to :cpp:func:`lammps_set_fix_external_callback` and +:cpp:func:`lammps_fix_external_set_force` to also set the contribution +to the per-atom virial from the external code. + +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. + +\endverbatim + * + * \param handle pointer to a previously created LAMMPS instance cast to ``void *``. + * \param id fix ID of fix external instance + * \param virial the 6 per-atom stress tensor components to be added to the per-atom virial */ + +void lammps_fix_external_set_virial_peratom(void *handle, const char *id, double **virial) +{ + LAMMPS *lmp = (LAMMPS *) handle; + + BEGIN_CAPTURE + { + int ifix = lmp->modify->find_fix(id); + if (ifix < 0) + lmp->error->all(FLERR,"Can not find fix with ID '{}'!", id); + + Fix *fix = lmp->modify->fix[ifix]; + + if (strcmp("external",fix->style) != 0) + lmp->error->all(FLERR,"Fix '{}' is not of style external!", id); + + FixExternal * fext = (FixExternal*) fix; + fext->set_virial_peratom(virial); + } + END_CAPTURE +} + /* ---------------------------------------------------------------------- */ /** Free memory buffer allocated by LAMMPS. diff --git a/src/library.h b/src/library.h index 2732314771..25b5199dc7 100644 --- a/src/library.h +++ b/src/library.h @@ -226,16 +226,21 @@ void lammps_decode_image_flags(int64_t image, int *flags); #if defined(LAMMPS_BIGBIG) typedef void (*FixExternalFnPtr)(void *, int64_t, int, int64_t *, double **, double **); -void lammps_set_fix_external_callback(void *handle, char *id, FixExternalFnPtr funcptr, void *ptr); +void lammps_set_fix_external_callback(void *handle, const char *id, FixExternalFnPtr funcptr, void *ptr); #elif defined(LAMMPS_SMALLBIG) typedef void (*FixExternalFnPtr)(void *, int64_t, int, int *, double **, double **); -void lammps_set_fix_external_callback(void *, char *, FixExternalFnPtr, void *); +void lammps_set_fix_external_callback(void *, const char *, FixExternalFnPtr, void *); #else typedef void (*FixExternalFnPtr)(void *, int, int, int *, double **, double **); -void lammps_set_fix_external_callback(void *, char *, FixExternalFnPtr, void *); +void lammps_set_fix_external_callback(void *, const char *, FixExternalFnPtr, void *); #endif -void lammps_fix_external_set_energy_global(void *, char *, double); -void lammps_fix_external_set_virial_global(void *, char *, double *); +double **lammps_fix_external_get_force(void *handle, const char *id); +void lammps_fix_external_set_energy_global(void *handle, const char *id, double eng); +void lammps_fix_external_set_energy_peratom(void *handle, const char *id, double *eng); +void lammps_fix_external_set_virial_global(void *handle, const char *id, double *virial); +void lammps_fix_external_set_virial_peratom(void *handle, const char *id, double **virial); +void lammps_fix_external_set_vector_length(void *handle, const char *id, int len); +void lammps_fix_external_set_virial_peratom(void *handle, const char *id, double **val); void lammps_free(void *ptr); diff --git a/unittest/c-library/test_library_external.cpp b/unittest/c-library/test_library_external.cpp index 78ed91195c..f6f126c2b8 100644 --- a/unittest/c-library/test_library_external.cpp +++ b/unittest/c-library/test_library_external.cpp @@ -24,14 +24,17 @@ typedef int32_t tag_t; typedef int64_t step_t; typedef int64_t tag_t; #endif -static void callback_one(void *lmp, step_t timestep, int nlocal, tag_t *ids, double **x, double **f) +static void callback_one(void *handle, step_t timestep, int nlocal, tag_t *, double **, double **f) { for (int i = 0; i < nlocal; ++i) f[i][0] = f[i][1] = f[i][2] = (double)timestep; + lammps_fix_external_set_energy_global(handle, "ext", 1.0); + double v[6] = {1.0,1.0,1.0,0.0,0.0,0.0 }; + lammps_fix_external_set_virial_global(handle, "ext", v); } } -TEST(lammps_external_pf, null_args) +TEST(lammps_external, callback) { const char *args[] = {"liblammps", "-log", "none", "-nocite"}; char **argv = (char **)args; @@ -53,18 +56,78 @@ TEST(lammps_external_pf, null_args) "velocity all set 0.1 0.0 -0.1\n" "thermo 5\n" "fix 1 all nve\n" - "fix ext all external pf/callback 5 1\n"); + "fix ext all external pf/callback 5 1\n" + "fix_modify ext energy yes virial yes\n"); output = ::testing::internal::GetCapturedStdout(); if (verbose) std::cout << output; ::testing::internal::CaptureStdout(); - lammps_set_fix_external_callback(handle, (char *)"ext", &callback_one, handle); + lammps_set_fix_external_callback(handle, "ext", &callback_one, handle); lammps_command(handle, "run 10 post no"); - double temp = lammps_get_thermo(handle,"temp"); - output = ::testing::internal::GetCapturedStdout(); + double temp = lammps_get_thermo(handle, "temp"); + double pe = lammps_get_thermo(handle, "pe"); + double press = lammps_get_thermo(handle, "press"); + output = ::testing::internal::GetCapturedStdout(); if (verbose) std::cout << output; - EXPECT_DOUBLE_EQ(temp,1.0/30.0); + EXPECT_DOUBLE_EQ(temp, 1.0 / 30.0); + EXPECT_DOUBLE_EQ(pe, 1.0 / 8.0); + EXPECT_DOUBLE_EQ(press, 0.15416666666666667); + + ::testing::internal::CaptureStdout(); + lammps_close(handle); + output = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << output; +} + +TEST(lammps_external, array) +{ + const char *args[] = {"liblammps", "-log", "none", "-nocite"}; + char **argv = (char **)args; + int argc = sizeof(args) / sizeof(char *); + + ::testing::internal::CaptureStdout(); + void *handle = lammps_open_no_mpi(argc, argv, NULL); + std::string output = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << output; + + ::testing::internal::CaptureStdout(); + lammps_commands_string(handle, "lattice sc 1.0\n" + "region box block -1 1 -1 1 -1 1\n" + "create_box 1 box\n" + "create_atoms 1 box\n" + "mass 1 1.0\n" + "pair_style zero 0.1\n" + "pair_coeff 1 1\n" + "velocity all set 0.1 0.0 -0.1\n" + "thermo 5\n" + "fix 1 all nve\n" + "fix ext all external pf/array 1\n"); + + output = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << output; + + ::testing::internal::CaptureStdout(); + double **force = lammps_fix_external_get_force(handle, "ext"); + int nlocal = lammps_extract_setting(handle, "nlocal"); + for (int i = 0; i < nlocal; ++i) + force[i][0] = force[i][1] = force[i][2] = 0.0; + lammps_command(handle, "run 5 post no"); + double temp = lammps_get_thermo(handle, "temp"); + output = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << output; + EXPECT_DOUBLE_EQ(temp, 4.0 / 525.0); + + ::testing::internal::CaptureStdout(); + nlocal = lammps_extract_setting(handle, "nlocal"); + force = lammps_fix_external_get_force(handle, "ext"); + for (int i = 0; i < nlocal; ++i) + force[i][0] = force[i][1] = force[i][2] = 6.0; + lammps_command(handle, "run 5 post no"); + temp = lammps_get_thermo(handle, "temp"); + output = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << output; + EXPECT_DOUBLE_EQ(temp, 1.0 / 30.0); ::testing::internal::CaptureStdout(); lammps_close(handle); diff --git a/unittest/python/python-fix-external.py b/unittest/python/python-fix-external.py index badc9e5731..4f589bb5f6 100644 --- a/unittest/python/python-fix-external.py +++ b/unittest/python/python-fix-external.py @@ -36,6 +36,47 @@ class PythonExternal(unittest.TestCase): lmp.command("run 10 post no") self.assertAlmostEqual(lmp.get_thermo("temp"),1.0/30.0,14) + def testExternalArray(self): + """Test fix external from Python with pf/array""" + + machine=None + if 'LAMMPS_MACHINE_NAME' in os.environ: + machine=os.environ['LAMMPS_MACHINE_NAME'] + lmp=lammps(name=machine, cmdargs=['-nocite', '-log','none', '-echo', 'screen']) + + # a few commands to set up simple system + basic_system="""lattice sc 1.0 + region box block -1 1 -1 1 -1 1 + create_box 1 box + create_atoms 1 box + mass 1 1.0 + pair_style zero 0.1 + pair_coeff 1 1 + velocity all set 0.1 0.0 -0.1 + thermo 5 + fix 1 all nve + fix ext all external pf/array 1 +""" + lmp.commands_string(basic_system) + force = lmp.fix_external_get_force("ext"); + nlocal = lmp.extract_setting("nlocal"); + for i in range(nlocal): + force[i][0] = 0.0 + force[i][1] = 0.0 + force[i][2] = 0.0 + + lmp.command("run 5 post no") + self.assertAlmostEqual(lmp.get_thermo("temp"),4.0/525.0,14) + + force = lmp.fix_external_get_force("ext"); + nlocal = lmp.extract_setting("nlocal"); + for i in range(nlocal): + force[i][0] = 6.0 + force[i][1] = 6.0 + force[i][2] = 6.0 + lmp.command("run 5 post no") + self.assertAlmostEqual(lmp.get_thermo("temp"),1.0/30.0,14) + ############################## if __name__ == "__main__": unittest.main() From 5912d0a1c2d86333478aea3399488a3cb11051ab Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 16 Jul 2021 17:43:44 -0400 Subject: [PATCH 04/19] add support for setting global energy for fix external to python module --- python/lammps/core.py | 17 +++++++++++++++++ unittest/c-library/test_library_external.cpp | 13 ++++++++----- unittest/python/python-fix-external.py | 5 +++++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/python/lammps/core.py b/python/lammps/core.py index 5079828ba8..44cd51bb33 100644 --- a/python/lammps/core.py +++ b/python/lammps/core.py @@ -1791,6 +1791,23 @@ class lammps(object): # ------------------------------------------------------------------------- + def fix_external_set_energy_global(self, fix_id, eng): + """Get access to that array with per-atom forces of a fix external instance with a given fix ID. + + This is a wrapper around the :cpp:func:`lammps_fix_external_get_force` function + of the C-library interface. + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param eng: potential energy to be added by fix external + :type: float + """ + + with ExceptionCheck(self): + return self.lib.lammps_fix_external_set_energy_global(self.lmp, fix_id.encode(), eng) + + # ------------------------------------------------------------------------- + def get_neighlist(self, idx): """Returns an instance of :class:`NeighList` which wraps access to the neighbor list with the given index diff --git a/unittest/c-library/test_library_external.cpp b/unittest/c-library/test_library_external.cpp index f6f126c2b8..7c53daaef7 100644 --- a/unittest/c-library/test_library_external.cpp +++ b/unittest/c-library/test_library_external.cpp @@ -28,8 +28,11 @@ static void callback_one(void *handle, step_t timestep, int nlocal, tag_t *, dou { for (int i = 0; i < nlocal; ++i) f[i][0] = f[i][1] = f[i][2] = (double)timestep; - lammps_fix_external_set_energy_global(handle, "ext", 1.0); - double v[6] = {1.0,1.0,1.0,0.0,0.0,0.0 }; + if (timestep < 10) + lammps_fix_external_set_energy_global(handle, "ext", 0.0); + else + lammps_fix_external_set_energy_global(handle, "ext", 1.0); + double v[6] = {1.0, 1.0, 1.0, 0.0, 0.0, 0.0}; lammps_fix_external_set_virial_global(handle, "ext", v); } } @@ -65,10 +68,10 @@ TEST(lammps_external, callback) ::testing::internal::CaptureStdout(); lammps_set_fix_external_callback(handle, "ext", &callback_one, handle); lammps_command(handle, "run 10 post no"); - double temp = lammps_get_thermo(handle, "temp"); - double pe = lammps_get_thermo(handle, "pe"); + double temp = lammps_get_thermo(handle, "temp"); + double pe = lammps_get_thermo(handle, "pe"); double press = lammps_get_thermo(handle, "press"); - output = ::testing::internal::GetCapturedStdout(); + output = ::testing::internal::GetCapturedStdout(); if (verbose) std::cout << output; EXPECT_DOUBLE_EQ(temp, 1.0 / 30.0); EXPECT_DOUBLE_EQ(pe, 1.0 / 8.0); diff --git a/unittest/python/python-fix-external.py b/unittest/python/python-fix-external.py index 4f589bb5f6..02fe805626 100644 --- a/unittest/python/python-fix-external.py +++ b/unittest/python/python-fix-external.py @@ -8,6 +8,10 @@ def callback_one(lmp, ntimestep, nlocal, tag, x, f): f[i][0] = float(ntimestep) f[i][1] = float(ntimestep) f[i][2] = float(ntimestep) + if ntimestep < 10: + lmp.fix_external_set_energy_global("ext",0.5) + else: + lmp.fix_external_set_energy_global("ext",1.0) class PythonExternal(unittest.TestCase): def testExternalCallback(self): @@ -35,6 +39,7 @@ class PythonExternal(unittest.TestCase): lmp.set_fix_external_callback("ext",callback_one,lmp) lmp.command("run 10 post no") self.assertAlmostEqual(lmp.get_thermo("temp"),1.0/30.0,14) + self.assertAlmostEqual(lmp.get_thermo("pe"),1.0/8.0,14) def testExternalArray(self): """Test fix external from Python with pf/array""" From d462bb3131c70172ef61f3d07a86a6f4ae9dc46d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 16 Jul 2021 22:44:04 -0400 Subject: [PATCH 05/19] fix off-by-one bug --- src/fix_external.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fix_external.cpp b/src/fix_external.cpp index 4bc1e7eebc..26c852fbd0 100644 --- a/src/fix_external.cpp +++ b/src/fix_external.cpp @@ -254,7 +254,7 @@ void FixExternal::set_vector_length(int n) void FixExternal::set_vector(int index, double value) { - if (index >= size_vector) + if (index > size_vector) error->all(FLERR,"Invalid set_vector index in fix external"); caller_vector[index-1] = value; } From fa654f2270233fac4b586afa5c95d8363ce14917 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 16 Jul 2021 23:41:25 -0400 Subject: [PATCH 06/19] add support for set_vector for fix external in c-library, python and unittest --- .gitignore | 1 + doc/src/Library_utility.rst | 24 ++++++ python/lammps/core.py | 37 ++++++++ src/library.cpp | 90 ++++++++++++++++++-- src/library.h | 2 +- unittest/c-library/test_library_external.cpp | 31 +++++-- unittest/python/python-fix-external.py | 19 ++++- 7 files changed, 188 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 14d9dbebc9..b71a750c7d 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,7 @@ Thumbs.db /build* /CMakeCache.txt /CMakeFiles/ +/Testing /Makefile /cmake_install.cmake /lmp diff --git a/doc/src/Library_utility.rst b/doc/src/Library_utility.rst index 2748d418b6..32fac6bcc8 100644 --- a/doc/src/Library_utility.rst +++ b/doc/src/Library_utility.rst @@ -8,7 +8,11 @@ functions. They do not directly call the LAMMPS library. - :cpp:func:`lammps_decode_image_flags` - :cpp:func:`lammps_set_fix_external_callback` - :cpp:func:`lammps_fix_external_set_energy_global` +- :cpp:func:`lammps_fix_external_set_energy_peratom` - :cpp:func:`lammps_fix_external_set_virial_global` +- :cpp:func:`lammps_fix_external_set_virial_peratom` +- :cpp:func:`lammps_fix_external_set_vector_length` +- :cpp:func:`lammps_fix_external_set_vector` - :cpp:func:`lammps_free` - :cpp:func:`lammps_is_running` - :cpp:func:`lammps_force_timeout` @@ -43,11 +47,31 @@ where such memory buffers were allocated that require the use of ----------------------- +.. doxygenfunction:: lammps_fix_external_set_energy_peratom + :project: progguide + +----------------------- + .. doxygenfunction:: lammps_fix_external_set_virial_global :project: progguide ----------------------- +.. doxygenfunction:: lammps_fix_external_set_virial_peratom + :project: progguide + +----------------------- + +.. doxygenfunction:: lammps_fix_external_set_vector_length + :project: progguide + +----------------------- + +.. doxygenfunction:: lammps_fix_external_set_vector + :project: progguide + +----------------------- + .. doxygenfunction:: lammps_free :project: progguide diff --git a/python/lammps/core.py b/python/lammps/core.py index 44cd51bb33..05192b5f6e 100644 --- a/python/lammps/core.py +++ b/python/lammps/core.py @@ -303,6 +303,9 @@ class lammps(object): self.lib.lammps_fix_external_set_energy_peratom.argtypes = [c_void_p, c_char_p, POINTER(c_double)] self.lib.lammps_fix_external_set_virial_peratom.argtypes = [c_void_p, c_char_p, POINTER(POINTER(c_double))] + self.lib.lammps_fix_external_set_vector_length.argtypes = [c_void_p, c_char_p, c_int] + self.lib.lammps_fix_external_set_vector.argtypes = [c_void_p, c_char_p, c_int, c_double] + # detect if Python is using a version of mpi4py that can pass communicators # only needed if LAMMPS has been compiled with MPI support. self.has_mpi4py = False @@ -1806,6 +1809,40 @@ class lammps(object): with ExceptionCheck(self): return self.lib.lammps_fix_external_set_energy_global(self.lmp, fix_id.encode(), eng) + # ------------------------------------------------------------------------- + def fix_external_set_vector_length(self, fix_id, length): + """Set the vector length for a global vector stored with fix external for analysis + + This is a wrapper around the :cpp:func:`lammps_fix_external_set_vector_length` function + of the C-library interface. + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param length: length of the global vector + :type: int + """ + + with ExceptionCheck(self): + return self.lib.lammps_fix_external_set_vector_length(self.lmp, fix_id.encode(), length) + + # ------------------------------------------------------------------------- + def fix_external_set_vector(self, fix_id, idx, val): + """Store a global vector value for a fix external instance with the given ID. + + This is a wrapper around the :cpp:func:`lammps_fix_external_set_vector` function + of the C-library interface. + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param idx: 1-based index of the value in the global vector + :type: int + :param val: value to be stored in the global vector + :type: float + """ + + with ExceptionCheck(self): + return self.lib.lammps_fix_external_set_vector(self.lmp, fix_id.encode(), idx, val) + # ------------------------------------------------------------------------- def get_neighlist(self, idx): diff --git a/src/library.cpp b/src/library.cpp index 4488114579..2c75657447 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -4821,7 +4821,7 @@ mode. The function has to have C language bindings with the prototype: void func(void *ptr, bigint timestep, int nlocal, tagint *ids, double **x, double **fexternal); -This is an alternative to the array mechanism set up by :cpp:func:`lammps_fix_external_set_force`. +This is an alternative to the array mechanism set up by :cpp:func:`lammps_fix_external_get_force`. Please see the documentation for :doc:`fix external ` for more information about how to use the fix and how to couple it with an @@ -4913,7 +4913,7 @@ double **lammps_fix_external_get_force(void *handle, const char *id) \verbatim embed:rst This is a companion function to :cpp:func:`lammps_set_fix_external_callback` and -:cpp:func:`lammps_fix_external_set_force` to also set the contribution +:cpp:func:`lammps_fix_external_get_force` to also set the contribution to the global energy from the external code. Please see the documentation for :doc:`fix external ` for @@ -4952,7 +4952,7 @@ void lammps_fix_external_set_energy_global(void *handle, const char *id, double \verbatim embed:rst This is a companion function to :cpp:func:`lammps_set_fix_external_callback` and -:cpp:func:`lammps_fix_external_set_force` to also set the contribution +:cpp:func:`lammps_fix_external_get_force` to also set the contribution to the global virial from the external code. Please see the documentation for :doc:`fix external ` for @@ -4991,7 +4991,7 @@ void lammps_fix_external_set_virial_global(void *handle, const char *id, double \verbatim embed:rst This is a companion function to :cpp:func:`lammps_set_fix_external_callback` and -:cpp:func:`lammps_fix_external_set_force` to also set the contribution +:cpp:func:`lammps_fix_external_get_force` to also set the contribution to the per-atom energy from the external code. Please see the documentation for :doc:`fix external ` for @@ -5030,7 +5030,7 @@ void lammps_fix_external_set_energy_peratom(void *handle, const char *id, double \verbatim embed:rst This is a companion function to :cpp:func:`lammps_set_fix_external_callback` and -:cpp:func:`lammps_fix_external_set_force` to also set the contribution +:cpp:func:`lammps_fix_external_get_force` to also set the contribution to the per-atom virial from the external code. Please see the documentation for :doc:`fix external ` for @@ -5064,6 +5064,86 @@ void lammps_fix_external_set_virial_peratom(void *handle, const char *id, double END_CAPTURE } +/** Set the vector length for a global vector stored with fix external for analysis + +\verbatim embed:rst + +This is a companion function to :cpp:func:`lammps_set_fix_external_callback` and +:cpp:func:`lammps_fix_external_get_force` to set the length of a global vector of +properties that will be stored with the fix via :cpp:func:`lammps_fix_external_set_vector`. + +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. + +\endverbatim + * + * \param handle pointer to a previously created LAMMPS instance cast to ``void *``. + * \param id fix ID of fix external instance + * \param len length of the global vector to be stored with the fix */ + +void lammps_fix_external_set_vector_length(void *handle, const char *id, int len) +{ + LAMMPS *lmp = (LAMMPS *) handle; + + BEGIN_CAPTURE + { + int ifix = lmp->modify->find_fix(id); + if (ifix < 0) + lmp->error->all(FLERR,"Can not find fix with ID '{}'!", id); + + Fix *fix = lmp->modify->fix[ifix]; + + if (strcmp("external",fix->style) != 0) + lmp->error->all(FLERR,"Fix '{}' is not of style external!", id); + + FixExternal *fext = (FixExternal*) fix; + fext->set_vector_length(len); + } + END_CAPTURE +} + +/** Store global vector for a fix external instance with the given ID. + +\verbatim embed:rst + +This is a companion function to :cpp:func:`lammps_set_fix_external_callback` and +:cpp:func:`lammps_fix_external_get_force` to set the values of a global vector of +properties that will be stored with the fix. The length of the vector +must be set beforehand with :cpp:func:`lammps_fix_external_set_vector_length`. + +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. + +\endverbatim + * + * \param handle pointer to a previously created LAMMPS instance cast to ``void *``. + * \param id fix ID of fix external instance + * \param idx 1 based index of in global vector + * \param val value to be stored in global vector */ + +void lammps_fix_external_set_vector(void *handle, const char *id, int idx, double val) +{ + LAMMPS *lmp = (LAMMPS *) handle; + + BEGIN_CAPTURE + { + int ifix = lmp->modify->find_fix(id); + if (ifix < 0) + lmp->error->all(FLERR,"Can not find fix with ID '{}'!", id); + + Fix *fix = lmp->modify->fix[ifix]; + + if (strcmp("external",fix->style) != 0) + lmp->error->all(FLERR,"Fix '{}' is not of style external!", id); + + FixExternal * fext = (FixExternal*) fix; + fext->set_vector(idx, val); + } + END_CAPTURE +} + /* ---------------------------------------------------------------------- */ /** Free memory buffer allocated by LAMMPS. diff --git a/src/library.h b/src/library.h index 25b5199dc7..654eda38fa 100644 --- a/src/library.h +++ b/src/library.h @@ -240,7 +240,7 @@ void lammps_fix_external_set_energy_peratom(void *handle, const char *id, double void lammps_fix_external_set_virial_global(void *handle, const char *id, double *virial); void lammps_fix_external_set_virial_peratom(void *handle, const char *id, double **virial); void lammps_fix_external_set_vector_length(void *handle, const char *id, int len); -void lammps_fix_external_set_virial_peratom(void *handle, const char *id, double **val); +void lammps_fix_external_set_vector(void *handle, const char *id, int idx, double val); void lammps_free(void *ptr); diff --git a/unittest/c-library/test_library_external.cpp b/unittest/c-library/test_library_external.cpp index 7c53daaef7..d44f3ff2ee 100644 --- a/unittest/c-library/test_library_external.cpp +++ b/unittest/c-library/test_library_external.cpp @@ -28,12 +28,20 @@ static void callback_one(void *handle, step_t timestep, int nlocal, tag_t *, dou { for (int i = 0; i < nlocal; ++i) f[i][0] = f[i][1] = f[i][2] = (double)timestep; - if (timestep < 10) - lammps_fix_external_set_energy_global(handle, "ext", 0.0); - else - lammps_fix_external_set_energy_global(handle, "ext", 1.0); + double v[6] = {1.0, 1.0, 1.0, 0.0, 0.0, 0.0}; lammps_fix_external_set_virial_global(handle, "ext", v); + if (timestep < 10) { + lammps_fix_external_set_energy_global(handle, "ext", 0.5); + lammps_fix_external_set_vector(handle, "ext", 1, timestep); + lammps_fix_external_set_vector(handle, "ext", 3, 1.0); + lammps_fix_external_set_vector(handle, "ext", 4, -0.25); + } else { + lammps_fix_external_set_energy_global(handle, "ext", 1.0); + lammps_fix_external_set_vector(handle, "ext", 2, timestep); + lammps_fix_external_set_vector(handle, "ext", 5, -1.0); + lammps_fix_external_set_vector(handle, "ext", 6, 0.25); + } } } @@ -57,11 +65,12 @@ TEST(lammps_external, callback) "pair_style zero 0.1\n" "pair_coeff 1 1\n" "velocity all set 0.1 0.0 -0.1\n" - "thermo 5\n" "fix 1 all nve\n" "fix ext all external pf/callback 5 1\n" + "thermo_style custom step temp pe ke etotal press\n" + "thermo 5\n" "fix_modify ext energy yes virial yes\n"); - + lammps_fix_external_set_vector_length(handle, "ext", 6); output = ::testing::internal::GetCapturedStdout(); if (verbose) std::cout << output; @@ -71,11 +80,19 @@ TEST(lammps_external, callback) double temp = lammps_get_thermo(handle, "temp"); double pe = lammps_get_thermo(handle, "pe"); double press = lammps_get_thermo(handle, "press"); - output = ::testing::internal::GetCapturedStdout(); + double val = 0.0; + double *valp; + for (int i = 0; i < 6; ++i) { + valp = (double *)lammps_extract_fix(handle, "ext", LMP_STYLE_GLOBAL, LMP_TYPE_VECTOR, i, 0); + val += *valp; + lammps_free(valp); + } + output = ::testing::internal::GetCapturedStdout(); if (verbose) std::cout << output; EXPECT_DOUBLE_EQ(temp, 1.0 / 30.0); EXPECT_DOUBLE_EQ(pe, 1.0 / 8.0); EXPECT_DOUBLE_EQ(press, 0.15416666666666667); + EXPECT_DOUBLE_EQ(val, 15); ::testing::internal::CaptureStdout(); lammps_close(handle); diff --git a/unittest/python/python-fix-external.py b/unittest/python/python-fix-external.py index 02fe805626..eab4687e2b 100644 --- a/unittest/python/python-fix-external.py +++ b/unittest/python/python-fix-external.py @@ -1,6 +1,6 @@ import sys,os,unittest from ctypes import * -from lammps import lammps +from lammps import lammps, LMP_STYLE_GLOBAL, LMP_TYPE_VECTOR # add timestep dependent force def callback_one(lmp, ntimestep, nlocal, tag, x, f): @@ -9,9 +9,15 @@ def callback_one(lmp, ntimestep, nlocal, tag, x, f): f[i][1] = float(ntimestep) f[i][2] = float(ntimestep) if ntimestep < 10: - lmp.fix_external_set_energy_global("ext",0.5) + lmp.fix_external_set_energy_global("ext", 0.5) + lmp.fix_external_set_vector("ext", 1, ntimestep) + lmp.fix_external_set_vector("ext", 3, 1.0) + lmp.fix_external_set_vector("ext", 4, -0.25) else: - lmp.fix_external_set_energy_global("ext",1.0) + lmp.fix_external_set_energy_global("ext", 1.0) + lmp.fix_external_set_vector("ext", 2, ntimestep) + lmp.fix_external_set_vector("ext", 5, -1.0) + lmp.fix_external_set_vector("ext", 6, 0.25) class PythonExternal(unittest.TestCase): def testExternalCallback(self): @@ -31,15 +37,22 @@ class PythonExternal(unittest.TestCase): pair_style zero 0.1 pair_coeff 1 1 velocity all set 0.1 0.0 -0.1 + thermo_style custom step temp pe ke etotal press thermo 5 fix 1 all nve fix ext all external pf/callback 5 1 + fix_modify ext energy yes virial yes """ lmp.commands_string(basic_system) + lmp.fix_external_set_vector_length("ext",6); lmp.set_fix_external_callback("ext",callback_one,lmp) lmp.command("run 10 post no") self.assertAlmostEqual(lmp.get_thermo("temp"),1.0/30.0,14) self.assertAlmostEqual(lmp.get_thermo("pe"),1.0/8.0,14) + val = 0.0 + for i in range(0,6): + val += lmp.extract_fix("ext",LMP_STYLE_GLOBAL,LMP_TYPE_VECTOR,nrow=i) + self.assertAlmostEqual(val,15.0,14) def testExternalArray(self): """Test fix external from Python with pf/array""" From f251bc544f90758a85819b9ecb6e4f4c31e58024 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 17 Jul 2021 07:38:53 -0400 Subject: [PATCH 07/19] support setting global virial for fix external from python --- python/lammps/core.py | 24 +++++++++++++++++++++--- src/library.cpp | 2 +- unittest/python/python-fix-external.py | 3 +++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/python/lammps/core.py b/python/lammps/core.py index 05192b5f6e..9ea2530cdc 100644 --- a/python/lammps/core.py +++ b/python/lammps/core.py @@ -1795,20 +1795,38 @@ class lammps(object): # ------------------------------------------------------------------------- def fix_external_set_energy_global(self, fix_id, eng): - """Get access to that array with per-atom forces of a fix external instance with a given fix ID. + """Set the global energy contribution for a fix external instance with the given ID. - This is a wrapper around the :cpp:func:`lammps_fix_external_get_force` function + This is a wrapper around the :cpp:func:`lammps_fix_external_set_energy_global` function of the C-library interface. :param fix_id: Fix-ID of a fix external instance :type: string - :param eng: potential energy to be added by fix external + :param eng: potential energy value to be added by fix external :type: float """ with ExceptionCheck(self): return self.lib.lammps_fix_external_set_energy_global(self.lmp, fix_id.encode(), eng) + # ------------------------------------------------------------------------- + + def fix_external_set_virial_global(self, fix_id, virial): + """Set the global virial contribution for a fix external instance with the given ID. + + This is a wrapper around the :cpp:func:`lammps_fix_external_set_virial_global` function + of the C-library interface. + + :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 + :type: float + """ + + cvirial = (6*c_double)(*virial) + with ExceptionCheck(self): + return self.lib.lammps_fix_external_set_virial_global(self.lmp, fix_id.encode(), cvirial) + # ------------------------------------------------------------------------- def fix_external_set_vector_length(self, fix_id, length): """Set the vector length for a global vector stored with fix external for analysis diff --git a/src/library.cpp b/src/library.cpp index 2c75657447..167c0c39b5 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -5103,7 +5103,7 @@ void lammps_fix_external_set_vector_length(void *handle, const char *id, int len END_CAPTURE } -/** Store global vector for a fix external instance with the given ID. +/** Store a global vector value for a fix external instance with the given ID. \verbatim embed:rst diff --git a/unittest/python/python-fix-external.py b/unittest/python/python-fix-external.py index eab4687e2b..1b46943b5a 100644 --- a/unittest/python/python-fix-external.py +++ b/unittest/python/python-fix-external.py @@ -4,6 +4,8 @@ from lammps import lammps, LMP_STYLE_GLOBAL, LMP_TYPE_VECTOR # add timestep dependent force def callback_one(lmp, ntimestep, nlocal, tag, x, f): + virial = [1.0, 1.0, 1.0, 0.0, 0.0, 0.0] + lmp.fix_external_set_virial_global("ext",virial) for i in range(nlocal): f[i][0] = float(ntimestep) f[i][1] = float(ntimestep) @@ -49,6 +51,7 @@ class PythonExternal(unittest.TestCase): lmp.command("run 10 post no") self.assertAlmostEqual(lmp.get_thermo("temp"),1.0/30.0,14) self.assertAlmostEqual(lmp.get_thermo("pe"),1.0/8.0,14) + self.assertAlmostEqual(lmp.get_thermo("press"),0.15416666666666667,14) val = 0.0 for i in range(0,6): val += lmp.extract_fix("ext",LMP_STYLE_GLOBAL,LMP_TYPE_VECTOR,nrow=i) From 8460d67eb200cb26ab335a85c84e557952cf3c6c Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 19 Jul 2021 00:25:00 -0400 Subject: [PATCH 08/19] update embedded documentation to correctly describe the functionality --- src/library.cpp | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/library.cpp b/src/library.cpp index 167c0c39b5..a6e4509c56 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -4914,7 +4914,9 @@ double **lammps_fix_external_get_force(void *handle, const char *id) This is a companion function to :cpp:func:`lammps_set_fix_external_callback` and :cpp:func:`lammps_fix_external_get_force` to also set the contribution -to the global energy from the external code. +to the global energy from the external code. The value of the *eng* +argument will be stored in the fix and applied on the current and all +following timesteps until changed. Please see the documentation for :doc:`fix external ` for more information about how to use the fix and how to couple it with an @@ -4951,9 +4953,11 @@ void lammps_fix_external_set_energy_global(void *handle, const char *id, double \verbatim embed:rst -This is a companion function to :cpp:func:`lammps_set_fix_external_callback` and -:cpp:func:`lammps_fix_external_get_force` to also set the contribution -to the global virial from the external code. +This is a companion function to :cpp:func:`lammps_set_fix_external_callback` +to set the contribution to the global virial from the external code +as part of the callback function. For this to work, the handle to the +LAMMPS object must be passed as the *ptr* argument when registering the +callback function. Please see the documentation for :doc:`fix external ` for more information about how to use the fix and how to couple it with an @@ -4990,9 +4994,12 @@ void lammps_fix_external_set_virial_global(void *handle, const char *id, double \verbatim embed:rst -This is a companion function to :cpp:func:`lammps_set_fix_external_callback` and -:cpp:func:`lammps_fix_external_get_force` to also set the contribution -to the per-atom energy from the external code. +This is a companion function to :cpp:func:`lammps_set_fix_external_callback` +to set the per-atom energy contribution due to the fix from the external code +as part of the callback function. For this to work, the handle to the +LAMMPS object must be passed as the *ptr* argument when registering the +callback function. No check is made whether the sum of the +contributions are consistent with any global added energy. Please see the documentation for :doc:`fix external ` for more information about how to use the fix and how to couple it with an @@ -5029,9 +5036,12 @@ void lammps_fix_external_set_energy_peratom(void *handle, const char *id, double \verbatim embed:rst -This is a companion function to :cpp:func:`lammps_set_fix_external_callback` and -:cpp:func:`lammps_fix_external_get_force` to also set the contribution -to the per-atom virial from the external code. +This is a companion function to :cpp:func:`lammps_set_fix_external_callback` +to set the per-atom virial contribution due to the fix from the external code +as part of the callback function. For this to work, the handle to the +LAMMPS object must be passed as the *ptr* argument when registering the +callback function. No check is made whether the sum of the +contributions are consistent with any globally added virial components. Please see the documentation for :doc:`fix external ` for more information about how to use the fix and how to couple it with an From 1c4e8aba122e5d792788464bf5f88988aa91f348 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 19 Jul 2021 00:29:27 -0400 Subject: [PATCH 09/19] add tests for per-atom values, global energy with pf/array mode --- unittest/c-library/test_library_external.cpp | 45 +++++++++++++++++--- unittest/python/python-fix-external.py | 10 +++-- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/unittest/c-library/test_library_external.cpp b/unittest/c-library/test_library_external.cpp index d44f3ff2ee..8acb7a43f4 100644 --- a/unittest/c-library/test_library_external.cpp +++ b/unittest/c-library/test_library_external.cpp @@ -24,7 +24,7 @@ typedef int32_t tag_t; typedef int64_t step_t; typedef int64_t tag_t; #endif -static void callback_one(void *handle, step_t timestep, int nlocal, tag_t *, double **, double **f) +static void callback(void *handle, step_t timestep, int nlocal, tag_t *, double **, double **f) { for (int i = 0; i < nlocal; ++i) f[i][0] = f[i][1] = f[i][2] = (double)timestep; @@ -42,6 +42,23 @@ static void callback_one(void *handle, step_t timestep, int nlocal, tag_t *, dou lammps_fix_external_set_vector(handle, "ext", 5, -1.0); lammps_fix_external_set_vector(handle, "ext", 6, 0.25); } + double *eatom = new double[nlocal]; + double **vatom = new double *[nlocal]; + vatom[0] = new double[nlocal * 6]; + eatom[0] = 0.0; + vatom[0][0] = vatom[0][1] = vatom[0][2] = vatom[0][3] = vatom[0][4] = vatom[0][5] = 0.0; + + for (int i = 1; i < nlocal; ++i) { + eatom[i] = 0.1 * i; + vatom[i] = vatom[0] + 6 * i; + vatom[i][0] = vatom[i][1] = vatom[i][2] = 0.1; + vatom[i][3] = vatom[i][4] = vatom[i][5] = -0.2; + } + lammps_fix_external_set_energy_peratom(handle, "ext", eatom); + lammps_fix_external_set_virial_peratom(handle, "ext", vatom); + delete[] eatom; + delete[] vatom[0]; + delete[] vatom; } } @@ -67,7 +84,10 @@ TEST(lammps_external, callback) "velocity all set 0.1 0.0 -0.1\n" "fix 1 all nve\n" "fix ext all external pf/callback 5 1\n" - "thermo_style custom step temp pe ke etotal press\n" + "compute eatm all pe/atom fix\n" + "compute vatm all stress/atom NULL fix\n" + "compute sum all reduce sum c_eatm c_vatm[*]\n" + "thermo_style custom step temp pe ke etotal press c_sum[*]\n" "thermo 5\n" "fix_modify ext energy yes virial yes\n"); lammps_fix_external_set_vector_length(handle, "ext", 6); @@ -75,7 +95,7 @@ TEST(lammps_external, callback) if (verbose) std::cout << output; ::testing::internal::CaptureStdout(); - lammps_set_fix_external_callback(handle, "ext", &callback_one, handle); + lammps_set_fix_external_callback(handle, "ext", &callback, handle); lammps_command(handle, "run 10 post no"); double temp = lammps_get_thermo(handle, "temp"); double pe = lammps_get_thermo(handle, "pe"); @@ -87,12 +107,21 @@ TEST(lammps_external, callback) val += *valp; lammps_free(valp); } + double *reduce = + (double *)lammps_extract_compute(handle, "sum", LMP_STYLE_GLOBAL, LMP_TYPE_VECTOR); output = ::testing::internal::GetCapturedStdout(); if (verbose) std::cout << output; EXPECT_DOUBLE_EQ(temp, 1.0 / 30.0); EXPECT_DOUBLE_EQ(pe, 1.0 / 8.0); EXPECT_DOUBLE_EQ(press, 0.15416666666666667); EXPECT_DOUBLE_EQ(val, 15); + EXPECT_DOUBLE_EQ(reduce[0], 2.8); + EXPECT_DOUBLE_EQ(reduce[1], -0.7); + EXPECT_DOUBLE_EQ(reduce[2], -0.7); + EXPECT_DOUBLE_EQ(reduce[3], -0.7); + EXPECT_DOUBLE_EQ(reduce[4], 1.4); + EXPECT_DOUBLE_EQ(reduce[5], 1.4); + EXPECT_DOUBLE_EQ(reduce[6], 1.4); ::testing::internal::CaptureStdout(); lammps_close(handle); @@ -120,9 +149,9 @@ TEST(lammps_external, array) "pair_style zero 0.1\n" "pair_coeff 1 1\n" "velocity all set 0.1 0.0 -0.1\n" - "thermo 5\n" "fix 1 all nve\n" - "fix ext all external pf/array 1\n"); + "fix ext all external pf/array 1\n" + "thermo 5\n"); output = ::testing::internal::GetCapturedStdout(); if (verbose) std::cout << output; @@ -132,22 +161,28 @@ TEST(lammps_external, array) int nlocal = lammps_extract_setting(handle, "nlocal"); for (int i = 0; i < nlocal; ++i) force[i][0] = force[i][1] = force[i][2] = 0.0; + lammps_fix_external_set_energy_global(handle, "ext", 0.5); lammps_command(handle, "run 5 post no"); double temp = lammps_get_thermo(handle, "temp"); + double pe = lammps_get_thermo(handle, "pe"); output = ::testing::internal::GetCapturedStdout(); if (verbose) std::cout << output; EXPECT_DOUBLE_EQ(temp, 4.0 / 525.0); + EXPECT_DOUBLE_EQ(pe, 1.0 / 16.0); ::testing::internal::CaptureStdout(); nlocal = lammps_extract_setting(handle, "nlocal"); force = lammps_fix_external_get_force(handle, "ext"); for (int i = 0; i < nlocal; ++i) force[i][0] = force[i][1] = force[i][2] = 6.0; + lammps_fix_external_set_energy_global(handle, "ext", 1.0); lammps_command(handle, "run 5 post no"); temp = lammps_get_thermo(handle, "temp"); + pe = lammps_get_thermo(handle, "pe"); output = ::testing::internal::GetCapturedStdout(); if (verbose) std::cout << output; EXPECT_DOUBLE_EQ(temp, 1.0 / 30.0); + EXPECT_DOUBLE_EQ(pe, 1.0 / 8.0); ::testing::internal::CaptureStdout(); lammps_close(handle); diff --git a/unittest/python/python-fix-external.py b/unittest/python/python-fix-external.py index 1b46943b5a..e1c511d480 100644 --- a/unittest/python/python-fix-external.py +++ b/unittest/python/python-fix-external.py @@ -4,8 +4,7 @@ from lammps import lammps, LMP_STYLE_GLOBAL, LMP_TYPE_VECTOR # add timestep dependent force def callback_one(lmp, ntimestep, nlocal, tag, x, f): - virial = [1.0, 1.0, 1.0, 0.0, 0.0, 0.0] - lmp.fix_external_set_virial_global("ext",virial) + lmp.fix_external_set_virial_global("ext",[1.0, 1.0, 1.0, 0.0, 0.0, 0.0]) for i in range(nlocal): f[i][0] = float(ntimestep) f[i][1] = float(ntimestep) @@ -74,9 +73,10 @@ class PythonExternal(unittest.TestCase): pair_style zero 0.1 pair_coeff 1 1 velocity all set 0.1 0.0 -0.1 - thermo 5 fix 1 all nve fix ext all external pf/array 1 + thermo_style custom step temp pe ke + thermo 5 """ lmp.commands_string(basic_system) force = lmp.fix_external_get_force("ext"); @@ -85,9 +85,11 @@ class PythonExternal(unittest.TestCase): force[i][0] = 0.0 force[i][1] = 0.0 force[i][2] = 0.0 + lmp.fix_external_set_energy_global("ext", 0.5) lmp.command("run 5 post no") self.assertAlmostEqual(lmp.get_thermo("temp"),4.0/525.0,14) + self.assertAlmostEqual(lmp.get_thermo("pe"),1.0/16.0,14) force = lmp.fix_external_get_force("ext"); nlocal = lmp.extract_setting("nlocal"); @@ -95,8 +97,10 @@ class PythonExternal(unittest.TestCase): force[i][0] = 6.0 force[i][1] = 6.0 force[i][2] = 6.0 + lmp.fix_external_set_energy_global("ext", 1.0) lmp.command("run 5 post no") self.assertAlmostEqual(lmp.get_thermo("temp"),1.0/30.0,14) + self.assertAlmostEqual(lmp.get_thermo("pe"),1.0/8.0,14) ############################## if __name__ == "__main__": From 9615867600189214066c7969e5eab3e2bf0aad13 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 21 Jul 2021 00:25:32 -0400 Subject: [PATCH 10/19] make contribution to global virial compatible with pf/array mode --- src/fix_external.cpp | 12 ++++++++---- src/fix_external.h | 1 + 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/fix_external.cpp b/src/fix_external.cpp index 26c852fbd0..99539e86cf 100644 --- a/src/fix_external.cpp +++ b/src/fix_external.cpp @@ -80,7 +80,7 @@ FixExternal::~FixExternal() atom->delete_callback(id,Atom::GROW); memory->destroy(fexternal); - delete [] caller_vector; + delete[] caller_vector; } /* ---------------------------------------------------------------------- */ @@ -163,6 +163,12 @@ void FixExternal::post_force(int vflag) f[i][1] += fexternal[i][1]; f[i][2] += fexternal[i][2]; } + + // add contribution to global virial from previously stored value + + if (vflag_global) + for (int i = 0; i < 6; ++i) + virial[i] = user_virial[i]; } } @@ -196,10 +202,8 @@ void FixExternal::set_energy_global(double caller_energy) void FixExternal::set_virial_global(double *caller_virial) { - if (!vflag_global) return; - for (int i = 0; i < 6; i++) - virial[i] = caller_virial[i]; + user_virial[i] = caller_virial[i]; } /* ---------------------------------------------------------------------- diff --git a/src/fix_external.h b/src/fix_external.h index f0f46cd4c5..16db5d5015 100644 --- a/src/fix_external.h +++ b/src/fix_external.h @@ -64,6 +64,7 @@ class FixExternal : public Fix { FnPtr callback; void *ptr_caller; double user_energy; + double user_virial[6]; double *caller_vector; }; From f6e9c308181c577ec26fc4d827fb052960ad5e7d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 21 Jul 2021 00:47:10 -0400 Subject: [PATCH 11/19] update unit tests --- unittest/c-library/test_library_external.cpp | 15 ++++++++++++--- unittest/python/python-fix-external.py | 9 +++++++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/unittest/c-library/test_library_external.cpp b/unittest/c-library/test_library_external.cpp index 8acb7a43f4..005b31fcab 100644 --- a/unittest/c-library/test_library_external.cpp +++ b/unittest/c-library/test_library_external.cpp @@ -162,13 +162,17 @@ TEST(lammps_external, array) for (int i = 0; i < nlocal; ++i) force[i][0] = force[i][1] = force[i][2] = 0.0; lammps_fix_external_set_energy_global(handle, "ext", 0.5); + double v[6] = {0.5, 0.5, 0.5, 0.0, 0.0, 0.0}; + lammps_fix_external_set_virial_global(handle, "ext", v); lammps_command(handle, "run 5 post no"); - double temp = lammps_get_thermo(handle, "temp"); - double pe = lammps_get_thermo(handle, "pe"); - output = ::testing::internal::GetCapturedStdout(); + double temp = lammps_get_thermo(handle, "temp"); + double pe = lammps_get_thermo(handle, "pe"); + double press = lammps_get_thermo(handle, "press"); + output = ::testing::internal::GetCapturedStdout(); if (verbose) std::cout << output; EXPECT_DOUBLE_EQ(temp, 4.0 / 525.0); EXPECT_DOUBLE_EQ(pe, 1.0 / 16.0); + EXPECT_DOUBLE_EQ(press, 0.069166666666666668); ::testing::internal::CaptureStdout(); nlocal = lammps_extract_setting(handle, "nlocal"); @@ -176,13 +180,18 @@ TEST(lammps_external, array) for (int i = 0; i < nlocal; ++i) force[i][0] = force[i][1] = force[i][2] = 6.0; lammps_fix_external_set_energy_global(handle, "ext", 1.0); + v[0] = v[1] = v[2] = 1.0; + v[3] = v[4] = v[5] = 0.0; + lammps_fix_external_set_virial_global(handle, "ext", v); lammps_command(handle, "run 5 post no"); temp = lammps_get_thermo(handle, "temp"); pe = lammps_get_thermo(handle, "pe"); + press = lammps_get_thermo(handle, "press"); output = ::testing::internal::GetCapturedStdout(); if (verbose) std::cout << output; EXPECT_DOUBLE_EQ(temp, 1.0 / 30.0); EXPECT_DOUBLE_EQ(pe, 1.0 / 8.0); + EXPECT_DOUBLE_EQ(press, 0.15416666666666667); ::testing::internal::CaptureStdout(); lammps_close(handle); diff --git a/unittest/python/python-fix-external.py b/unittest/python/python-fix-external.py index e1c511d480..2d4c50e454 100644 --- a/unittest/python/python-fix-external.py +++ b/unittest/python/python-fix-external.py @@ -75,7 +75,8 @@ class PythonExternal(unittest.TestCase): velocity all set 0.1 0.0 -0.1 fix 1 all nve fix ext all external pf/array 1 - thermo_style custom step temp pe ke + fix_modify ext energy yes virial yes + thermo_style custom step temp pe ke press thermo 5 """ lmp.commands_string(basic_system) @@ -86,10 +87,12 @@ class PythonExternal(unittest.TestCase): force[i][1] = 0.0 force[i][2] = 0.0 lmp.fix_external_set_energy_global("ext", 0.5) - + lmp.fix_external_set_virial_global("ext",[0.5, 0.5, 0.5, 0.0, 0.0, 0.0]) + lmp.command("run 5 post no") self.assertAlmostEqual(lmp.get_thermo("temp"),4.0/525.0,14) self.assertAlmostEqual(lmp.get_thermo("pe"),1.0/16.0,14) + self.assertAlmostEqual(lmp.get_thermo("press"),0.06916666666666667,14) force = lmp.fix_external_get_force("ext"); nlocal = lmp.extract_setting("nlocal"); @@ -98,9 +101,11 @@ class PythonExternal(unittest.TestCase): force[i][1] = 6.0 force[i][2] = 6.0 lmp.fix_external_set_energy_global("ext", 1.0) + lmp.fix_external_set_virial_global("ext",[1.0, 1.0, 1.0, 0.0, 0.0, 0.0]) lmp.command("run 5 post no") self.assertAlmostEqual(lmp.get_thermo("temp"),1.0/30.0,14) self.assertAlmostEqual(lmp.get_thermo("pe"),1.0/8.0,14) + self.assertAlmostEqual(lmp.get_thermo("press"),0.15416666666666667,14) ############################## if __name__ == "__main__": From 72744ea441fb2e0b04e4a8df058c2a1063ddb456 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 21 Jul 2021 12:08:19 -0400 Subject: [PATCH 12/19] update swig interface file to the latest changes in the c library header --- tools/swig/lammps.i | 46 ++++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/tools/swig/lammps.i b/tools/swig/lammps.i index 56547dda53..5bf47f2463 100644 --- a/tools/swig/lammps.i +++ b/tools/swig/lammps.i @@ -128,16 +128,27 @@ extern int lammps_id_name(void *, const char *, int, char *buffer, int buf_si extern int lammps_plugin_count(); extern int lammps_plugin_name(int, char *, char *, int); /* -extern int lammps_encode_image_flags(int ix, int iy, int iz); -extern void lammps_decode_image_flags(int image, int *flags); -extern int64_t lammps_encode_image_flags(int ix, int iy, int iz); -extern void lammps_decode_image_flags(int64_t image, int *flags); -extern void lammps_set_fix_external_callback(void *, char *, FixExternalFnPtr, void*); -extern void lammps_set_fix_external_callback(void *, char *, FixExternalFnPtr, void*); -extern void lammps_set_fix_external_callback(void *, char *, FixExternalFnPtr, void*); -extern void lammps_fix_external_set_energy_global(void *, char *, double); -extern void lammps_fix_external_set_virial_global(void *, char *, double *); + * Have not found a good way to map these functions in a general way. + * So some individual customization for the specific use case and compilation is needed. + * + extern int lammps_encode_image_flags(int ix, int iy, int iz); + extern void lammps_decode_image_flags(int image, int *flags); + extern int64_t lammps_encode_image_flags(int ix, int iy, int iz); + extern void lammps_decode_image_flags(int64_t image, int *flags); + + * Supporting the fix external callback mechanism will require extra code specific to the application. + typedef void (*FixExternalFnPtr)(void *, int64_t, int, int64_t *, double **, double **); + extern void lammps_set_fix_external_callback(void *handle, const char *id, FixExternalFnPtr funcptr, void *ptr); + * these two functions can only be used from the callback, so we don't support them either + extern void lammps_fix_external_set_energy_peratom(void *handle, const char *id, double *eng); + extern void lammps_fix_external_set_virial_peratom(void *handle, const char *id, double **virial); */ +extern double **lammps_fix_external_get_force(void *handle, const char *id); +extern void lammps_fix_external_set_energy_global(void *handle, const char *id, double eng); +extern void lammps_fix_external_set_virial_global(void *handle, const char *id, double *virial); +extern void lammps_fix_external_set_vector_length(void *handle, const char *id, int len); +extern void lammps_fix_external_set_vector(void *handle, const char *id, int idx, double val); + extern void lammps_free(void *ptr); extern int lammps_is_running(void *handle); extern void lammps_force_timeout(void *handle); @@ -255,16 +266,21 @@ extern int lammps_encode_image_flags(int ix, int iy, int iz); extern void lammps_decode_image_flags(int image, int *flags); extern int64_t lammps_encode_image_flags(int ix, int iy, int iz); extern void lammps_decode_image_flags(int64_t image, int *flags); -extern void lammps_set_fix_external_callback(void *, char *, FixExternalFnPtr, void*); -extern void lammps_set_fix_external_callback(void *, char *, FixExternalFnPtr, void*); -extern void lammps_set_fix_external_callback(void *, char *, FixExternalFnPtr, void*); -extern void lammps_fix_external_set_energy_global(void *, char *, double); -extern void lammps_fix_external_set_virial_global(void *, char *, double *); +typedef void (*FixExternalFnPtr)(void *, int64_t, int, int64_t *, double **, double **); +extern void lammps_set_fix_external_callback(void *handle, const char *id, FixExternalFnPtr funcptr, void *ptr); +extern void lammps_fix_external_set_energy_peratom(void *handle, const char *id, double *eng); +extern void lammps_fix_external_set_virial_peratom(void *handle, const char *id, double **virial); */ +extern double **lammps_fix_external_get_force(void *handle, const char *id); +extern void lammps_fix_external_set_energy_global(void *handle, const char *id, double eng); +extern void lammps_fix_external_set_virial_global(void *handle, const char *id, double *virial); +extern void lammps_fix_external_set_vector_length(void *handle, const char *id, int len); +extern void lammps_fix_external_set_vector(void *handle, const char *id, int idx, double val); + extern void lammps_free(void *ptr); extern int lammps_is_running(void *handle); extern void lammps_force_timeout(void *handle); extern int lammps_has_error(void *handle); extern int lammps_get_last_error_message(void *handle, char *buffer, int buf_size); -/* last revised for LAMMPS 8 April 2021 */ +/* last revised on 21 July 2021 */ From aa885a9d8df4a432fdc1141fc73934412ff0f0c6 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 21 Jul 2021 17:06:21 -0400 Subject: [PATCH 13/19] make virial processing use the total global virial --- src/fix_external.cpp | 37 +++++++++++++++++++++++++++-------- src/library.cpp | 46 ++++++++++++++++++++++++++++++-------------- 2 files changed, 61 insertions(+), 22 deletions(-) diff --git a/src/fix_external.cpp b/src/fix_external.cpp index 99539e86cf..f95c30c28d 100644 --- a/src/fix_external.cpp +++ b/src/fix_external.cpp @@ -15,6 +15,7 @@ #include "fix_external.h" #include "atom.h" +#include "comm.h" #include "error.h" #include "memory.h" #include "update.h" @@ -185,10 +186,13 @@ void FixExternal::min_post_force(int vflag) /* ---------------------------------------------------------------------- caller invokes this method to set its contribution to global energy + this is the *total* energy across all MPI ranks of the external code + and must be set for all MPI ranks. unlike other energy/virial set methods: do not just return if eflag_global is not set b/c input script could access this quantity via compute_scalar() even if eflag is not set on a particular timestep + this function is compatible with CALLBACK and ARRAY mode ------------------------------------------------------------------------- */ void FixExternal::set_energy_global(double caller_energy) @@ -197,22 +201,32 @@ void FixExternal::set_energy_global(double caller_energy) } /* ---------------------------------------------------------------------- - caller invokes this method to set its contribution to global virial + caller invokes this method to set its contribution to the global virial + for all MPI ranks. the virial value is the *total* contribution across + all MPI ranks of the external code and thus we need to divide by the + number of MPI ranks since the tallying code expects per MPI rank contributions. + this function is compatible with PF_CALLBACK and PF_ARRAY mode ------------------------------------------------------------------------- */ void FixExternal::set_virial_global(double *caller_virial) { + const double npscale = 1.0/(double)comm->nprocs; for (int i = 0; i < 6; i++) - user_virial[i] = caller_virial[i]; + user_virial[i] = npscale * caller_virial[i]; } /* ---------------------------------------------------------------------- - caller invokes this method to set its contribution to peratom energy + caller invokes this method to set its contribution to peratom energy. + this is applied to the *local* atoms only. + this function is compatible with PF_CALLBACK mode only since it tallies + its energy contributions directly into the accumulator arrays. ------------------------------------------------------------------------- */ void FixExternal::set_energy_peratom(double *caller_energy) { if (!eflag_atom) return; + if ((mode == PF_ARRAY) && (comm->me == 0)) + error->warning(FLERR,"Can only set energy/atom for fix external in pf/callback mode"); int nlocal = atom->nlocal; for (int i = 0; i < nlocal; i++) @@ -221,6 +235,9 @@ void FixExternal::set_energy_peratom(double *caller_energy) /* ---------------------------------------------------------------------- caller invokes this method to set its contribution to peratom virial + this is applied to the *local* atoms only. + this function is compatible with PF_CALLBACK mode only since it tallies + its virial contributions directly into the accumulator arrays. ------------------------------------------------------------------------- */ void FixExternal::set_virial_peratom(double **caller_virial) @@ -228,6 +245,8 @@ void FixExternal::set_virial_peratom(double **caller_virial) int i,j; if (!vflag_atom) return; + if ((mode == PF_ARRAY) && (comm->me == 0)) + error->warning(FLERR,"Can only set virial/atom for fix external in pf/callback mode"); int nlocal = atom->nlocal; for (i = 0; i < nlocal; i++) @@ -236,8 +255,8 @@ void FixExternal::set_virial_peratom(double **caller_virial) } /* ---------------------------------------------------------------------- - caller invokes this method to set length of vector of values - assume all vector values are extensive, could make this an option + caller invokes this method to set length of global vector of values + assume all vector values are extensive. ------------------------------------------------------------------------- */ void FixExternal::set_vector_length(int n) @@ -252,14 +271,15 @@ void FixExternal::set_vector_length(int n) } /* ---------------------------------------------------------------------- - caller invokes this method to set Index value in vector - index ranges from 1 to N inclusive + caller invokes this method to set value for item at "index" in vector + index is 1-based, thus index ranges from 1 to N inclusively. + Must be called from all MPI ranks. ------------------------------------------------------------------------- */ void FixExternal::set_vector(int index, double value) { if (index > size_vector) - error->all(FLERR,"Invalid set_vector index in fix external"); + error->all(FLERR,"Invalid set_vector index ({} of {}) in fix external",index,size_vector); caller_vector[index-1] = value; } @@ -290,6 +310,7 @@ double FixExternal::compute_vector(int n) double FixExternal::memory_usage() { double bytes = 3*atom->nmax * sizeof(double); + bytes += 6*sizeof(double); return bytes; } diff --git a/src/library.cpp b/src/library.cpp index a6e4509c56..0c897538a1 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -4806,8 +4806,8 @@ void lammps_decode_image_flags(imageint image, int *flags) /* ---------------------------------------------------------------------- */ -/** Set the callback function for a fix external instance with the given ID. - Optionally also set the pointer to the calling object. +/** Set up the callback function for a fix external instance with the given ID. + \verbatim embed:rst Fix :doc:`external ` allows programs that are running LAMMPS through @@ -4821,7 +4821,15 @@ mode. The function has to have C language bindings with the prototype: void func(void *ptr, bigint timestep, int nlocal, tagint *ids, double **x, double **fexternal); -This is an alternative to the array mechanism set up by :cpp:func:`lammps_fix_external_get_force`. +The argument *ptr* to this function will be stored in fix external and +the passed as the first argument calling the callback function `func()`. +This would usually be a pointer to the LAMMPS instance, i.e. the same +pointer as the *handle* argument. This would be needed to call +functions that set the global or per-atom energy or virial +contributions. + +The callback mechanism is on of the two modes of fix external. The +alternative is the array mode set up by :cpp:func:`lammps_fix_external_get_force`. Please see the documentation for :doc:`fix external ` for more information about how to use the fix and how to couple it with an @@ -4865,16 +4873,24 @@ its library interface to add or modify certain LAMMPS properties on specific timesteps, similar to the way other fixes do. This function provides access to the per-atom force storage in the fix -to be added to the individual atoms when using the "pf/array" mode. The -*fexternal* array can be accessed similar to the "native" per-atom -*arrays accessible via the :cpp:func:`lammps_extract_atom` function. -Because the underlying data structures can change as atoms migrate -between MPI processes, this function should be always called immediately -before the forces are going to be set. +external instance to be added to the individual atoms when using the +"pf/array" mode. The *fexternal* array can be accessed similar to the +"native" per-atom *arrays accessible via the +:cpp:func:`lammps_extract_atom` function. Please note that the array +stores forces for *local* atoms, in the order determined by the neighbor +list build. Because the underlying data structures can change as well as +the order of atom as they migrate between MPI processes, this function +should be always called immediately before the forces are going to be +set to get an up-to-date pointer. You can use +e.g. :cpp:func:`lammps_get_natoms` to obtain the number of local atoms +and thus the dimensions of the returned force array (``double force[nlocal][3]``). -This is an alternative to the callback mechanism set up by -:cpp:func:`lammps_set_fix_external_callback` with out using the callback -mechanism to call out to the external program. +This is an alternative to the callback mechanism in fix external set up by +:cpp:func:`lammps_set_fix_external_callback`. The main difference is +that this mechanism can be used when forces can be pre-computed and the +control alternates between LAMMPS and the external command, while the +callback mechanism can call the external code to compute the force when +the fix is triggered and needs them. Please see the documentation for :doc:`fix external ` for more information about how to use the fix and how to couple it with an @@ -4914,9 +4930,11 @@ double **lammps_fix_external_get_force(void *handle, const char *id) This is a companion function to :cpp:func:`lammps_set_fix_external_callback` and :cpp:func:`lammps_fix_external_get_force` to also set the contribution -to the global energy from the external code. The value of the *eng* +to the global energy from the external code. The value of the *eng* argument will be stored in the fix and applied on the current and all -following timesteps until changed. +following timesteps until changed by another call to this function. +When running in parallel, the value is the per-MPI process contribution, +not the total energy. Please see the documentation for :doc:`fix external ` for more information about how to use the fix and how to couple it with an From bb46dd7d1f765a3e9c5b926b102e59662ec39f9f Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Jul 2021 13:14:49 -0400 Subject: [PATCH 14/19] update embedded documentation for the library interface functions for accessing fix external --- src/library.cpp | 122 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 87 insertions(+), 35 deletions(-) diff --git a/src/library.cpp b/src/library.cpp index 0c897538a1..b9c2fbe4e9 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -4823,13 +4823,14 @@ mode. The function has to have C language bindings with the prototype: The argument *ptr* to this function will be stored in fix external and the passed as the first argument calling the callback function `func()`. -This would usually be a pointer to the LAMMPS instance, i.e. the same +This would usually be a pointer to the active LAMMPS instance, i.e. the same pointer as the *handle* argument. This would be needed to call -functions that set the global or per-atom energy or virial -contributions. +functions that set the global or per-atom energy or virial contributions +from within the callback function. -The callback mechanism is on of the two modes of fix external. The -alternative is the array mode set up by :cpp:func:`lammps_fix_external_get_force`. +The callback mechanism is one of the two modes of how forces and can be +applied to a simulation with the help of fix external. The alternative +is the array mode where you call :cpp:func:`lammps_fix_external_get_force`. Please see the documentation for :doc:`fix external ` for more information about how to use the fix and how to couple it with an @@ -4872,23 +4873,25 @@ Fix :doc:`external ` allows programs that are running LAMMPS throu its library interface to add or modify certain LAMMPS properties on specific timesteps, similar to the way other fixes do. -This function provides access to the per-atom force storage in the fix -external instance to be added to the individual atoms when using the -"pf/array" mode. The *fexternal* array can be accessed similar to the -"native" per-atom *arrays accessible via the +This function provides access to the per-atom force storage in a fix +external instance with the given fix-ID to be added to the individual +atoms when using the "pf/array" mode. The *fexternal* array can be +accessed like other "native" per-atom arrays accessible via the :cpp:func:`lammps_extract_atom` function. Please note that the array -stores forces for *local* atoms, in the order determined by the neighbor -list build. Because the underlying data structures can change as well as -the order of atom as they migrate between MPI processes, this function -should be always called immediately before the forces are going to be -set to get an up-to-date pointer. You can use -e.g. :cpp:func:`lammps_get_natoms` to obtain the number of local atoms -and thus the dimensions of the returned force array (``double force[nlocal][3]``). +stores holds the forces for *local* atoms for each MPI ranks, in the +order determined by the neighbor list build. Because the underlying +data structures can change as well as the order of atom as they migrate +between MPI processes because of the domain decomposition +parallelization, this function should be always called immediately +before the forces are going to be set to get an up-to-date pointer. + You can use e.g. :cpp:func:`lammps_get_natoms` to obtain the number +of local atoms `nlocal` and then assume the dimensions of the returned +force array as ``double force[nlocal][3]``. This is an alternative to the callback mechanism in fix external set up by :cpp:func:`lammps_set_fix_external_callback`. The main difference is -that this mechanism can be used when forces can be pre-computed and the -control alternates between LAMMPS and the external command, while the +that this mechanism can be used when forces are be pre-computed and the +control alternates between LAMMPS and the external code, while the callback mechanism can call the external code to compute the force when the fix is triggered and needs them. @@ -4933,8 +4936,12 @@ This is a companion function to :cpp:func:`lammps_set_fix_external_callback` and to the global energy from the external code. The value of the *eng* argument will be stored in the fix and applied on the current and all following timesteps until changed by another call to this function. -When running in parallel, the value is the per-MPI process contribution, -not the total energy. +The energy is in energy units as determined by the current :doc:`units ` +settings and is the **total** energy of the contribution. Thus when +running in parallel all MPI processes have to call this function with +the **same** value and this will be returned as scalar property of the +fix external instance when accessed in LAMMPS input commands or from +variables. Please see the documentation for :doc:`fix external ` for more information about how to use the fix and how to couple it with an @@ -4944,7 +4951,7 @@ external code. * * \param handle pointer to a previously created LAMMPS instance cast to ``void *``. * \param id fix ID of fix external instance - * \param eng energy to be added to the global energy */ + * \param eng total energy to be added to the global energy */ void lammps_fix_external_set_energy_global(void *handle, const char *id, double eng) { @@ -4972,10 +4979,18 @@ void lammps_fix_external_set_energy_global(void *handle, const char *id, double \verbatim embed:rst This is a companion function to :cpp:func:`lammps_set_fix_external_callback` -to set the contribution to the global virial from the external code -as part of the callback function. For this to work, the handle to the -LAMMPS object must be passed as the *ptr* argument when registering the -callback function. +and :cpp:func:`lammps_fix_external_get_force` to set the contribution to +the global virial from the external code. + +The 6 values of the *virial* array will be stored in the fix and applied +on the current and all following timesteps until changed by another call +to this function. The components of the virial need to be stored in the +order: *xx*, *yy*, *zz*, *xy*, *xz*, *yz*. In LAMMPS the virial is +stored internally as `stress*volume` in units of `pressure*volume` as +determined by the current :doc:`units ` settings and is the +**total** contribution. Thus when running in parallel all MPI processes +have to call this function with the **same** value and this will then +be added by fix external. Please see the documentation for :doc:`fix external ` for more information about how to use the fix and how to couple it with an @@ -5016,8 +5031,15 @@ This is a companion function to :cpp:func:`lammps_set_fix_external_callback` to set the per-atom energy contribution due to the fix from the external code as part of the callback function. For this to work, the handle to the LAMMPS object must be passed as the *ptr* argument when registering the -callback function. No check is made whether the sum of the -contributions are consistent with any global added energy. +callback function. + +.. note:: + + This function is fully independent from :cpp:func:`lammps_fix_external_set_energy_global` + and will **NOT** add any contributions to the global energy tally + and **NOT** check whether the sum of the contributions added here are + consistent with the global added energy. + Please see the documentation for :doc:`fix external ` for more information about how to use the fix and how to couple it with an @@ -5027,7 +5049,7 @@ external code. * * \param handle pointer to a previously created LAMMPS instance cast to ``void *``. * \param id fix ID of fix external instance - * \param eng energy to be added to the per-atom energy */ + * \param eng pointer to array of length nlocal with the energy to be added to the per-atom energy */ void lammps_fix_external_set_energy_peratom(void *handle, const char *id, double *eng) { @@ -5058,8 +5080,18 @@ This is a companion function to :cpp:func:`lammps_set_fix_external_callback` to set the per-atom virial contribution due to the fix from the external code as part of the callback function. For this to work, the handle to the LAMMPS object must be passed as the *ptr* argument when registering the -callback function. No check is made whether the sum of the -contributions are consistent with any globally added virial components. +callback function. + +.. note:: + + This function is fully independent from :cpp:func:`lammps_fix_external_set_virial_global` + and will **NOT** add any contributions to the global virial tally + and **NOT** check whether the sum of the contributions added here are + consistent with the global added virial. + +The order and units of the per-atom stress tensor elements are the same +as for the global virial. The code in fix external assumes the +dimensions of the per-atom virial array is ``double virial[nlocal][6]``. Please see the documentation for :doc:`fix external ` for more information about how to use the fix and how to couple it with an @@ -5069,7 +5101,7 @@ external code. * * \param handle pointer to a previously created LAMMPS instance cast to ``void *``. * \param id fix ID of fix external instance - * \param virial the 6 per-atom stress tensor components to be added to the per-atom virial */ + * \param virial a list of nlocal entries with the 6 per-atom stress tensor components to be added to the per-atom virial */ void lammps_fix_external_set_virial_peratom(void *handle, const char *id, double **virial) { @@ -5098,7 +5130,13 @@ void lammps_fix_external_set_virial_peratom(void *handle, const char *id, double This is a companion function to :cpp:func:`lammps_set_fix_external_callback` and :cpp:func:`lammps_fix_external_get_force` to set the length of a global vector of -properties that will be stored with the fix via :cpp:func:`lammps_fix_external_set_vector`. +properties that will be stored with the fix via +:cpp:func:`lammps_fix_external_set_vector`. + +This function needs to be called **before** a call to +:cpp:func:`lammps_fix_external_set_vector` and **before** a run or minimize +command. When running in parallel it must be called from **all** MPI +processes and with the same length parameter. Please see the documentation for :doc:`fix external ` for more information about how to use the fix and how to couple it with an @@ -5137,8 +5175,22 @@ void lammps_fix_external_set_vector_length(void *handle, const char *id, int len This is a companion function to :cpp:func:`lammps_set_fix_external_callback` and :cpp:func:`lammps_fix_external_get_force` to set the values of a global vector of -properties that will be stored with the fix. The length of the vector -must be set beforehand with :cpp:func:`lammps_fix_external_set_vector_length`. +properties that will be stored with the fix. And can be accessed from +within LAMMPS input commands (e.g. fix ave/time or variables) when used +in a vector context. + +This function needs to be called **after** a call to +:cpp:func:`lammps_fix_external_set_vector_length` and the and **before** a run or minimize +command. When running in parallel it must be called from **all** MPI +processes and with the **same** index and value parameters. The value +is assumed to be extensive. + +.. note:: + + The index in the *idx* parameter is 1-based, i.e. the first element + is set with idx = 1 and the last element of the vector with idx = N, + where N is the value of the *len* parameter of the call to + :cpp:func:`lammps_fix_external_set_vector_length`. Please see the documentation for :doc:`fix external ` for more information about how to use the fix and how to couple it with an @@ -5148,7 +5200,7 @@ external code. * * \param handle pointer to a previously created LAMMPS instance cast to ``void *``. * \param id fix ID of fix external instance - * \param idx 1 based index of in global vector + * \param idx 1-based index of in global vector * \param val value to be stored in global vector */ void lammps_fix_external_set_vector(void *handle, const char *id, int idx, double val) From 324ae3181bcfee8b1eca651136fe78073b0067e4 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Jul 2021 15:27:51 -0400 Subject: [PATCH 15/19] python interface for per-atom data for fix external --- python/lammps/core.py | 52 ++++++++++++++++++++++++-- python/lammps/numpy_wrapper.py | 20 ++++++++++ unittest/python/python-fix-external.py | 41 +++++++++++++++++++- 3 files changed, 109 insertions(+), 4 deletions(-) diff --git a/python/lammps/core.py b/python/lammps/core.py index 9ea2530cdc..e5ff88cda0 100644 --- a/python/lammps/core.py +++ b/python/lammps/core.py @@ -1749,8 +1749,8 @@ class lammps(object): - ntimestep is the current timestep - nlocal is the number of local atoms on the current MPI process - tag is a 1d NumPy array of integers representing the atom IDs of the local atoms - - x is a 2d NumPy array of floating point numbers of the coordinates of the local atoms - - f is a 2d NumPy array of floating point numbers of the forces on the local atoms that will be added + - 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 :param fix_id: Fix-ID of a fix external instance :type: string @@ -1777,7 +1777,7 @@ class lammps(object): # ------------------------------------------------------------------------- def fix_external_get_force(self, fix_id): - """Get access to that array with per-atom forces of a fix external instance with a given fix ID. + """Get access to the array with per-atom forces of a fix external instance with a given fix ID. This is a wrapper around the :cpp:func:`lammps_fix_external_get_force` function of the C-library interface. @@ -1827,6 +1827,52 @@ class lammps(object): with ExceptionCheck(self): return self.lib.lammps_fix_external_set_virial_global(self.lmp, fix_id.encode(), cvirial) + # ------------------------------------------------------------------------- + + def fix_external_set_energy_peratom(self, fix_id, eatom): + """Set the global energy contribution for a fix external instance with the given ID. + + This is a wrapper around the :cpp:func:`lammps_fix_external_set_energy_peratom` function + of the C-library interface. + + :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 + :type: float + """ + + nlocal = self.extract_setting('nlocal') + ceatom = (nlocal*c_double)(*eatom) + with ExceptionCheck(self): + return self.lib.lammps_fix_external_set_energy_peratom(self.lmp, fix_id.encode(), ceatom) + + # ------------------------------------------------------------------------- + + def fix_external_set_virial_peratom(self, fix_id, virial): + """Set the global virial contribution for a fix external instance with the given ID. + + This is a wrapper around the :cpp:func:`lammps_fix_external_set_virial_peratom` function + of the C-library interface. + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param eng: list of natoms lists with 6 floating point numbers to be added by fix external + :type: float + """ + + # copy virial data to C compatible buffer + nlocal = self.extract_setting('nlocal') + vbuf = (c_double * 6) + vptr = POINTER(c_double) + cvirial = (vptr * nlocal)() + for i in range(nlocal): + cvirial[i] = vbuf() + for j in range(6): + cvirial[i][j] = virial[i][j] + + with ExceptionCheck(self): + return self.lib.lammps_fix_external_set_virial_peratom(self.lmp, fix_id.encode(), cvirial) + # ------------------------------------------------------------------------- def fix_external_set_vector_length(self, fix_id, length): """Set the vector length for a global vector stored with fix external for analysis diff --git a/python/lammps/numpy_wrapper.py b/python/lammps/numpy_wrapper.py index 20fdf4cc68..1998930319 100644 --- a/python/lammps/numpy_wrapper.py +++ b/python/lammps/numpy_wrapper.py @@ -246,6 +246,26 @@ class numpy_wrapper: return np.ctypeslib.as_array(value) return value + # ------------------------------------------------------------------------- + + 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. + + This function is a wrapper around the + :py:meth:`lammps.fix_external_get_force() ` + method. It behaves the same as the original method, but returns a NumPy array instead + of a ``ctypes`` pointer. + + :param fix_id: Fix-ID of a fix external instance + :type: string + :return: requested data + :rtype: numpy.array + """ + import numpy as np + nlocal = self.lmp.extract_setting('nlocal') + value = self.lmp.fix_external_get_force(fix_id) + return self.darray(value,nlocal,3) + # ------------------------------------------------------------------------- def get_neighlist(self, idx): diff --git a/unittest/python/python-fix-external.py b/unittest/python/python-fix-external.py index 2d4c50e454..8b85d2ccd1 100644 --- a/unittest/python/python-fix-external.py +++ b/unittest/python/python-fix-external.py @@ -20,6 +20,18 @@ def callback_one(lmp, ntimestep, nlocal, tag, x, f): lmp.fix_external_set_vector("ext", 5, -1.0) lmp.fix_external_set_vector("ext", 6, 0.25) + eatom = [0.0,0.1,0.2,0.3,0.4,0.5,0.6,0.7] + vatom = [ [0.1,0.0,0.0,0.0,0.0,0.0], + [0.0,0.2,0.0,0.0,0.0,0.0], + [0.0,0.0,0.3,0.0,0.0,0.0], + [0.0,0.0,0.0,0.4,0.0,0.0], + [0.0,0.0,0.0,0.0,0.5,0.0], + [0.0,0.0,0.0,0.0,0.0,0.6], + [0.0,0.0,0.0,0.0,-7.0,0.0], + [0.0,-8.0,0.0,0.0,0.0,0.0] ] + lmp.fix_external_set_energy_peratom("ext",eatom) + lmp.fix_external_set_virial_peratom("ext",vatom) + class PythonExternal(unittest.TestCase): def testExternalCallback(self): """Test fix external from Python with pf/callback""" @@ -42,6 +54,10 @@ class PythonExternal(unittest.TestCase): thermo 5 fix 1 all nve fix ext all external pf/callback 5 1 + compute eatm all pe/atom fix + compute vatm all stress/atom NULL fix + compute sum all reduce sum c_eatm c_vatm[*] + thermo_style custom step temp pe ke etotal press c_sum[*] fix_modify ext energy yes virial yes """ lmp.commands_string(basic_system) @@ -51,6 +67,14 @@ class PythonExternal(unittest.TestCase): self.assertAlmostEqual(lmp.get_thermo("temp"),1.0/30.0,14) self.assertAlmostEqual(lmp.get_thermo("pe"),1.0/8.0,14) self.assertAlmostEqual(lmp.get_thermo("press"),0.15416666666666667,14) + reduce = lmp.extract_compute("sum", LMP_STYLE_GLOBAL, LMP_TYPE_VECTOR) + self.assertAlmostEqual(reduce[0],2.8,14) + self.assertAlmostEqual(reduce[1],-0.1,14) + self.assertAlmostEqual(reduce[2],7.8,14) + self.assertAlmostEqual(reduce[3],-0.3,14) + self.assertAlmostEqual(reduce[4],-0.4,14) + self.assertAlmostEqual(reduce[5],6.5,14) + self.assertAlmostEqual(reduce[6],-0.6,14) val = 0.0 for i in range(0,6): val += lmp.extract_fix("ext",LMP_STYLE_GLOBAL,LMP_TYPE_VECTOR,nrow=i) @@ -59,6 +83,12 @@ class PythonExternal(unittest.TestCase): def testExternalArray(self): """Test fix external from Python with pf/array""" + try: + import numpy + NUMPY_INSTALLED = True + except ImportError: + NUMPY_INSTALLED = False + machine=None if 'LAMMPS_MACHINE_NAME' in os.environ: machine=os.environ['LAMMPS_MACHINE_NAME'] @@ -93,6 +123,11 @@ class PythonExternal(unittest.TestCase): self.assertAlmostEqual(lmp.get_thermo("temp"),4.0/525.0,14) self.assertAlmostEqual(lmp.get_thermo("pe"),1.0/16.0,14) self.assertAlmostEqual(lmp.get_thermo("press"),0.06916666666666667,14) + if NUMPY_INSTALLED: + npforce = lmp.numpy.fix_external_get_force("ext") + self.assertEqual(len(npforce),8) + self.assertEqual(len(npforce[0]),3) + self.assertEqual(npforce[1][1],0.0) force = lmp.fix_external_get_force("ext"); nlocal = lmp.extract_setting("nlocal"); @@ -106,8 +141,12 @@ class PythonExternal(unittest.TestCase): self.assertAlmostEqual(lmp.get_thermo("temp"),1.0/30.0,14) self.assertAlmostEqual(lmp.get_thermo("pe"),1.0/8.0,14) self.assertAlmostEqual(lmp.get_thermo("press"),0.15416666666666667,14) + if NUMPY_INSTALLED: + npforce = lmp.numpy.fix_external_get_force("ext") + self.assertEqual(npforce[0][0],6.0) + self.assertEqual(npforce[3][1],6.0) + self.assertEqual(npforce[7][2],6.0) ############################## if __name__ == "__main__": unittest.main() - From bf8bde5b03a4d00010375c4889d164209c965242 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Jul 2021 16:59:04 -0400 Subject: [PATCH 16/19] implement numpy wrapper for setting per-atom energy. virial not yet implemented --- python/lammps/core.py | 16 ++++----- python/lammps/numpy_wrapper.py | 45 +++++++++++++++++++++++++- src/library.cpp | 2 +- unittest/python/python-fix-external.py | 7 ++++ 4 files changed, 60 insertions(+), 10 deletions(-) diff --git a/python/lammps/core.py b/python/lammps/core.py index e5ff88cda0..269b716c13 100644 --- a/python/lammps/core.py +++ b/python/lammps/core.py @@ -1830,7 +1830,7 @@ class lammps(object): # ------------------------------------------------------------------------- def fix_external_set_energy_peratom(self, fix_id, eatom): - """Set the global energy contribution for a fix external instance with the given ID. + """Set the per-atom energy contribution for a fix external instance with the given ID. This is a wrapper around the :cpp:func:`lammps_fix_external_set_energy_peratom` function of the C-library interface. @@ -1848,15 +1848,15 @@ class lammps(object): # ------------------------------------------------------------------------- - def fix_external_set_virial_peratom(self, fix_id, virial): - """Set the global virial contribution for a fix external instance with the given ID. + def fix_external_set_virial_peratom(self, fix_id, vatom): + """Set the per-atom virial contribution for a fix external instance with the given ID. This is a wrapper around the :cpp:func:`lammps_fix_external_set_virial_peratom` function of the C-library interface. :param fix_id: Fix-ID of a fix external instance :type: string - :param eng: list of natoms lists with 6 floating point numbers to be added by fix external + :param vatom: list of natoms lists with 6 floating point numbers to be added by fix external :type: float """ @@ -1864,14 +1864,14 @@ class lammps(object): nlocal = self.extract_setting('nlocal') vbuf = (c_double * 6) vptr = POINTER(c_double) - cvirial = (vptr * nlocal)() + c_virial = (vptr * nlocal)() for i in range(nlocal): - cvirial[i] = vbuf() + c_virial[i] = vbuf() for j in range(6): - cvirial[i][j] = virial[i][j] + c_virial[i][j] = vatom[i][j] with ExceptionCheck(self): - return self.lib.lammps_fix_external_set_virial_peratom(self.lmp, fix_id.encode(), cvirial) + return self.lib.lammps_fix_external_set_virial_peratom(self.lmp, fix_id.encode(), c_virial) # ------------------------------------------------------------------------- def fix_external_set_vector_length(self, fix_id, length): diff --git a/python/lammps/numpy_wrapper.py b/python/lammps/numpy_wrapper.py index 1998930319..7752d7902e 100644 --- a/python/lammps/numpy_wrapper.py +++ b/python/lammps/numpy_wrapper.py @@ -266,7 +266,50 @@ class numpy_wrapper: value = self.lmp.fix_external_get_force(fix_id) return self.darray(value,nlocal,3) - # ------------------------------------------------------------------------- + # ------------------------------------------------------------------------- + + def fix_external_set_energy_peratom(self, fix_id, eatom): + """Set the per-atom energy contribution for a fix external instance with the given ID. + + This function is an alternative to + :py:meth:`lammps.fix_external_set_energy_peratom() ` + method. It behaves the same as the original method, but accepts a NumPy array + instead of a list as argument. + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param eatom: per-atom potential energy + :type: numpy.array + """ + import numpy as np + nlocal = self.lmp.extract_setting('nlocal') + c_double_p = POINTER(c_double) + value = eatom.astype(np.double) + return self.lmp.lib.lammps_fix_external_set_energy_peratom(self.lmp.lmp, fix_id.encode(), + value.ctypes.data_as(c_double_p)) + + # ------------------------------------------------------------------------- + + def fix_external_set_virial_peratom(self, fix_id, vatom): + """Set the per-atom virial contribution for a fix external instance with the given ID. + + This function is an alternative to + :py:meth:`lammps.fix_external_set_virial_peratom() ` + method. It behaves the same as the original method, but accepts a NumPy array + instead of a list as argument. + + .. note:: + + This function is not yet implemented. + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param eatom: per-atom potential energy + :type: numpy.array + """ + raise Exception('fix_external_set_virial_peratom() not yet implemented for NumPy arrays') + + # ------------------------------------------------------------------------- def get_neighlist(self, idx): """Returns an instance of :class:`NumPyNeighList` which wraps access to the neighbor list with the given index diff --git a/src/library.cpp b/src/library.cpp index b9c2fbe4e9..7d49868158 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -5189,7 +5189,7 @@ is assumed to be extensive. The index in the *idx* parameter is 1-based, i.e. the first element is set with idx = 1 and the last element of the vector with idx = N, - where N is the value of the *len* parameter of the call to + where N is the value of the *len* parameter of the call to :cpp:func:`lammps_fix_external_set_vector_length`. Please see the documentation for :doc:`fix external ` for diff --git a/unittest/python/python-fix-external.py b/unittest/python/python-fix-external.py index 8b85d2ccd1..d7928ccbe2 100644 --- a/unittest/python/python-fix-external.py +++ b/unittest/python/python-fix-external.py @@ -32,6 +32,13 @@ def callback_one(lmp, ntimestep, nlocal, tag, x, f): lmp.fix_external_set_energy_peratom("ext",eatom) lmp.fix_external_set_virial_peratom("ext",vatom) + #import numpy as np + #eng = np.array(eatom) + #vir = np.array(vatom) + + #lmp.numpy.fix_external_set_energy_peratom("ext",eng) + #lmp.numpy.fix_external_set_virial_peratom("ext",vir) # not yet implemented + class PythonExternal(unittest.TestCase): def testExternalCallback(self): """Test fix external from Python with pf/callback""" From a078d1ba53f5698ea58b11456a01a6437bab7d10 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Jul 2021 22:49:16 -0400 Subject: [PATCH 17/19] check energy and virial per atom arrays for correct size --- python/lammps/core.py | 6 ++++++ python/lammps/numpy_wrapper.py | 11 ++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/python/lammps/core.py b/python/lammps/core.py index 269b716c13..d981243503 100644 --- a/python/lammps/core.py +++ b/python/lammps/core.py @@ -1842,6 +1842,8 @@ class lammps(object): """ nlocal = self.extract_setting('nlocal') + if len(eatom) < nlocal: + raise Exception('per-atom energy list length must be at least nlocal') ceatom = (nlocal*c_double)(*eatom) with ExceptionCheck(self): return self.lib.lammps_fix_external_set_energy_peratom(self.lmp, fix_id.encode(), ceatom) @@ -1862,6 +1864,10 @@ class lammps(object): # copy virial data to C compatible buffer nlocal = self.extract_setting('nlocal') + if len(vatom) < nlocal: + raise Exception('per-atom virial first dimension must be at least nlocal') + if len(vatom[0]) != 6: + raise Exception('per-atom virial second dimension must be 6') vbuf = (c_double * 6) vptr = POINTER(c_double) c_virial = (vptr * nlocal)() diff --git a/python/lammps/numpy_wrapper.py b/python/lammps/numpy_wrapper.py index 7752d7902e..29a6f9d74e 100644 --- a/python/lammps/numpy_wrapper.py +++ b/python/lammps/numpy_wrapper.py @@ -283,6 +283,9 @@ class numpy_wrapper: """ import numpy as np nlocal = self.lmp.extract_setting('nlocal') + if len(eatom) < nlocal: + raise Exception('per-atom energy dimension must be at least nlocal') + c_double_p = POINTER(c_double) value = eatom.astype(np.double) return self.lmp.lib.lammps_fix_external_set_energy_peratom(self.lmp.lmp, fix_id.encode(), @@ -307,7 +310,13 @@ class numpy_wrapper: :param eatom: per-atom potential energy :type: numpy.array """ - raise Exception('fix_external_set_virial_peratom() not yet implemented for NumPy arrays') + import numpy as np + nlocal = self.lmp.extract_setting('nlocal') + if len(vatom) < nlocal: + raise Exception('per-atom virial first dimension must be at least nlocal') + if len(vatom[0]) != 6: + raise Exception('per-atom virial second dimension must be 6') + # ------------------------------------------------------------------------- From c8cc5ecb9fcc4848e2c4447a7d420fc839f433a1 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Jul 2021 22:50:05 -0400 Subject: [PATCH 18/19] implement setting per-atom virial from numpy array (thanks to stackoverflow) --- python/lammps/numpy_wrapper.py | 16 +++++++--- unittest/python/python-fix-external.py | 43 ++++++++++++++++++-------- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/python/lammps/numpy_wrapper.py b/python/lammps/numpy_wrapper.py index 29a6f9d74e..6f4503a9c8 100644 --- a/python/lammps/numpy_wrapper.py +++ b/python/lammps/numpy_wrapper.py @@ -17,7 +17,7 @@ ################################################################################ import warnings -from ctypes import POINTER, c_double, c_int, c_int32, c_int64, cast +from ctypes import POINTER, c_void_p, c_char_p, c_double, c_int, c_int32, c_int64, cast from .constants import * # lgtm [py/polluting-import] @@ -301,10 +301,6 @@ class numpy_wrapper: method. It behaves the same as the original method, but accepts a NumPy array instead of a list as argument. - .. note:: - - This function is not yet implemented. - :param fix_id: Fix-ID of a fix external instance :type: string :param eatom: per-atom potential energy @@ -317,6 +313,16 @@ class numpy_wrapper: if len(vatom[0]) != 6: raise Exception('per-atom virial second dimension must be 6') + c_double_pp = np.ctypeslib.ndpointer(dtype=np.uintp, ndim=1, flags='C') + + # recast numpy array to be compatible with library interface + value = (vatom.__array_interface__['data'][0] + + np.arange(vatom.shape[0])*vatom.strides[0]).astype(np.uintp) + + # change prototype to our custom type + self.lmp.lib.lammps_fix_external_set_virial_peratom.argtypes = [ c_void_p, c_char_p, c_double_pp ] + + self.lmp.lib.lammps_fix_external_set_virial_peratom(self.lmp.lmp, fix_id.encode(), value) # ------------------------------------------------------------------------- diff --git a/unittest/python/python-fix-external.py b/unittest/python/python-fix-external.py index d7928ccbe2..c061b9f466 100644 --- a/unittest/python/python-fix-external.py +++ b/unittest/python/python-fix-external.py @@ -2,6 +2,12 @@ import sys,os,unittest from ctypes import * from lammps import lammps, LMP_STYLE_GLOBAL, LMP_TYPE_VECTOR +try: + import numpy + NUMPY_INSTALLED = True +except ImportError: + NUMPY_INSTALLED = False + # add timestep dependent force def callback_one(lmp, ntimestep, nlocal, tag, x, f): lmp.fix_external_set_virial_global("ext",[1.0, 1.0, 1.0, 0.0, 0.0, 0.0]) @@ -29,17 +35,21 @@ def callback_one(lmp, ntimestep, nlocal, tag, x, f): [0.0,0.0,0.0,0.0,0.0,0.6], [0.0,0.0,0.0,0.0,-7.0,0.0], [0.0,-8.0,0.0,0.0,0.0,0.0] ] - lmp.fix_external_set_energy_peratom("ext",eatom) - lmp.fix_external_set_virial_peratom("ext",vatom) + if ntimestep < 5: + lmp.fix_external_set_energy_peratom("ext",eatom) + lmp.fix_external_set_virial_peratom("ext",vatom) + else: + import numpy as np + eng = np.array(eatom) + vir = np.array(vatom) - #import numpy as np - #eng = np.array(eatom) - #vir = np.array(vatom) + lmp.numpy.fix_external_set_energy_peratom("ext",eng) + lmp.numpy.fix_external_set_virial_peratom("ext",vir) - #lmp.numpy.fix_external_set_energy_peratom("ext",eng) - #lmp.numpy.fix_external_set_virial_peratom("ext",vir) # not yet implemented + # ------------------------------------------------------------------------ class PythonExternal(unittest.TestCase): + @unittest.skipIf(not NUMPY_INSTALLED, "NumPy is not available") def testExternalCallback(self): """Test fix external from Python with pf/callback""" @@ -70,10 +80,23 @@ class PythonExternal(unittest.TestCase): lmp.commands_string(basic_system) lmp.fix_external_set_vector_length("ext",6); lmp.set_fix_external_callback("ext",callback_one,lmp) + + # check setting per-atom data with python lists + lmp.command("run 0 post no") + reduce = lmp.extract_compute("sum", LMP_STYLE_GLOBAL, LMP_TYPE_VECTOR) + self.assertAlmostEqual(reduce[0],2.8,14) + self.assertAlmostEqual(reduce[1],-0.1,14) + self.assertAlmostEqual(reduce[2],7.8,14) + self.assertAlmostEqual(reduce[3],-0.3,14) + self.assertAlmostEqual(reduce[4],-0.4,14) + self.assertAlmostEqual(reduce[5],6.5,14) + self.assertAlmostEqual(reduce[6],-0.6,14) + lmp.command("run 10 post no") self.assertAlmostEqual(lmp.get_thermo("temp"),1.0/30.0,14) self.assertAlmostEqual(lmp.get_thermo("pe"),1.0/8.0,14) self.assertAlmostEqual(lmp.get_thermo("press"),0.15416666666666667,14) + # check setting per-atom data numpy arrays reduce = lmp.extract_compute("sum", LMP_STYLE_GLOBAL, LMP_TYPE_VECTOR) self.assertAlmostEqual(reduce[0],2.8,14) self.assertAlmostEqual(reduce[1],-0.1,14) @@ -90,12 +113,6 @@ class PythonExternal(unittest.TestCase): def testExternalArray(self): """Test fix external from Python with pf/array""" - try: - import numpy - NUMPY_INSTALLED = True - except ImportError: - NUMPY_INSTALLED = False - machine=None if 'LAMMPS_MACHINE_NAME' in os.environ: machine=os.environ['LAMMPS_MACHINE_NAME'] From c33bead8b1a9b2c412666f178d8edf23b5c31303 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 22 Jul 2021 23:26:22 -0400 Subject: [PATCH 19/19] silence static code analysis warning --- src/MISC/fix_pair_tracker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MISC/fix_pair_tracker.cpp b/src/MISC/fix_pair_tracker.cpp index 7939485aff..755025baed 100644 --- a/src/MISC/fix_pair_tracker.cpp +++ b/src/MISC/fix_pair_tracker.cpp @@ -246,7 +246,7 @@ void FixPairTracker::reallocate(int n) double FixPairTracker::memory_usage() { - double bytes = nmax * nvalues * sizeof(double); + double bytes = nmax * (double) nvalues * sizeof(double); bytes += nmax * 2 * sizeof(int); return bytes; }