Merge branch 'master' into collected-small-changes

This commit is contained in:
Axel Kohlmeyer 2021-04-01 10:11:59 -04:00
commit 0b60650c1b
No known key found for this signature in database
GPG Key ID: D9B44E93BF0C375A
24 changed files with 665 additions and 134 deletions

View File

@ -101,6 +101,9 @@ and parsing files or arguments.
.. doxygenfunction:: split_words
:project: progguide
.. doxygenfunction:: split_lines
:project: progguide
.. doxygenfunction:: strmatch
:project: progguide

View File

@ -740,6 +740,7 @@ void Neighbor::build_nbor_list(double **x, const int inum, const int host_inum,
// If binning on GPU, do this now
if (_gpu_nbor==1) {
mn = _max_nbors;
const numtyp i_cell_size=static_cast<numtyp>(1.0/_cell_size);
const int neigh_block=_block_cell_id;
const int GX=(int)ceil((float)nall/neigh_block);

View File

@ -15,7 +15,7 @@ from .pylammps import *
# convert module string version to numeric version
def get_version_number():
from datetime import datetime
import time
from sys import version_info
vstring = None
if version_info.major == 3 and version_info.minor >= 8:
@ -32,7 +32,7 @@ def get_version_number():
if not vstring:
return 0
d = datetime.strptime(vstring, "%d%b%Y")
return d.year*10000 + d.month*100 + d.day
t = time.strptime(vstring, "%d%b%Y")
return t.tm_year*10000 + t.tm_mon*100 + t.tm_mday
__version__ = get_version_number()

View File

@ -729,12 +729,11 @@ class lammps(object):
def extract_global(self, name, dtype=LAMMPS_AUTODETECT):
"""Query LAMMPS about global settings of different types.
This is a wrapper around the :cpp:func:`lammps_extract_global`
function of the C-library interface. Unlike the C function
this method returns the value and not a pointer and thus can
only return the first value for keywords representing a list
of values. The :cpp:func:`lammps_extract_global` documentation
includes a list of the supported keywords and their data types.
This is a wrapper around the :cpp:func:`lammps_extract_global` function
of the C-library interface. Since there are no pointers in Python, this
method will - unlike the C function - return the value or a list of
values. The :cpp:func:`lammps_extract_global` documentation includes a
list of the supported keywords and their data types.
Since Python needs to know the data type to be able to interpret
the result, by default, this function will try to auto-detect the data type
by asking the library. You can also force a specific data type. For that
@ -746,12 +745,23 @@ class lammps(object):
:type name: string
:param dtype: data type of the returned data (see :ref:`py_datatype_constants`)
:type dtype: int, optional
:return: value of the property or None
:rtype: int, float, or NoneType
:return: value of the property or list of values or None
:rtype: int, float, list, or NoneType
"""
if dtype == LAMMPS_AUTODETECT:
dtype = self.extract_global_datatype(name)
# set length of vector for items that are not a scalar
vec_dict = { 'boxlo':3, 'boxhi':3, 'sublo':3, 'subhi':3,
'sublo_lambda':3, 'subhi_lambda':3, 'periodicity':3 }
if name in vec_dict:
veclen = vec_dict[name]
elif name == 'respa_dt':
veclen = self.extract_global('respa_levels',LAMMPS_INT)
else:
veclen = 1
if name: name = name.encode()
else: return None
@ -766,14 +776,19 @@ class lammps(object):
target_type = float
elif dtype == LAMMPS_STRING:
self.lib.lammps_extract_global.restype = c_char_p
target_type = lambda x: str(x, 'ascii')
ptr = self.lib.lammps_extract_global(self.lmp, name)
if ptr:
return target_type(ptr[0])
if dtype == LAMMPS_STRING:
return ptr.decode('utf-8')
if veclen > 1:
result = []
for i in range(0,veclen):
result.append(target_type(ptr[i]))
return result
else: return target_type(ptr[0])
return None
# -------------------------------------------------------------------------
# extract per-atom info datatype

View File

@ -12,11 +12,11 @@
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing authors: Axel Kohlmeyer (Temple U),
Ryan S. Elliott (UMN)
Ellad B. Tadmor (UMN)
Ronald Miller (Carleton U)
Yaser Afshar (UMN)
Contributing authors: Axel Kohlmeyer (Temple U),
Ryan S. Elliott (UMN),
Ellad B. Tadmor (UMN),
Ronald Miller (Carleton U),
Yaser Afshar (UMN)
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
@ -65,6 +65,7 @@
#include "error.h"
#include "fix_store_kim.h"
#include "input.h"
#include "variable.h"
#include "modify.h"
#include "update.h"
@ -129,6 +130,7 @@ void KimInteractions::do_setup(int narg, char **arg)
"=======\n");
if (simulatorModel) {
auto first_visit = input->variable->find("kim_update");
if (!fixed_types) {
std::string atom_type_sym_list =
fmt::format("{}", fmt::join(arg, arg + narg, " "));
@ -198,15 +200,17 @@ void KimInteractions::do_setup(int narg, char **arg)
const std::string sim_field_str(sim_field);
if (sim_field_str == "model-defn") {
if (first_visit < 0) input->one("variable kim_update equal 0");
else input->one("variable kim_update equal 1");
if (domain->periodicity[0] &&
domain->periodicity[1] &&
domain->periodicity[2])
input->one("variable kim_periodic equal 1");
else if (domain->periodicity[0] &&
domain->periodicity[1] &&
else if (!domain->periodicity[0] &&
!domain->periodicity[1] &&
!domain->periodicity[2])
input->one("variable kim_periodic equal 2");
else input->one("variable kim_periodic equal 0");
input->one("variable kim_periodic equal 0");
else input->one("variable kim_periodic equal 2");
// KIM Simulator Model has a Model definition
no_model_definition = false;
@ -241,6 +245,7 @@ void KimInteractions::do_setup(int narg, char **arg)
} else {
// not a simulator model. issue pair_style and pair_coeff commands.
if (fixed_types)

View File

@ -55,6 +55,16 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp)
nfunc = 0;
pfuncs = nullptr;
// check for PYTHONUNBUFFERED environment variable
const char * PYTHONUNBUFFERED = getenv("PYTHONUNBUFFERED");
if (PYTHONUNBUFFERED != nullptr && strcmp(PYTHONUNBUFFERED, "1") == 0) {
// Python Global configuration variable
// Force the stdout and stderr streams to be unbuffered.
Py_UnbufferedStdioFlag = 1;
}
// one-time initialization of Python interpreter
// pyMain stores pointer to main module
external_interpreter = Py_IsInitialized();
@ -496,6 +506,7 @@ int PythonImpl::create_entry(char *name)
"cannot be used unless output is a string");
pfuncs[ifunc].length_longstr = length_longstr;
pfuncs[ifunc].longstr = new char[length_longstr+1];
pfuncs[ifunc].longstr[length_longstr] = '\0';
}
if (strstr(ostr,"v_") != ostr) error->all(FLERR,"Invalid python command");

View File

@ -551,7 +551,7 @@ void PairOxdna2Coaxstk::coeff(int narg, char **arg)
{
int count;
if (narg != 21) error->all(FLERR,"Incorrect args for pair coefficients in oxdna/coaxstk");
if (narg != 21) error->all(FLERR,"Incorrect args for pair coefficients in oxdna2/coaxstk");
if (!allocated) allocate();
int ilo,ihi,jlo,jhi;
@ -673,7 +673,7 @@ void PairOxdna2Coaxstk::coeff(int narg, char **arg)
}
}
if (count == 0) error->all(FLERR,"Incorrect args for pair coefficients in oxdna/coaxstk");
if (count == 0) error->all(FLERR,"Incorrect args for pair coefficients in oxdna2/coaxstk");
}

