document and add unit tests for lammps_set_fix_external_callback()

This commit is contained in:
Axel Kohlmeyer
2021-07-16 14:51:04 -04:00
parent 95792ac928
commit 88e363c0bb
6 changed files with 159 additions and 14 deletions

View File

@ -4804,15 +4804,38 @@ void lammps_decode_image_flags(imageint image, int *flags)
flags[2] = (image >> IMG2BITS) - IMGMAX; 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 <fix_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 <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; LAMMPS *lmp = (LAMMPS *) handle;
FixExternal::FnPtr callback = (FixExternal::FnPtr) callback_ptr; FixExternal::FnPtr callback = (FixExternal::FnPtr) funcptr;
BEGIN_CAPTURE BEGIN_CAPTURE
{ {
@ -4823,18 +4846,16 @@ void lammps_set_fix_external_callback(void *handle, char *id, FixExternalFnPtr c
Fix *fix = lmp->modify->fix[ifix]; Fix *fix = lmp->modify->fix[ifix];
if (strcmp("external",fix->style) != 0) if (strcmp("external",fix->style) != 0)
lmp->error->all(FLERR,"Fix '{}' is not of style " lmp->error->all(FLERR,"Fix '{}' is not of style 'external'", id);
"external!", id);
FixExternal * fext = (FixExternal*) fix; FixExternal *fext = (FixExternal *) fix;
fext->set_callback(callback, caller); fext->set_callback(callback, ptr);
} }
END_CAPTURE END_CAPTURE
} }
/* set global energy contribution from fix external */ /* set global energy contribution from fix external */
void lammps_fix_external_set_energy_global(void *handle, char *id, void lammps_fix_external_set_energy_global(void *handle, char *id, double energy)
double energy)
{ {
LAMMPS *lmp = (LAMMPS *) handle; 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) 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; FixExternal *fext = (FixExternal*) fix;
fext->set_energy_global(energy); fext->set_energy_global(energy);
} }
END_CAPTURE END_CAPTURE

View File

@ -226,7 +226,7 @@ void lammps_decode_image_flags(int64_t image, int *flags);
#if defined(LAMMPS_BIGBIG) #if defined(LAMMPS_BIGBIG)
typedef void (*FixExternalFnPtr)(void *, int64_t, int, int64_t *, double **, double **); 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) #elif defined(LAMMPS_SMALLBIG)
typedef void (*FixExternalFnPtr)(void *, int64_t, int, int *, double **, double **); 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 *, char *, FixExternalFnPtr, void *);

View File

@ -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) target_link_libraries(test_library_commands PRIVATE lammps GTest::GTest GTest::GMock)
add_test(LibraryCommands test_library_commands) 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) 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_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}) target_compile_definitions(test_library_properties PRIVATE -DTEST_INPUT_FOLDER=${CMAKE_CURRENT_SOURCE_DIR})

View File

@ -0,0 +1,73 @@
// unit tests creating LAMMPS instances via the library interface
#include "library.h"
#include <cinttypes>
#include <string>
#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;
}

View File

@ -85,6 +85,11 @@ if(Python_EXECUTABLE)
COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-formats.py -v COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-formats.py -v
WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH})
set_tests_properties(PythonFormats PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") 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() else()
message(STATUS "Skipping Tests for the LAMMPS Python Module: no suitable Python interpreter") message(STATUS "Skipping Tests for the LAMMPS Python Module: no suitable Python interpreter")
endif() endif()

View File

@ -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()