expand library interface for fix external functionality

This commit is contained in:
Axel Kohlmeyer
2021-07-16 17:25:47 -04:00
parent 8b1dedf04a
commit f3dc13c9dd
6 changed files with 350 additions and 29 deletions

View File

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

View File

@ -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
# -------------------------------------------------------------------------

View File

@ -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 <fix_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 <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 <fix_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 <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 <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 <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 <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 <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.

View File

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

View File

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

View File

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