View File

@ -273,7 +273,7 @@ void PairOxdna2Dh::coeff(int narg, char **arg)
{
int count;
if (narg != 5) error->all(FLERR,"Incorrect args for pair coefficients in oxdna/dh");
if (narg != 5) error->all(FLERR,"Incorrect args for pair coefficients in oxdna2/dh");
if (!allocated) allocate();
int ilo,ihi,jlo,jhi;
@ -356,7 +356,7 @@ void PairOxdna2Dh::coeff(int narg, char **arg)
}
}
if (count == 0) error->all(FLERR,"Incorrect args for pair coefficients in oxdna/dh");
if (count == 0) error->all(FLERR,"Incorrect args for pair coefficients in oxdna2/dh");
}
/* ----------------------------------------------------------------------

View File

@ -1022,7 +1022,7 @@ void PairOxrna2Stk::coeff(int narg, char **arg)
}
}
if (count == 0) error->all(FLERR,"Incorrect args for pair coefficients in oxdna/stk");
if (count == 0) error->all(FLERR,"Incorrect args for pair coefficients in oxrna2/stk");
}

View File

@ -576,7 +576,7 @@ void PairOxrna2Xstk::coeff(int narg, char **arg)
{
int count;
if (narg != 22) error->all(FLERR,"Incorrect args for pair coefficients in oxdna/xstk");
if (narg != 22) error->all(FLERR,"Incorrect args for pair coefficients in oxrna2/xstk");
if (!allocated) allocate();
int ilo,ihi,jlo,jhi;
@ -707,7 +707,7 @@ void PairOxrna2Xstk::coeff(int narg, char **arg)
}
}
if (count == 0) error->all(FLERR,"Incorrect args for pair coefficients in oxdna/xstk");
if (count == 0) error->all(FLERR,"Incorrect args for pair coefficients in oxrna2/xstk");
}

View File

@ -31,12 +31,14 @@
#include "group.h"
#include "info.h"
#include "input.h"
#include "integrate.h"
#include "memory.h"
#include "modify.h"
#include "molecule.h"
#include "neigh_list.h"
#include "neighbor.h"
#include "region.h"
#include "respa.h"
#include "output.h"
#include "thermo.h"
#include "timer.h"
@ -984,12 +986,14 @@ to then decide how to cast the (void*) pointer and access the data.
* \return integer constant encoding the data type of the property
* or -1 if not found. */
int lammps_extract_global_datatype(void *handle, const char *name)
int lammps_extract_global_datatype(void * /*handle*/, const char *name)
{
if (strcmp(name,"dt") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"ntimestep") == 0) return LAMMPS_BIGINT;
if (strcmp(name,"atime") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"atimestep") == 0) return LAMMPS_BIGINT;
if (strcmp(name,"respa_levels") == 0) return LAMMPS_INT;
if (strcmp(name,"respa_dt") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"boxlo") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"boxhi") == 0) return LAMMPS_DOUBLE;
@ -1116,6 +1120,14 @@ report the "native" data type. The following tables are provided:
- bigint
- 1
- the number of the timestep when "atime" was last updated.
* - respa_levels
- int
- 1
- number of r-RESPA levels. See :doc:`run_style`.
* - respa_dt
- double
- number of r-RESPA levels
- length of the time steps with r-RESPA. See :doc:`run_style`.
.. _extract_box_settings:
@ -1366,12 +1378,22 @@ void *lammps_extract_global(void *handle, const char *name)
if (strcmp(name,"atime") == 0) return (void *) &lmp->update->atime;
if (strcmp(name,"atimestep") == 0) return (void *) &lmp->update->atimestep;
if (utils::strmatch(lmp->update->integrate_style,"^respa")) {
Respa *respa = (Respa *)lmp->update->integrate;
if (strcmp(name,"respa_levels") == 0) return (void *) &respa->nlevels;
if (strcmp(name,"respa_dt") == 0) return (void *) respa->step;
}
if (strcmp(name,"boxlo") == 0) return (void *) lmp->domain->boxlo;
if (strcmp(name,"boxhi") == 0) return (void *) lmp->domain->boxhi;
if (strcmp(name,"sublo") == 0) return (void *) lmp->domain->sublo;
if (strcmp(name,"subhi") == 0) return (void *) lmp->domain->subhi;
if (strcmp(name,"sublo_lambda") == 0) return (void *) lmp->domain->sublo_lamda;
if (strcmp(name,"subhi_lambda") == 0) return (void *) lmp->domain->subhi_lamda;
// these are only valid for a triclinic cell
if (lmp->domain->triclinic) {
if (strcmp(name,"sublo_lambda") == 0)
return (void *) lmp->domain->sublo_lamda;
if (strcmp(name,"subhi_lambda") == 0)
return (void *) lmp->domain->subhi_lamda;
}
if (strcmp(name,"boxxlo") == 0) return (void *) &lmp->domain->boxlo[0];
if (strcmp(name,"boxxhi") == 0) return (void *) &lmp->domain->boxhi[0];
if (strcmp(name,"boxylo") == 0) return (void *) &lmp->domain->boxlo[1];

View File

@ -851,6 +851,14 @@ std::vector<std::string> utils::split_words(const std::string &text)
return list;
}
/* ----------------------------------------------------------------------
Convert multi-line string into lines
------------------------------------------------------------------------- */
std::vector<std::string> utils::split_lines(const std::string &text)
{
return Tokenizer(text, "\n").as_vector();
}
/* ----------------------------------------------------------------------
Return whether string is a valid integer number
------------------------------------------------------------------------- */

View File

@ -321,6 +321,12 @@ namespace LAMMPS_NS {
std::vector<std::string> split_words(const std::string &text);
/** Take multi-line text and split into lines
*
* \param text string that should be split
* \return STL vector with the lines */
std::vector<std::string> split_lines(const std::string &text);
/** Check if string can be converted to valid integer
*
* \param str string that should be checked

View File

@ -124,7 +124,7 @@ file(GLOB FIX_TIMESTEP_TESTS LIST_DIRECTORIES false ${TEST_INPUT_FOLDER}/fix-tim
foreach(TEST ${FIX_TIMESTEP_TESTS})
string(REGEX REPLACE "^.*fix-timestep-(.*)\.yaml" "FixTimestep:\\1" TNAME ${TEST})
add_test(NAME ${TNAME} COMMAND test_fix_timestep ${TEST} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set_tests_properties(${TNAME} PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR};PYTHONPATH=${TEST_INPUT_FOLDER}:$ENV{PYTHONPATH}")
set_tests_properties(${TNAME} PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR};PYTHONPATH=${TEST_INPUT_FOLDER}:${LAMMPS_PYTHON_DIR}:$ENV{PYTHONPATH}")
endforeach()
# dihedral style tester

View File

@ -0,0 +1,73 @@
---
lammps_version: 10 Mar 2021
date_generated: Wed Mar 24 18:57:26 202
epsilon: 9e-12
prerequisites: ! |
atom full
fix python/move
pre_commands: ! ""
post_commands: ! |
fix test all python/move py_nve.NVE
input_file: in.fourmol
natoms: 29
run_pos: ! |2
1 -2.7045559775384037e-01 2.4912159905679729e+00 -1.6695851791541885e-01
2 3.1004029573899528e-01 2.9612354631094391e+00 -8.5466363037021464e-01
3 -7.0398551400789477e-01 1.2305509955830618e+00 -6.2777526944456274e-01
4 -1.5818159336499285e+00 1.4837407818929933e+00 -1.2538710836062004e+00
5 -9.0719763672789266e-01 9.2652103885675297e-01 3.9954210488374786e-01
6 2.4831720524855985e-01 2.8313021497871271e-01 -1.2314233331711453e+00
7 3.4143527641386412e-01 -2.2646551041391422e-02 -2.5292291414903052e+00
8 1.1743552229100009e+00 -4.8863228565853950e-01 -6.3783432910825522e-01
9 1.3800524229500313e+00 -2.5274721030406683e-01 2.8353985887095157e-01
10 2.0510765220543883e+00 -1.4604063740302866e+00 -9.8323745081712954e-01
11 1.7878031944442556e+00 -1.9921863272948861e+00 -1.8890602447625777e+00
12 3.0063007039340053e+00 -4.9013350496963298e-01 -1.6231898107386229e+00
13 4.0515402959192999e+00 -8.9202011606653986e-01 -1.6400005529924957e+00
14 2.6066963345543819e+00 -4.1789253965514150e-01 -2.6634003608794394e+00
15 2.9695287185712913e+00 5.5422613165234036e-01 -1.2342022021790127e+00
16 2.6747029695228521e+00 -2.4124119054564295e+00 -2.3435746150616148e-02
17 2.2153577785283796e+00 -2.0897985186907717e+00 1.1963150794479436e+00
18 2.1369701704115704e+00 3.0158507413630606e+00 -3.5179348337215015e+00
19 1.5355837136087378e+00 2.6255292355375675e+00 -4.2353987779879052e+00
20 2.7727573005678776e+00 3.6923910449610169e+00 -3.9330842459133493e+00
21 4.9040128073204299e+00 -4.0752348172957946e+00 -3.6210314709891711e+00
22 4.3582355554440841e+00 -4.2126119427287048e+00 -4.4612844196314052e+00
23 5.7439382849307599e+00 -3.5821957939275029e+00 -3.8766361295935821e+00
24 2.0689243582422630e+00 3.1513346907271012e+00 3.1550389754828800e+00
25 1.3045351331492134e+00 3.2665125705842848e+00 2.5111855257433504e+00
26 2.5809237402711274e+00 4.0117602605482832e+00 3.2212060529089896e+00
27 -1.9611343130357228e+00 -4.3563411931359752e+00 2.1098293115523705e+00
28 -2.7473562684513411e+00 -4.0200819932379330e+00 1.5830052163433954e+00
29 -1.3126000191359855e+00 -3.5962518039482929e+00 2.2746342468737835e+00
run_vel: ! |2
1 8.1705744183262364e-03 1.6516406176274288e-02 4.7902264318912978e-03
2 5.4501493445687759e-03 5.1791699408496334e-03 -1.4372931530376651e-03
3 -8.2298292722385643e-03 -1.2926551614621381e-02 -4.0984181178163881e-03
4 -3.7699042590093588e-03 -6.5722892098813799e-03 -1.1184640360133230e-03
5 -1.1021961004346586e-02 -9.8906780939335987e-03 -2.8410737829284395e-03
6 -3.9676663166400034e-02 4.6817061464710256e-02 3.7148491979476124e-02
7 9.1033953013898580e-04 -1.0128524411938794e-02 -5.1568251805019748e-02
8 7.9064712058855707e-03 -3.3507254552631767e-03 3.4557098492564629e-02
9 1.5644176117320923e-03 3.7365546102722164e-03 1.5047408822037646e-02
10 2.9201446820573174e-02 -2.9249578745486147e-02 -1.5018077424322538e-02
11 -4.7835961513517560e-03 -3.7481385134185206e-03 -2.3464104142290089e-03
12 2.2696451841920521e-03 -3.4774154398129479e-04 -3.0640770327796806e-03
13 2.7531740451953168e-03 5.8171061612840667e-03 -7.9467454022160518e-04
14 3.5246182371994252e-03 -5.7939995585585468e-03 -3.9478431172751344e-03
15 -1.8547943640122894e-03 -5.8554729942777743e-03 6.2938485140538649e-03
16 1.8681499973445245e-02 -1.3262466204585335e-02 -4.5638651457003243e-02
17 -1.2896269981100382e-02 9.7527665265956451e-03 3.7296535360836762e-02
18 -8.0065794848261610e-04 -8.6270473212554395e-04 -1.4483040697508738e-03
19 1.2452390836182623e-03 -2.5061097118772701e-03 7.2998631009712975e-03
20 3.5930060229597042e-03 3.6938860309252966e-03 3.2322732687893028e-03
21 -1.4689220370766550e-03 -2.7352129761527741e-04 7.0581624215243391e-04
22 -7.0694199254630339e-03 -4.2577148924878554e-03 2.8079117614251796e-04
23 6.0446963117374913e-03 -1.4000131614795382e-03 2.5819754847014255e-03
24 3.1926367902287940e-04 -9.9445664749276438e-04 1.4999996959365452e-04
25 1.3789754514814662e-04 -4.4335894884532569e-03 -8.1808136725080281e-04
26 2.0485904035217549e-03 2.7813358633835984e-03 4.3245727149206692e-03
27 4.5604120293369857e-04 -1.0305523026921137e-03 2.1188058381358511e-04
28 -6.2544520861855116e-03 1.4127711176146942e-03 -1.8429821884794269e-03
29 6.4110631534401762e-04 3.1273432719593867e-03 3.7253671105656715e-03
...

View File

@ -0,0 +1,104 @@
from __future__ import print_function
from lammps import lammps
class LAMMPSFix(object):
def __init__(self, ptr, group_name="all"):
self.lmp = lammps(ptr=ptr)
self.group_name = group_name
class LAMMPSFixMove(LAMMPSFix):
def __init__(self, ptr, group_name="all"):
super(LAMMPSFixMove, self).__init__(ptr, group_name)
def init(self):
pass
def initial_integrate(self, vflag):
pass
def final_integrate(self):
pass
def initial_integrate_respa(self, vflag, ilevel, iloop):
pass
def final_integrate_respa(self, ilevel, iloop):
pass
def reset_dt(self):
pass
class NVE(LAMMPSFixMove):
""" Python implementation of fix/nve """
def __init__(self, ptr, group_name="all"):
super(NVE, self).__init__(ptr)
assert(self.group_name == "all")
self._step_respa = None
def init(self):
dt = self.lmp.extract_global("dt")
ftm2v = self.lmp.extract_global("ftm2v")
self.ntypes = self.lmp.extract_global("ntypes")
self.dtv = dt
self.dtf = 0.5 * dt * ftm2v
@property
def step_respa(self):
if not self._step_respa:
self._step_respa = self.lmp.extract_global("respa_dt")
return self._step_respa
def initial_integrate(self, vflag):
nlocal = self.lmp.extract_global("nlocal")
mass = self.lmp.extract_atom("mass")
atype = self.lmp.extract_atom("type")
x = self.lmp.extract_atom("x")
v = self.lmp.extract_atom("v")
f = self.lmp.extract_atom("f")
for i in range(nlocal):
dtfm = self.dtf / mass[int(atype[i])]
v[i][0] += dtfm * f[i][0]
v[i][1] += dtfm * f[i][1]
v[i][2] += dtfm * f[i][2]
x[i][0] += self.dtv * v[i][0]
x[i][1] += self.dtv * v[i][1]
x[i][2] += self.dtv * v[i][2]
def final_integrate(self):
nlocal = self.lmp.extract_global("nlocal")
mass = self.lmp.extract_atom("mass")
atype = self.lmp.extract_atom("type")
v = self.lmp.extract_atom("v")
f = self.lmp.extract_atom("f")
for i in range(nlocal):
dtfm = self.dtf / mass[int(atype[i])]
v[i][0] += dtfm * f[i][0]
v[i][1] += dtfm * f[i][1]
v[i][2] += dtfm * f[i][2]
def initial_integrate_respa(self, vflag, ilevel, iloop):
ftm2v = self.lmp.extract_global("ftm2v")
self.dtv = self.step_respa[ilevel]
self.dtf = 0.5 * self.step_respa[ilevel] * ftm2v
# innermost level - NVE update of v and x
# all other levels - NVE update of v
if ilevel == 0:
self.initial_integrate(vflag)
else:
self.final_integrate()
def final_integrate_respa(self, ilevel, iloop):
ftm2v = self.lmp.extract_global("ftm2v")
self.dtf = 0.5 * self.step_respa[ilevel] * ftm2v
self.final_integrate()
def reset_dt(self):
dt = self.lmp.extract_global("dt")
ftm2v = self.lmp.extract_global("ftm2v")
self.dtv = dt;
self.dtf = 0.5 * dt * ftm2v;

View File

@ -8,7 +8,7 @@ add_executable(test_python_package test_python_package.cpp)
target_link_libraries(test_python_package PRIVATE lammps GTest::GMock GTest::GTest)
target_compile_definitions(test_python_package PRIVATE -DTEST_INPUT_FOLDER=${TEST_INPUT_FOLDER})
add_test(NAME PythonPackage COMMAND test_python_package WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set_tests_properties(PythonPackage PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR};PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}:${LAMMPS_PYTHON_DIR}:$ENV{PYTHONPATH}")
set_tests_properties(PythonPackage PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR};PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}:${LAMMPS_PYTHON_DIR}:$ENV{PYTHONPATH};PYTHONUNBUFFERED=1")
# we must have shared libraries enabled for testing the python module
if(NOT BUILD_SHARED_LIBS)

View File

@ -4,6 +4,16 @@ from __future__ import print_function
def square(val):
return val*val
def bool_to_val(txt):
if txt.upper() in ["TRUE", "YES"]:
return 1.0
return 0.0
def val_to_bool(val):
if val != 0:
return "True"
return "False"
def printnum():
print("2.25")
@ -11,8 +21,10 @@ def printtxt():
print("sometext")
def getidxvar(lmpptr):
from lammps import lammps, LMP_VAR_EQUAL
from lammps import lammps
lmp = lammps(ptr=lmpptr)
val = lmp.extract_variable("idx",None,LMP_VAR_EQUAL)
val = lmp.extract_variable("idx")
print(val)
def longstr():
return "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent metus."

View File

@ -259,15 +259,43 @@ create_atoms 1 single &
result = self.lmp.get_thermo(key)
self.assertEqual(value, result, key)
def test_extract_global_double(self):
def test_extract_global(self):
self.lmp.command("region box block -1 1 -2 2 -3 3")
self.lmp.command("create_box 1 box")
self.assertEqual(self.lmp.extract_global("units"), "lj")
self.assertEqual(self.lmp.extract_global("ntimestep"), 0)
self.assertEqual(self.lmp.extract_global("dt"), 0.005)
self.assertEqual(self.lmp.extract_global("boxxlo"), -1.0)
self.assertEqual(self.lmp.extract_global("boxxhi"), 1.0)
self.assertEqual(self.lmp.extract_global("boxylo"), -2.0)
self.assertEqual(self.lmp.extract_global("boxyhi"), 2.0)
self.assertEqual(self.lmp.extract_global("boxzlo"), -3.0)
self.assertEqual(self.lmp.extract_global("boxzhi"), 3.0)
self.assertEqual(self.lmp.extract_global("boxlo"), [-1.0, -2.0, -3.0])
self.assertEqual(self.lmp.extract_global("boxhi"), [1.0, 2.0, 3.0])
self.assertEqual(self.lmp.extract_global("sublo"), [-1.0, -2.0, -3.0])
self.assertEqual(self.lmp.extract_global("subhi"), [1.0, 2.0, 3.0])
self.assertEqual(self.lmp.extract_global("periodicity"), [1,1,1])
self.assertEqual(self.lmp.extract_global("triclinic"), 0)
self.assertEqual(self.lmp.extract_global("sublo_lambda"), None)
self.assertEqual(self.lmp.extract_global("subhi_lambda"), None)
self.assertEqual(self.lmp.extract_global("respa_levels"), None)
self.assertEqual(self.lmp.extract_global("respa_dt"), None)
# set and initialize r-RESPA
self.lmp.command("run_style respa 3 5 2 pair 2 kspace 3")
self.lmp.command("mass * 1.0")
self.lmp.command("run 1 post no")
self.assertEqual(self.lmp.extract_global("ntimestep"), 1)
self.assertEqual(self.lmp.extract_global("respa_levels"), 3)
self.assertEqual(self.lmp.extract_global("respa_dt"), [0.0005, 0.0025, 0.005])
# checks only for triclinic boxes
self.lmp.command("change_box all triclinic")
self.assertEqual(self.lmp.extract_global("triclinic"), 1)
self.assertEqual(self.lmp.extract_global("sublo_lambda"), [0.0, 0.0, 0.0])
self.assertEqual(self.lmp.extract_global("subhi_lambda"), [1.0, 1.0, 1.0])
##############################
if __name__ == "__main__":

2
unittest/python/run.py Normal file
View File

@ -0,0 +1,2 @@
from __future__ import print_function
print("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent metus.")

View File

@ -14,19 +14,25 @@
#include "atom.h"
#include "info.h"
#include "input.h"
#include "variable.h"
#include "library.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <cmath>
#include <cstring>
#include <vector>
#include <functional>
#include "../testing/core.h"
#include "../testing/systems/melt.h"
// location of '*.py' files required by tests
#define STRINGIFY(val) XSTR(val)
#define XSTR(val) #val
std::string INPUT_FOLDER = STRINGIFY(TEST_INPUT_FOLDER);
// whether to print verbose output (i.e. not capturing LAMMPS screen output).
const char * LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent metus.";
bool verbose = false;
using LAMMPS_NS::utils::split_words;
@ -34,95 +40,287 @@ using LAMMPS_NS::utils::split_words;
namespace LAMMPS_NS {
using ::testing::MatchesRegex;
using ::testing::StrEq;
using ::testing::Eq;
using ::testing::HasSubstr;
class PythonPackageTest : public ::testing::Test {
class PythonPackageTest : public LAMMPSTest {
protected:
LAMMPS *lmp;
Info *info;
void SetUp() override
void InitSystem() override
{
const char *args[] = {"PythonPackageTest", "-log", "none", "-echo", "screen", "-nocite"};
char **argv = (char **)args;
int argc = sizeof(args) / sizeof(char *);
if (!verbose) ::testing::internal::CaptureStdout();
lmp = new LAMMPS(argc, argv, MPI_COMM_WORLD);
if (!verbose) ::testing::internal::GetCapturedStdout();
ASSERT_NE(lmp, nullptr);
info = new Info(lmp);
if (!verbose) ::testing::internal::CaptureStdout();
lmp->input->one("units real");
lmp->input->one("dimension 3");
lmp->input->one("region box block -4 4 -4 4 -4 4");
lmp->input->one("create_box 1 box");
lmp->input->one("create_atoms 1 single 0.0 0.0 0.0 units box");
lmp->input->one("create_atoms 1 single 1.9 -1.9 1.9999 units box");
lmp->input->one("pair_style zero 2.0");
lmp->input->one("pair_coeff * *");
lmp->input->one("mass * 1.0");
lmp->input->one("variable input_dir index " + INPUT_FOLDER);
if (!verbose) ::testing::internal::GetCapturedStdout();
}
if (!info->has_package("PYTHON")) GTEST_SKIP();
void TearDown() override
{
if (!verbose) ::testing::internal::CaptureStdout();
delete info;
delete lmp;
if (!verbose) ::testing::internal::GetCapturedStdout();
HIDE_OUTPUT([&] {
command("units real");
command("dimension 3");
command("region box block -4 4 -4 4 -4 4");
command("create_box 1 box");
command("create_atoms 1 single 0.0 0.0 0.0 units box");
command("create_atoms 1 single 1.9 -1.9 1.9999 units box");
command("pair_style zero 2.0");
command("pair_coeff * *");
command("mass * 1.0");
command("variable input_dir index " + INPUT_FOLDER);
});
}
};
TEST_F(PythonPackageTest, python_invoke)
class FixPythonInvokeTest : public MeltTest {
protected:
void InitSystem() override
{
if (!info->has_package("PYTHON")) GTEST_SKIP();
MeltTest::InitSystem();
}
};
TEST_F(PythonPackageTest, InvokeFunctionFromFile)
{
if (!info->has_style("command", "python")) GTEST_SKIP();
// execute python function from file
if (!verbose) ::testing::internal::CaptureStdout();
lmp->input->one("python printnum file ${input_dir}/func.py");
if (!verbose) ::testing::internal::GetCapturedStdout();
::testing::internal::CaptureStdout();
lmp->input->one("python printnum invoke");
std::string output = ::testing::internal::GetCapturedStdout();
if (verbose) std::cout << output;
ASSERT_THAT(output, MatchesRegex("python.*2.25.*"));
HIDE_OUTPUT([&] {
command("python printnum file ${input_dir}/func.py");
});
auto output = CAPTURE_OUTPUT([&]() {
command("python printnum invoke");
});
ASSERT_THAT(output, HasSubstr("2.25\n"));
}
TEST_F(PythonPackageTest, InvokeFunctionPassInt)
{
// execute python function, passing integer as argument
HIDE_OUTPUT([&] {
command("variable sq python square");
command("python square input 1 2 format ii return v_sq file ${input_dir}/func.py");
command("python square invoke");
});
ASSERT_EQ(get_variable_value("sq"), 4.0);
}
TEST_F(PythonPackageTest, InvokeFunctionPassFloat)
{
// execute python function, passing float as argument
HIDE_OUTPUT([&] {
command("variable sq python square");
command("python square input 1 2.5 format ff return v_sq file ${input_dir}/func.py");
});
ASSERT_EQ(get_variable_value("sq"), 6.25);
}
TEST_F(PythonPackageTest, InvokeFunctionPassString)
{
// execute python function, passing string as argument
HIDE_OUTPUT([&] {
command("variable val python bool_to_val");
command("python bool_to_val input 1 \"true\" format sf return v_val file ${input_dir}/func.py");
});
ASSERT_EQ(get_variable_value("val"), 1.0);
}
TEST_F(PythonPackageTest, InvokeFunctionPassStringVariable)
{
// execute python function, passing string variable as argument
HIDE_OUTPUT([&] {
command("variable val python bool_to_val");
command("python bool_to_val input 1 v_str format sf return v_val file ${input_dir}/func.py");
});
HIDE_OUTPUT([&] {
command("variable str string \"true\"");
});
ASSERT_EQ(get_variable_value("val"), 1.0);
HIDE_OUTPUT([&] {
command("variable str string \"false\"");
});
ASSERT_EQ(get_variable_value("val"), 0.0);
}
TEST_F(PythonPackageTest, InvokeStringFunction)
{
// execute python function, passing string variable as argument
HIDE_OUTPUT([&] {
command("variable str python val_to_bool");
command("python val_to_bool input 1 v_val format is return v_str file ${input_dir}/func.py");
});
HIDE_OUTPUT([&] {
command("variable val equal 0");
});
ASSERT_THAT(get_variable_string("str"), StrEq("False"));
HIDE_OUTPUT([&] {
command("variable val equal 1");
});
ASSERT_THAT(get_variable_string("str"), StrEq("True"));
}
TEST_F(PythonPackageTest, InvokeLongStringFunction)
{
// execute python function, passing string variable as argument
HIDE_OUTPUT([&] {
command("variable str python longstr");
command("python longstr format s length 72 return v_str file ${input_dir}/func.py");
});
ASSERT_THAT(get_variable_string("str"), StrEq(LOREM_IPSUM));
}
TEST_F(PythonPackageTest, InvokeOtherFunctionFromFile)
{
// execute another python function from same file
if (!verbose) ::testing::internal::CaptureStdout();
lmp->input->one("python printtxt exists");
if (!verbose) ::testing::internal::GetCapturedStdout();
::testing::internal::CaptureStdout();
lmp->input->one("python printtxt invoke");
output = ::testing::internal::GetCapturedStdout();
if (verbose) std::cout << output;
ASSERT_THAT(output, MatchesRegex("python.*sometext.*"));
HIDE_OUTPUT([&] {
command("python printnum file ${input_dir}/func.py");
command("python printtxt exists");
});
auto output = CAPTURE_OUTPUT([&] {
command("python printtxt invoke");
});
ASSERT_THAT(output, HasSubstr("sometext\n"));
}
TEST_F(PythonPackageTest, InvokeFunctionThatUsesLAMMPSModule)
{
// execute python function that uses the LAMMPS python module
if (!verbose) ::testing::internal::CaptureStdout();
lmp->input->one("variable idx equal 2.25");
lmp->input->one("python getidxvar input 1 SELF format p exists");
if (!verbose) ::testing::internal::GetCapturedStdout();
::testing::internal::CaptureStdout();
lmp->input->one("python getidxvar invoke");
output = ::testing::internal::GetCapturedStdout();
if (verbose) std::cout << output;
ASSERT_THAT(output, MatchesRegex("python.*2.25.*"));
HIDE_OUTPUT([&] {
command("python printnum file ${input_dir}/func.py");
command("variable idx equal 2.25");
command("python getidxvar input 1 SELF format p exists");
});
auto output = CAPTURE_OUTPUT([&] {
command("python getidxvar invoke");
});
ASSERT_THAT(output, HasSubstr("2.25\n"));
}
TEST_F(PythonPackageTest, python_variable)
{
if (!info->has_style("command", "python")) GTEST_SKIP();
if (!verbose) ::testing::internal::CaptureStdout();
lmp->input->one("variable sq python square");
lmp->input->one("variable val index 1.5");
lmp->input->one("python square input 1 v_val return v_sq format ff file ${input_dir}/func.py");
if (!verbose) ::testing::internal::GetCapturedStdout();
::testing::internal::CaptureStdout();
lmp->input->one("print \"${sq}\"");
std::string output = ::testing::internal::GetCapturedStdout();
if (verbose) std::cout << output;
// define variable that evaluates a python function
HIDE_OUTPUT([&] {
command("variable sq python square");
command("variable val index 1.5");
command("python square input 1 v_val return v_sq format ff file ${input_dir}/func.py");
});
std::string output = CAPTURE_OUTPUT([&] {
command("print \"${sq}\"");
});
ASSERT_THAT(output, MatchesRegex("print.*2.25.*"));
}
TEST_F(PythonPackageTest, InlineFunction)
{
// define variable that evaluates a python function
HIDE_OUTPUT([&] {
command("variable fact python factorial");
command("python factorial input 1 v_n return v_fact format ii here \"\"\"\n"
"def factorial(n):\n"
" if n == 0 or n == 1: return 1\n"
" return n*factorial(n-1)\n"
"\"\"\"");
});
HIDE_OUTPUT([&] {
command("variable n equal 1");
});
ASSERT_EQ(get_variable_value("fact"), 1.0);
HIDE_OUTPUT([&] {
command("variable n equal 2");
});
ASSERT_EQ(get_variable_value("fact"), 2.0);
HIDE_OUTPUT([&] {
command("variable n equal 3");
});
ASSERT_EQ(get_variable_value("fact"), 6.0);
}
TEST_F(PythonPackageTest, RunSource)
{
// execute python script from file
auto output = CAPTURE_OUTPUT([&] {
command("python xyz source ${input_dir}/run.py");
});
ASSERT_THAT(output, HasSubstr(LOREM_IPSUM));
}
TEST_F(PythonPackageTest, RunSourceInline)
{
// execute inline python script
auto output = CAPTURE_OUTPUT([&] {
command("python xyz source \"\"\"\n"
"from __future__ import print_function\n"
"print(2+2)\n"
"\"\"\""
);
});
ASSERT_THAT(output, HasSubstr("4"));
}
TEST_F(FixPythonInvokeTest, end_of_step)
{
HIDE_OUTPUT([&] {
command("python end_of_step_callback here \"\"\"\n"
"from __future__ import print_function\n"
"def end_of_step_callback(ptr):\n"
" print(\"PYTHON_END_OF_STEP\")\n"
"\"\"\"");
command("fix eos all python/invoke 10 end_of_step end_of_step_callback");
});
auto output = CAPTURE_OUTPUT([&] {
command("run 50");
});
auto lines = utils::split_lines(output);
int count = 0;
for(auto & line : lines) {
if (line == "PYTHON_END_OF_STEP") ++count;
}
ASSERT_EQ(count, 5);
}
TEST_F(FixPythonInvokeTest, post_force)
{
HIDE_OUTPUT([&] {
command("python post_force_callback here \"\"\"\n"
"from __future__ import print_function\n"
"def post_force_callback(ptr, vflag):\n"
" print(\"PYTHON_POST_FORCE\")\n"
"\"\"\"");
command("fix pf all python/invoke 10 post_force post_force_callback");
});
auto output = CAPTURE_OUTPUT([&] {
command("run 50");
});
auto lines = utils::split_lines(output);
int count = 0;
for(auto & line : lines) {
if (line == "PYTHON_POST_FORCE") ++count;
}
ASSERT_EQ(count, 5);
}
} // namespace LAMMPS_NS
int main(int argc, char **argv)
@ -132,7 +330,7 @@ int main(int argc, char **argv)
// handle arguments passed via environment variable
if (const char *var = getenv("TEST_ARGS")) {
std::vector<std::string> env = split_words(var);
auto env = split_words(var);
for (auto arg : env) {
if (arg == "-v") {
verbose = true;

View File

@ -16,9 +16,12 @@
#include "info.h"
#include "input.h"
#include "lammps.h"
#include "variable.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <functional>
using namespace LAMMPS_NS;
using ::testing::MatchesRegex;
@ -45,29 +48,58 @@ class LAMMPSTest : public ::testing::Test {
public:
void command(const std::string &line) { lmp->input->one(line.c_str()); }
void HIDE_OUTPUT(std::function<void()> f) {
if (!verbose) ::testing::internal::CaptureStdout();
f();
if (!verbose) ::testing::internal::GetCapturedStdout();
}
std::string CAPTURE_OUTPUT(std::function<void()> f) {
::testing::internal::CaptureStdout();
f();
auto output = ::testing::internal::GetCapturedStdout();
if (verbose) std::cout << output;
return output;
}
double get_variable_value(const std::string & name) {
char * str = utils::strdup(fmt::format("v_{}", name));
double value = lmp->input->variable->compute_equal(str);
delete [] str;
return value;
}
std::string get_variable_string(const std::string & name) {
return lmp->input->variable->retrieve(name.c_str());
}
protected:
const char *testbinary = "LAMMPSTest";
LAMMPS *lmp;
Info *info;
void SetUp() override
{
const char *args[] = {testbinary, "-log", "none", "-echo", "screen", "-nocite"};
char **argv = (char **)args;
int argc = sizeof(args) / sizeof(char *);
if (!verbose) ::testing::internal::CaptureStdout();
lmp = new LAMMPS(argc, argv, MPI_COMM_WORLD);
HIDE_OUTPUT([&] {
lmp = new LAMMPS(argc, argv, MPI_COMM_WORLD);
info = new Info(lmp);
});
InitSystem();
if (!verbose) ::testing::internal::GetCapturedStdout();
}
virtual void InitSystem() {}
void TearDown() override
{
if (!verbose) ::testing::internal::CaptureStdout();
delete lmp;
lmp = nullptr;
if (!verbose) ::testing::internal::GetCapturedStdout();
HIDE_OUTPUT([&] {
delete info;
delete lmp;
info = nullptr;
lmp = nullptr;
});
}
};

View File

@ -19,23 +19,25 @@ class MeltTest : public LAMMPSTest {
protected:
virtual void InitSystem() override
{
command("units lj");
command("atom_style atomic");
command("atom_modify map yes");
HIDE_OUTPUT([&] {
command("units lj");
command("atom_style atomic");
command("atom_modify map yes");
command("lattice fcc 0.8442");
command("region box block 0 2 0 2 0 2");
command("create_box 1 box");
command("create_atoms 1 box");
command("mass 1 1.0");
command("lattice fcc 0.8442");
command("region box block 0 2 0 2 0 2");
command("create_box 1 box");
command("create_atoms 1 box");
command("mass 1 1.0");
command("velocity all create 3.0 87287");
command("velocity all create 3.0 87287");
command("pair_style lj/cut 2.5");
command("pair_coeff 1 1 1.0 1.0 2.5");
command("pair_style lj/cut 2.5");
command("pair_coeff 1 1 1.0 1.0 2.5");
command("neighbor 0.3 bin");
command("neigh_modify every 20 delay 0 check no");
command("neighbor 0.3 bin");
command("neigh_modify every 20 delay 0 check no");
});
}
};

View File

@ -113,7 +113,7 @@ TEST(Utils, count_words_with_extra_spaces)
TEST(Utils, split_words_simple)
{
std::vector<std::string> list = utils::split_words("one two three");
auto list = utils::split_words("one two three");
ASSERT_EQ(list.size(), 3);
ASSERT_THAT(list[0], StrEq("one"));
ASSERT_THAT(list[1], StrEq("two"));
@ -122,7 +122,7 @@ TEST(Utils, split_words_simple)
TEST(Utils, split_words_quoted)
{
std::vector<std::string> list = utils::split_words("one 'two' \"three\"");
auto list = utils::split_words("one 'two' \"three\"");
ASSERT_EQ(list.size(), 3);
ASSERT_THAT(list[0], StrEq("one"));
ASSERT_THAT(list[1], StrEq("two"));
@ -131,7 +131,7 @@ TEST(Utils, split_words_quoted)
TEST(Utils, split_words_escaped)
{
std::vector<std::string> list = utils::split_words("1\\' '\"two\"' 3\\\"");
auto list = utils::split_words("1\\' '\"two\"' 3\\\"");
ASSERT_EQ(list.size(), 3);
ASSERT_THAT(list[0], StrEq("1\\'"));
ASSERT_THAT(list[1], StrEq("\"two\""));
@ -140,13 +140,22 @@ TEST(Utils, split_words_escaped)
TEST(Utils, split_words_quote_in_quoted)
{
std::vector<std::string> list = utils::split_words("one 't\\'wo' \"th\\\"ree\"");
auto list = utils::split_words("one 't\\'wo' \"th\\\"ree\"");
ASSERT_EQ(list.size(), 3);
ASSERT_THAT(list[0], StrEq("one"));
ASSERT_THAT(list[1], StrEq("t\\'wo"));
ASSERT_THAT(list[2], StrEq("th\\\"ree"));
}
TEST(Utils, split_lines)
{
auto list = utils::split_lines(" line 1\nline 2 \n line 3 \n");
ASSERT_EQ(list.size(), 3);
ASSERT_THAT(list[0], StrEq(" line 1"));
ASSERT_THAT(list[1], StrEq("line 2 "));
ASSERT_THAT(list[2], StrEq(" line 3 "));
}
TEST(Utils, valid_integer1)
{
ASSERT_TRUE(utils::is_integer("10"));