Merge branch 'develop' into python-lint

This commit is contained in:
Axel Kohlmeyer
2024-09-05 04:03:06 -04:00
1597 changed files with 213908 additions and 60986 deletions

View File

@ -275,8 +275,14 @@ class lammps(object):
self.lib.lammps_extract_global.argtypes = [c_void_p, c_char_p]
self.lib.lammps_extract_global_datatype.argtypes = [c_void_p, c_char_p]
self.lib.lammps_extract_global_datatype.restype = c_int
self.lib.lammps_extract_pair.argtypes = [c_void_p, c_char_p]
self.lib.lammps_extract_pair_dimension.argtypes = [c_void_p, c_char_p]
self.lib.lammps_extract_pair_dimension.restype = c_int
self.lib.lammps_extract_compute.argtypes = [c_void_p, c_char_p, c_int, c_int]
self.lib.lammps_map_atom.argtypes = [c_void_p, c_void_p]
self.lib.lammps_map_atom.restype = c_int
self.lib.lammps_get_thermo.argtypes = [c_void_p, c_char_p]
self.lib.lammps_get_thermo.restype = c_double
@ -320,6 +326,8 @@ class lammps(object):
self.lib.lammps_extract_atom.argtypes = [c_void_p, c_char_p]
self.lib.lammps_extract_atom_datatype.argtypes = [c_void_p, c_char_p]
self.lib.lammps_extract_atom_datatype.restype = c_int
self.lib.lammps_extract_atom_size.argtypes = [c_void_p, c_char_p, c_int]
self.lib.lammps_extract_atom_size.restype = c_int
self.lib.lammps_extract_fix.argtypes = [c_void_p, c_char_p, c_int, c_int, c_int, c_int]
@ -344,8 +352,8 @@ class lammps(object):
if self.has_mpi_support:
try:
from mpi4py import __version__ as mpi4py_version
# tested to work with mpi4py versions 2 and 3
self.has_mpi4py = mpi4py_version.split('.')[0] in ['2','3']
# tested to work with mpi4py versions 2, 3, and 4
self.has_mpi4py = mpi4py_version.split('.')[0] in ['2','3','4']
except ImportError:
# ignore failing import
pass
@ -371,7 +379,7 @@ class lammps(object):
if not self.has_mpi_support:
raise Exception('LAMMPS not compiled with real MPI library')
if not self.has_mpi4py:
raise Exception('Python mpi4py version is not 2 or 3')
raise Exception('Python mpi4py version is not 2, 3, or 4')
if self.MPI._sizeof(self.MPI.Comm) == sizeof(c_int):
MPI_Comm = c_int
else:
@ -384,12 +392,16 @@ class lammps(object):
narg = 0
cargs = None
if cmdargs is not None:
cmdargs.insert(0,"lammps")
narg = len(cmdargs)
for i in range(narg):
if type(cmdargs[i]) is str:
cmdargs[i] = cmdargs[i].encode()
cargs = (c_char_p*(narg+1))(*cmdargs)
myargs = ["lammps".encode()]
narg = len(cmdargs) + 1
for arg in cmdargs:
if type(arg) is str:
myargs.append(arg.encode())
elif type(arg) is bytes:
myargs.append(arg)
else:
raise TypeError('Unsupported cmdargs type ', type(arg))
cargs = (c_char_p*(narg+1))(*myargs)
cargs[narg] = None
self.lib.lammps_open.argtypes = [c_int, c_char_p*(narg+1), MPI_Comm, c_void_p]
else:
@ -405,12 +417,16 @@ class lammps(object):
self.comm = self.MPI.COMM_WORLD
self.opened = 1
if cmdargs is not None:
cmdargs.insert(0,"lammps")
narg = len(cmdargs)
for i in range(narg):
if type(cmdargs[i]) is str:
cmdargs[i] = cmdargs[i].encode()
cargs = (c_char_p*(narg+1))(*cmdargs)
myargs = ["lammps".encode()]
narg = len(cmdargs) + 1
for arg in cmdargs:
if type(arg) is str:
myargs.append(arg.encode())
elif type(arg) is bytes:
myargs.append(arg)
else:
raise TypeError('Unsupported cmdargs type ', type(arg))
cargs = (c_char_p*(narg+1))(*myargs)
cargs[narg] = None
self.lib.lammps_open_no_mpi.argtypes = [c_int, c_char_p*(narg+1), c_void_p]
self.lmp = c_void_p(self.lib.lammps_open_no_mpi(narg,cargs,None))
@ -527,11 +543,11 @@ class lammps(object):
:param error_text:
:type error_text: string
"""
if error_text: error_text = error_text.encode()
else: error_text = "(unknown error)".encode()
if error_text: new_error_text = error_text.encode()
else: new_error_text = "(unknown error)".encode()
with ExceptionCheck(self):
self.lib.lammps_error(self.lmp, error_type, error_text)
self.lib.lammps_error(self.lmp, error_type, new_error_text)
# -------------------------------------------------------------------------
@ -606,11 +622,11 @@ class lammps(object):
:param path: Name of the file/path with LAMMPS commands
:type path: string
"""
if path: path = path.encode()
if path: newpath = path.encode()
else: return
with ExceptionCheck(self):
self.lib.lammps_file(self.lmp, path)
self.lib.lammps_file(self.lmp, newpath)
# -------------------------------------------------------------------------
@ -623,11 +639,11 @@ class lammps(object):
:param cmd: a single lammps command
:type cmd: string
"""
if cmd: cmd = cmd.encode()
if cmd: newcmd = cmd.encode()
else: return
with ExceptionCheck(self):
self.lib.lammps_command(self.lmp,cmd)
self.lib.lammps_command(self.lmp, newcmd)
# -------------------------------------------------------------------------
@ -661,10 +677,11 @@ class lammps(object):
:param multicmd: text block of lammps commands
:type multicmd: string
"""
if type(multicmd) is str: multicmd = multicmd.encode()
if type(multicmd) is str: newmulticmd = multicmd.encode()
else: newmulticmd = multicmd
with ExceptionCheck(self):
self.lib.lammps_commands_string(self.lmp,c_char_p(multicmd))
self.lib.lammps_commands_string(self.lmp,c_char_p(newmulticmd))
# -------------------------------------------------------------------------
@ -751,11 +768,11 @@ class lammps(object):
:return: value of thermo keyword
:rtype: double or None
"""
if name: name = name.encode()
if name: newname = name.encode()
else: return None
with ExceptionCheck(self):
return self.lib.lammps_get_thermo(self.lmp,name)
return self.lib.lammps_get_thermo(self.lmp, newname)
# -------------------------------------------------------------------------
@property
@ -829,9 +846,9 @@ class lammps(object):
:return: value of the setting
:rtype: int
"""
if name: name = name.encode()
if name: newname = name.encode()
else: return None
return int(self.lib.lammps_extract_setting(self.lmp,name))
return int(self.lib.lammps_extract_setting(self.lmp, newname))
# -------------------------------------------------------------------------
# extract global info datatype
@ -845,16 +862,16 @@ class lammps(object):
This function returns ``None`` if the keyword is not
recognized. Otherwise it will return a positive integer value that
corresponds to one of the :ref:`data type <py_datatype_constants>`
constants define in the :py:mod:`lammps` module.
constants defined in the :py:mod:`lammps` module.
:param name: name of the property
:type name: string
:return: data type of global property, see :ref:`py_datatype_constants`
:rtype: int
"""
if name: name = name.encode()
if name: newname = name.encode()
else: return None
return self.lib.lammps_extract_global_datatype(self.lmp, name)
return self.lib.lammps_extract_global_datatype(self.lmp, newname)
# -------------------------------------------------------------------------
# extract global info
@ -888,15 +905,17 @@ class lammps(object):
# 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,
'special_lj':4, 'special_coul':4 }
'special_lj':4, 'special_coul':4, 'procgrid':3 }
if name in vec_dict:
veclen = vec_dict[name]
elif name == 'respa_dt':
veclen = self.extract_global('respa_levels',LAMMPS_INT)
elif name == 'sametag':
veclen = self.extract_setting('nall')
else:
veclen = 1
if name: name = name.encode()
if name: newname = name.encode()
else: return None
if dtype == LAMMPS_INT:
@ -914,7 +933,7 @@ class lammps(object):
else:
target_type = None
ptr = self.lib.lammps_extract_global(self.lmp, name)
ptr = self.lib.lammps_extract_global(self.lmp, newname)
if ptr:
if dtype == LAMMPS_STRING:
return ptr.decode('utf-8')
@ -926,6 +945,118 @@ class lammps(object):
else: return target_type(ptr[0])
return None
# -------------------------------------------------------------------------
# extract pair property dimensionality
def extract_pair_dimension(self, name):
"""Retrieve pair style property dimensionality from LAMMPS
.. versionadded:: 29Aug2024
This is a wrapper around the :cpp:func:`lammps_extract_pair_dimension`
function of the C-library interface. The list of supported keywords
depends on the pair style. This function returns ``None`` if the keyword
is not recognized.
:param name: name of the property
:type name: string
:return: dimensionality of the extractable data (typically 0, 1, or 2)
:rtype: int
"""
if name:
newname = name.encode()
else:
return None
dim = self.lib.lammps_extract_pair_dimension(self.lmp, newname)
if dim < 0:
return None
else:
return dim;
# -------------------------------------------------------------------------
# get access to pair style extractable data
def extract_pair(self, name):
"""Extract pair style data from LAMMPS.
.. versionadded:: 29Aug2024
This is a wrapper around the :cpp:func:`lammps_extract_pair` 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.
Since Python needs to know the dimensionality to be able to interpret
the result, this function will detect the dimensionality by asking the library.
This function returns ``None`` if the keyword is not recognized.
:param name: name of the property
:type name: string
:return: value of the property or list of values or None
:rtype: float, list of float, list of list of floats, or NoneType
"""
if name:
newname = name.encode()
else:
return None
dim = self.extract_pair_dimension(name)
if dim is None:
return None
elif dim == 0:
self.lib.lammps_extract_pair.restype = POINTER(c_double)
elif dim == 1:
self.lib.lammps_extract_pair.restype = POINTER(c_double)
elif dim == 2:
self.lib.lammps_extract_pair.restype = POINTER(POINTER(c_double))
else:
return None
ntypes = self.extract_setting('ntypes')
ptr = self.lib.lammps_extract_pair(self.lmp, newname)
if ptr:
if dim == 0:
return float(ptr[0])
elif dim == 1:
result = [0.0]
for i in range(1,ntypes+1):
result.append(float(ptr[i]))
return result
elif dim == 2:
result = []
inner = []
for i in range(0,ntypes+1):
inner.append(float(0.0))
result.append(inner)
for i in range(1,ntypes+1):
inner = [0.0]
for j in range(1,ntypes+1):
inner.append(float(ptr[i][j]))
result.append(inner)
return result
else:
return None
return None
# -------------------------------------------------------------------------
# map global atom ID to local atom index
def map_atom(self, id):
"""Map a global atom ID (aka tag) to the local atom index
This is a wrapper around the :cpp:func:`lammps_map_atom`
function of the C-library interface.
:param id: atom ID
:type id: int
:return: local index
:rtype: int
"""
tag = self.c_tagint(id)
return self.lib.lammps_map_atom(self.lmp, pointer(tag))
# -------------------------------------------------------------------------
# extract per-atom info datatype
@ -945,9 +1076,37 @@ class lammps(object):
:return: data type of per-atom property (see :ref:`py_datatype_constants`)
:rtype: int
"""
if name: name = name.encode()
if name: newname = name.encode()
else: return None
return self.lib.lammps_extract_atom_datatype(self.lmp, name)
return self.lib.lammps_extract_atom_datatype(self.lmp, newname)
# -------------------------------------------------------------------------
# extract per-atom info datatype
def extract_atom_size(self, name, dtype):
"""Retrieve per-atom property dimensions from LAMMPS
This is a wrapper around the :cpp:func:`lammps_extract_atom_size`
function of the C-library interface. Its documentation includes a
list of the supported keywords.
This function returns ``None`` if the keyword is not
recognized. Otherwise it will return an integer value with the size
of the per-atom vector or array. If *name* corresponds to a per-atom
array, the *dtype* keyword must be either LMP_SIZE_ROWS or LMP_SIZE_COLS
from the :ref:`type <py_type_constants>` constants defined in the
:py:mod:`lammps` module. The return value is the requested size.
If *name* corresponds to a per-atom vector the *dtype* keyword is ignored.
:param name: name of the property
:type name: string
:param type: either LMP_SIZE_ROWS or LMP_SIZE_COLS for arrays, otherwise ignored
:type type: int
:return: data type of per-atom property (see :ref:`py_datatype_constants`)
:rtype: int
"""
if name: newname = name.encode()
else: return None
return self.lib.lammps_extract_atom_size(self.lmp, newname, dtype)
# -------------------------------------------------------------------------
# extract per-atom info
@ -955,25 +1114,25 @@ class lammps(object):
def extract_atom(self, name, dtype=LAMMPS_AUTODETECT):
"""Retrieve per-atom properties from LAMMPS
This is a wrapper around the :cpp:func:`lammps_extract_atom`
function of the C-library interface. Its 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 by setting ``dtype``
to one of the :ref:`data type <py_datatype_constants>` constants defined in the
:py:mod:`lammps` module.
This function returns ``None`` if either the keyword is not
recognized, or an invalid data type constant is used.
This is a wrapper around the :cpp:func:`lammps_extract_atom` function of the
C-library interface. Its 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 by setting ``dtype`` to one of the :ref:`data type
<py_datatype_constants>` constants defined in the :py:mod:`lammps` module.
This function returns ``None`` if either the keyword is not recognized, or
an invalid data type constant is used.
.. note::
While the returned arrays of per-atom data are dimensioned
for the range [0:nmax] - as is the underlying storage -
the data is usually only valid for the range of [0:nlocal],
unless the property of interest is also updated for ghost
atoms. In some cases, this depends on a LAMMPS setting, see
for example :doc:`comm_modify vel yes <comm_modify>`.
While the returned vectors or arrays of per-atom data are dimensioned for
the range [0:nmax] - as is the underlying storage - the data is usually
only valid for the range of [0:nlocal], unless the property of interest
is also updated for ghost atoms. In some cases, this depends on a LAMMPS
setting, see for example :doc:`comm_modify vel yes <comm_modify>`.
The actual size can be determined by calling
py:meth:`extract_atom_size() <lammps.lammps.extract_atom_size>`.
:param name: name of the property
:type name: string
@ -984,11 +1143,12 @@ class lammps(object):
ctypes.POINTER(ctypes.c_int64), ctypes.POINTER(ctypes.POINTER(ctypes.c_int64)),
ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.POINTER(ctypes.c_double)),
or NoneType
"""
if dtype == LAMMPS_AUTODETECT:
dtype = self.extract_atom_datatype(name)
if name: name = name.encode()
if name: newname = name.encode()
else: return None
if dtype == LAMMPS_INT:
@ -1005,7 +1165,7 @@ class lammps(object):
self.lib.lammps_extract_atom.restype = POINTER(POINTER(c_int64))
else: return None
ptr = self.lib.lammps_extract_atom(self.lmp, name)
ptr = self.lib.lammps_extract_atom(self.lmp, newname)
if ptr: return ptr
else: return None
@ -1033,47 +1193,47 @@ class lammps(object):
:return: requested data as scalar, pointer to 1d or 2d double array, or None
:rtype: c_double, ctypes.POINTER(c_double), ctypes.POINTER(ctypes.POINTER(c_double)), or NoneType
"""
if cid: cid = cid.encode()
if cid: newcid = cid.encode()
else: return None
if ctype == LMP_TYPE_SCALAR:
if cstyle == LMP_STYLE_GLOBAL:
self.lib.lammps_extract_compute.restype = POINTER(c_double)
with ExceptionCheck(self):
ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype)
ptr = self.lib.lammps_extract_compute(self.lmp,newcid,cstyle,ctype)
return ptr[0]
elif cstyle == LMP_STYLE_ATOM:
return None
elif cstyle == LMP_STYLE_LOCAL:
self.lib.lammps_extract_compute.restype = POINTER(c_int)
with ExceptionCheck(self):
ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype)
ptr = self.lib.lammps_extract_compute(self.lmp,newcid,cstyle,ctype)
return ptr[0]
elif ctype == LMP_TYPE_VECTOR:
self.lib.lammps_extract_compute.restype = POINTER(c_double)
with ExceptionCheck(self):
ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype)
ptr = self.lib.lammps_extract_compute(self.lmp,newcid,cstyle,ctype)
return ptr
elif ctype == LMP_TYPE_ARRAY:
self.lib.lammps_extract_compute.restype = POINTER(POINTER(c_double))
with ExceptionCheck(self):
ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype)
ptr = self.lib.lammps_extract_compute(self.lmp,newcid,cstyle,ctype)
return ptr
elif ctype == LMP_SIZE_COLS:
if cstyle == LMP_STYLE_GLOBAL or cstyle == LMP_STYLE_ATOM or cstyle == LMP_STYLE_LOCAL:
self.lib.lammps_extract_compute.restype = POINTER(c_int)
with ExceptionCheck(self):
ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype)
ptr = self.lib.lammps_extract_compute(self.lmp,newcid,cstyle,ctype)
return ptr[0]
elif ctype == LMP_SIZE_VECTOR or ctype == LMP_SIZE_ROWS:
if cstyle == LMP_STYLE_GLOBAL or cstyle == LMP_STYLE_LOCAL:
self.lib.lammps_extract_compute.restype = POINTER(c_int)
with ExceptionCheck(self):
ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype)
ptr = self.lib.lammps_extract_compute(self.lmp,newcid,cstyle,ctype)
return ptr[0]
return None
@ -1118,21 +1278,21 @@ class lammps(object):
:rtype: c_double, ctypes.POINTER(c_double), ctypes.POINTER(ctypes.POINTER(c_double)), or NoneType
"""
if fid: fid = fid.encode()
if fid: newfid = fid.encode()
else: return None
if fstyle == LMP_STYLE_GLOBAL:
if ftype in (LMP_TYPE_SCALAR, LMP_TYPE_VECTOR, LMP_TYPE_ARRAY):
self.lib.lammps_extract_fix.restype = POINTER(c_double)
with ExceptionCheck(self):
ptr = self.lib.lammps_extract_fix(self.lmp,fid,fstyle,ftype,nrow,ncol)
ptr = self.lib.lammps_extract_fix(self.lmp,newfid,fstyle,ftype,nrow,ncol)
result = ptr[0]
self.lib.lammps_free(ptr)
return result
elif ftype in (LMP_SIZE_VECTOR, LMP_SIZE_ROWS, LMP_SIZE_COLS):
self.lib.lammps_extract_fix.restype = POINTER(c_int)
with ExceptionCheck(self):
ptr = self.lib.lammps_extract_fix(self.lmp,fid,fstyle,ftype,nrow,ncol)
ptr = self.lib.lammps_extract_fix(self.lmp,newfid,fstyle,ftype,nrow,ncol)
return ptr[0]
else:
return None
@ -1147,7 +1307,7 @@ class lammps(object):
else:
return None
with ExceptionCheck(self):
ptr = self.lib.lammps_extract_fix(self.lmp,fid,fstyle,ftype,nrow,ncol)
ptr = self.lib.lammps_extract_fix(self.lmp,newfid,fstyle,ftype,nrow,ncol)
if ftype == LMP_SIZE_COLS:
return ptr[0]
else:
@ -1163,7 +1323,7 @@ class lammps(object):
else:
return None
with ExceptionCheck(self):
ptr = self.lib.lammps_extract_fix(self.lmp,fid,fstyle,ftype,nrow,ncol)
ptr = self.lib.lammps_extract_fix(self.lmp,newfid,fstyle,ftype,nrow,ncol)
if ftype in (LMP_TYPE_VECTOR, LMP_TYPE_ARRAY):
return ptr
else:
@ -1204,15 +1364,16 @@ class lammps(object):
:return: the requested data
:rtype: c_double, (c_double), or NoneType
"""
if name: name = name.encode()
if name: newname = name.encode()
else: return None
if group: group = group.encode()
if group: newgroup = group.encode()
else: newgroup = None
if vartype is None :
vartype = self.lib.lammps_extract_variable_datatype(self.lmp, name)
vartype = self.lib.lammps_extract_variable_datatype(self.lmp, newname)
if vartype == LMP_VAR_EQUAL:
self.lib.lammps_extract_variable.restype = POINTER(c_double)
with ExceptionCheck(self):
ptr = self.lib.lammps_extract_variable(self.lmp,name,group)
ptr = self.lib.lammps_extract_variable(self.lmp, newname, newgroup)
if ptr: result = ptr[0]
else: return None
self.lib.lammps_free(ptr)
@ -1222,7 +1383,7 @@ class lammps(object):
result = (c_double*nlocal)()
self.lib.lammps_extract_variable.restype = POINTER(c_double)
with ExceptionCheck(self):
ptr = self.lib.lammps_extract_variable(self.lmp,name,group)
ptr = self.lib.lammps_extract_variable(self.lmp, newname, newgroup)
if ptr:
for i in range(nlocal): result[i] = ptr[i]
self.lib.lammps_free(ptr)
@ -1231,27 +1392,27 @@ class lammps(object):
elif vartype == LMP_VAR_VECTOR :
nvector = 0
self.lib.lammps_extract_variable.restype = POINTER(c_int)
ptr = self.lib.lammps_extract_variable(self.lmp,name,
ptr = self.lib.lammps_extract_variable(self.lmp, newname,
'LMP_SIZE_VECTOR'.encode())
if ptr :
nvector = ptr[0]
self.lib.lammps_free(ptr)
else :
else:
return None
self.lib.lammps_extract_variable.restype = POINTER(c_double)
result = (c_double*nvector)()
values = self.lib.lammps_extract_variable(self.lmp,name,group)
values = self.lib.lammps_extract_variable(self.lmp, newname, newgroup)
if values :
for i in range(nvector) :
result[i] = values[i]
# do NOT free the values pointer (points to internal vector data)
return result
else :
else:
return None
elif vartype == LMP_VAR_STRING :
self.lib.lammps_extract_variable.restype = c_char_p
with ExceptionCheck(self) :
ptr = self.lib.lammps_extract_variable(self.lmp, name, group)
ptr = self.lib.lammps_extract_variable(self.lmp, newname, newgroup)
return ptr.decode('utf-8')
return None
@ -1282,12 +1443,12 @@ class lammps(object):
:return: either 0 on success or -1 on failure
:rtype: int
"""
if name: name = name.encode()
if name: newname = name.encode()
else: return -1
if value: value = str(value).encode()
if value: newvalue = str(value).encode()
else: return -1
with ExceptionCheck(self):
return self.lib.lammps_set_variable(self.lmp,name,value)
return self.lib.lammps_set_variable(self.lmp, newname, newvalue)
# -------------------------------------------------------------------------
@ -1306,12 +1467,12 @@ class lammps(object):
:return: either 0 on success or -1 on failure
:rtype: int
"""
if name: name = name.encode()
if name: newname = name.encode()
else: return -1
if value: value = str(value).encode()
if value: newvalue = str(value).encode()
else: return -1
with ExceptionCheck(self):
return self.lib.lammps_set_string_variable(self.lmp,name,value)
return self.lib.lammps_set_string_variable(self.lmp,newname,newvalue)
# -------------------------------------------------------------------------
@ -1330,10 +1491,10 @@ class lammps(object):
:return: either 0 on success or -1 on failure
:rtype: int
"""
if name: name = name.encode()
if name: newname = name.encode()
else: return -1
with ExceptionCheck(self):
return self.lib.lammps_set_internal_variable(self.lmp,name,value)
return self.lib.lammps_set_internal_variable(self.lmp,newname,value)
# -------------------------------------------------------------------------
@ -1347,15 +1508,16 @@ class lammps(object):
# e.g. for Python list or NumPy or ctypes
def gather_atoms(self,name,dtype,count):
if name: name = name.encode()
if name: newname = name.encode()
else: newname = None
natoms = self.get_natoms()
with ExceptionCheck(self):
if dtype == 0:
data = ((count*natoms)*c_int)()
self.lib.lammps_gather_atoms(self.lmp,name,dtype,count,data)
self.lib.lammps_gather_atoms(self.lmp,newname,dtype,count,data)
elif dtype == 1:
data = ((count*natoms)*c_double)()
self.lib.lammps_gather_atoms(self.lmp,name,dtype,count,data)
self.lib.lammps_gather_atoms(self.lmp,newname,dtype,count,data)
else:
return None
return data
@ -1363,28 +1525,30 @@ class lammps(object):
# -------------------------------------------------------------------------
def gather_atoms_concat(self,name,dtype,count):
if name: name = name.encode()
if name: newname = name.encode()
else: newname = None
natoms = self.get_natoms()
with ExceptionCheck(self):
if dtype == 0:
data = ((count*natoms)*c_int)()
self.lib.lammps_gather_atoms_concat(self.lmp,name,dtype,count,data)
self.lib.lammps_gather_atoms_concat(self.lmp,newname,dtype,count,data)
elif dtype == 1:
data = ((count*natoms)*c_double)()
self.lib.lammps_gather_atoms_concat(self.lmp,name,dtype,count,data)
self.lib.lammps_gather_atoms_concat(self.lmp,newname,dtype,count,data)
else:
return None
return data
def gather_atoms_subset(self,name,dtype,count,ndata,ids):
if name: name = name.encode()
if name: newname = name.encode()
else: newname = None
with ExceptionCheck(self):
if dtype == 0:
data = ((count*ndata)*c_int)()
self.lib.lammps_gather_atoms_subset(self.lmp,name,dtype,count,ndata,ids,data)
self.lib.lammps_gather_atoms_subset(self.lmp,newname,dtype,count,ndata,ids,data)
elif dtype == 1:
data = ((count*ndata)*c_double)()
self.lib.lammps_gather_atoms_subset(self.lmp,name,dtype,count,ndata,ids,data)
self.lib.lammps_gather_atoms_subset(self.lmp,newname,dtype,count,ndata,ids,data)
else:
return None
return data
@ -1401,16 +1565,18 @@ class lammps(object):
# e.g. for Python list or NumPy or ctypes
def scatter_atoms(self,name,dtype,count,data):
if name: name = name.encode()
if name: newname = name.encode()
else: newname = None
with ExceptionCheck(self):
self.lib.lammps_scatter_atoms(self.lmp,name,dtype,count,data)
self.lib.lammps_scatter_atoms(self.lmp,newname,dtype,count,data)
# -------------------------------------------------------------------------
def scatter_atoms_subset(self,name,dtype,count,ndata,ids,data):
if name: name = name.encode()
if name: newname = name.encode()
else: newname = None
with ExceptionCheck(self):
self.lib.lammps_scatter_atoms_subset(self.lmp,name,dtype,count,ndata,ids,data)
self.lib.lammps_scatter_atoms_subset(self.lmp,newname,dtype,count,ndata,ids,data)
# -------------------------------------------------------------------------
@ -1516,42 +1682,45 @@ class lammps(object):
# NOTE: need to ensure are converting to/from correct Python type
# e.g. for Python list or NumPy or ctypes
def gather(self,name,dtype,count):
if name: name = name.encode()
if name: newname = name.encode()
else: newname = None
natoms = self.get_natoms()
with ExceptionCheck(self):
if dtype == 0:
data = ((count*natoms)*c_int)()
self.lib.lammps_gather(self.lmp,name,dtype,count,data)
self.lib.lammps_gather(self.lmp,newname,dtype,count,data)
elif dtype == 1:
data = ((count*natoms)*c_double)()
self.lib.lammps_gather(self.lmp,name,dtype,count,data)
self.lib.lammps_gather(self.lmp,newname,dtype,count,data)
else:
return None
return data
def gather_concat(self,name,dtype,count):
if name: name = name.encode()
if name: newname = name.encode()
else: newname = None
natoms = self.get_natoms()
with ExceptionCheck(self):
if dtype == 0:
data = ((count*natoms)*c_int)()
self.lib.lammps_gather_concat(self.lmp,name,dtype,count,data)
self.lib.lammps_gather_concat(self.lmp,newname,dtype,count,data)
elif dtype == 1:
data = ((count*natoms)*c_double)()
self.lib.lammps_gather_concat(self.lmp,name,dtype,count,data)
self.lib.lammps_gather_concat(self.lmp,newname,dtype,count,data)
else:
return None
return data
def gather_subset(self,name,dtype,count,ndata,ids):
if name: name = name.encode()
if name: newname = name.encode()
else: newname = None
with ExceptionCheck(self):
if dtype == 0:
data = ((count*ndata)*c_int)()
self.lib.lammps_gather_subset(self.lmp,name,dtype,count,ndata,ids,data)
self.lib.lammps_gather_subset(self.lmp,newname,dtype,count,ndata,ids,data)
elif dtype == 1:
data = ((count*ndata)*c_double)()
self.lib.lammps_gather_subset(self.lmp,name,dtype,count,ndata,ids,data)
self.lib.lammps_gather_subset(self.lmp,newname,dtype,count,ndata,ids,data)
else:
return None
return data
@ -1566,14 +1735,16 @@ class lammps(object):
# e.g. for Python list or NumPy or ctypes
def scatter(self,name,dtype,count,data):
if name: name = name.encode()
if name: newname = name.encode()
else: newname = None
with ExceptionCheck(self):
self.lib.lammps_scatter(self.lmp,name,dtype,count,data)
self.lib.lammps_scatter(self.lmp,newname,dtype,count,data)
def scatter_subset(self,name,dtype,count,ndata,ids,data):
if name: name = name.encode()
if name: newname = name.encode()
else: newname = None
with ExceptionCheck(self):
self.lib.lammps_scatter_subset(self.lmp,name,dtype,count,ndata,ids,data)
self.lib.lammps_scatter_subset(self.lmp,newname,dtype,count,ndata,ids,data)
# -------------------------------------------------------------------------
@ -1837,6 +2008,21 @@ class lammps(object):
# -------------------------------------------------------------------------
@property
def has_curl_support(self):
""" Report whether the LAMMPS shared library was compiled with support
for downloading files through libcurl.
This is a wrapper around the :cpp:func:`lammps_config_has_curl_support`
function of the library interface.
:return: state of CURL support
:rtype: bool
"""
return self.lib.lammps_config_has_curl_support() != 0
# -------------------------------------------------------------------------
def has_package(self, name):
""" Report if the named package has been enabled in the LAMMPS shared library.
@ -1920,7 +2106,7 @@ class lammps(object):
""" List of the names of enabled packages in the LAMMPS shared library
This is a wrapper around the functions :cpp:func:`lammps_config_package_count`
and :cpp:func`lammps_config_package_name` of the library interface.
and :cpp:func:`lammps_config_package_name` of the library interface.
:return
"""
@ -2325,9 +2511,9 @@ class lammps(object):
:rtype: int
"""
style = style.encode()
newstyle = style.encode()
exact = int(exact)
idx = self.lib.lammps_find_pair_neighlist(self.lmp, style, exact, nsub, reqid)
idx = self.lib.lammps_find_pair_neighlist(self.lmp, newstyle, exact, nsub, reqid)
return idx
# -------------------------------------------------------------------------
@ -2348,8 +2534,8 @@ class lammps(object):
:rtype: int
"""
fixid = fixid.encode()
idx = self.lib.lammps_find_fix_neighlist(self.lmp, fixid, reqid)
newfixid = fixid.encode()
idx = self.lib.lammps_find_fix_neighlist(self.lmp, newfixid, reqid)
return idx
# -------------------------------------------------------------------------
@ -2371,6 +2557,10 @@ class lammps(object):
:rtype: int
"""
computeid = computeid.encode()
idx = self.lib.lammps_find_compute_neighlist(self.lmp, computeid, reqid)
newcomputeid = computeid.encode()
idx = self.lib.lammps_find_compute_neighlist(self.lmp, newcomputeid, reqid)
return idx
# Local Variables:
# fill-column: 80
# End:

View File

@ -58,7 +58,8 @@ class numpy_wrapper:
# -------------------------------------------------------------------------
def extract_atom(self, name, dtype=LAMMPS_AUTODETECT, nelem=LAMMPS_AUTODETECT, dim=LAMMPS_AUTODETECT):
def extract_atom(self, name, dtype=LAMMPS_AUTODETECT, nelem=LAMMPS_AUTODETECT,
dim=LAMMPS_AUTODETECT):
"""Retrieve per-atom properties from LAMMPS as NumPy arrays
This is a wrapper around the :py:meth:`lammps.extract_atom()` method.
@ -67,16 +68,16 @@ class numpy_wrapper:
.. note::
The returned arrays of per-atom data are by default dimensioned
for the range [0:nlocal] since that data is *always* valid. The
underlying storage for the data, however, is typically allocated
for the range of [0:nmax]. Whether there is valid data in the range
[nlocal:nlocal+nghost] depends on whether the property of interest
is also updated for ghost atoms. This is not often the case. In
some cases, it depends on a LAMMPS setting, see for example
:doc:`comm_modify vel yes <comm_modify>`. By using the optional
*nelem* parameter the size of the returned NumPy can be overridden.
There is no check whether the number of elements chosen is valid.
The returned vectors or arrays of per-atom data are dimensioned
according to the return value of :py:meth:`lammps.extract_atom_size()`.
Except for the "mass" property, the underlying storage will always be
dimensioned for the range [0:nmax]. The actual usable data may be
only in the range [0:nlocal] or [0:nlocal][0:dim]. Whether there is
valid data in the range [nlocal:nlocal+nghost] or [nlocal:local+nghost][0:dim]
depends on whether the property of interest is also updated for ghost atoms.
Also the value of *dim* depends on the value of *name*. By using the optional
*nelem* and *dim* parameters the dimensions of the returned NumPy array can
be overridden. There is no check whether the number of elements chosen is valid.
:param name: name of the property
:type name: string
@ -93,21 +94,10 @@ class numpy_wrapper:
dtype = self.lmp.extract_atom_datatype(name)
if nelem == LAMMPS_AUTODETECT:
if name == "mass":
nelem = self.lmp.extract_global("ntypes") + 1
else:
nelem = self.lmp.extract_global("nlocal")
nelem = self.lmp.extract_atom_size(name, LMP_SIZE_ROWS)
if dim == LAMMPS_AUTODETECT:
if dtype in (LAMMPS_INT_2D, LAMMPS_DOUBLE_2D, LAMMPS_INT64_2D):
# TODO add other fields
if name in ("x", "v", "f", "x0","omega", "angmom", "torque", "csforce", "vforce", "vest"):
dim = 3
elif name == "smd_data_9":
dim = 9
elif name == "smd_stress":
dim = 6
else:
dim = 2
dim = self.lmp.extract_atom_size(name, LMP_SIZE_COLS)
else:
dim = 1
@ -123,37 +113,6 @@ class numpy_wrapper:
# -------------------------------------------------------------------------
def extract_atom_iarray(self, name, nelem, dim=1):
warnings.warn("deprecated, use extract_atom instead", DeprecationWarning)
if name in ['id', 'molecule']:
c_int_type = self.lmp.c_tagint
elif name in ['image']:
c_int_type = self.lmp.c_imageint
else:
c_int_type = c_int
if dim == 1:
raw_ptr = self.lmp.extract_atom(name, LAMMPS_INT)
else:
raw_ptr = self.lmp.extract_atom(name, LAMMPS_INT_2D)
return self.iarray(c_int_type, raw_ptr, nelem, dim)
# -------------------------------------------------------------------------
def extract_atom_darray(self, name, nelem, dim=1):
warnings.warn("deprecated, use extract_atom instead", DeprecationWarning)
if dim == 1:
raw_ptr = self.lmp.extract_atom(name, LAMMPS_DOUBLE)
else:
raw_ptr = self.lmp.extract_atom(name, LAMMPS_DOUBLE_2D)
return self.darray(raw_ptr, nelem, dim)
# -------------------------------------------------------------------------
def extract_compute(self, cid, cstyle, ctype):
"""Retrieve data from a LAMMPS compute
@ -275,11 +234,11 @@ class numpy_wrapper:
# -------------------------------------------------------------------------
def gather_bonds(self):
"""Retrieve global list of bonds as NumPy array
"""Retrieve global list of bonds as a NumPy array
.. versionadded:: 28Jul2021
This is a wrapper around :py:meth:`lammps.gather_bonds() <lammps.lammps.gather_bonds()>`
This is a wrapper around :py:meth:`lammps.gather_bonds() <lammps.lammps.gather_bonds()>`.
It behaves the same as the original method, but returns a NumPy array instead
of a ``ctypes`` list.
@ -293,11 +252,11 @@ class numpy_wrapper:
# -------------------------------------------------------------------------
def gather_angles(self):
""" Retrieve global list of angles as NumPy array
""" Retrieve global list of angles as a NumPy array
.. versionadded:: 8Feb2023
This is a wrapper around :py:meth:`lammps.gather_angles() <lammps.lammps.gather_angles()>`
This is a wrapper around :py:meth:`lammps.gather_angles() <lammps.lammps.gather_angles()>`.
It behaves the same as the original method, but returns a NumPy array instead
of a ``ctypes`` list.
@ -311,11 +270,11 @@ class numpy_wrapper:
# -------------------------------------------------------------------------
def gather_dihedrals(self):
""" Retrieve global list of dihedrals as NumPy array
""" Retrieve global list of dihedrals as a NumPy array
.. versionadded:: 8Feb2023
This is a wrapper around :py:meth:`lammps.gather_dihedrals() <lammps.lammps.gather_dihedrals()>`
This is a wrapper around :py:meth:`lammps.gather_dihedrals() <lammps.lammps.gather_dihedrals()>`.
It behaves the same as the original method, but returns a NumPy array instead
of a ``ctypes`` list.
@ -329,11 +288,11 @@ class numpy_wrapper:
# -------------------------------------------------------------------------
def gather_impropers(self):
""" Retrieve global list of impropers as NumPy array
""" Retrieve global list of impropers as a NumPy array
.. versionadded:: 8Feb2023
This is a wrapper around :py:meth:`lammps.gather_impropers() <lammps.lammps.gather_impropers()>`
This is a wrapper around :py:meth:`lammps.gather_impropers() <lammps.lammps.gather_impropers()>`.
It behaves the same as the original method, but returns a NumPy array instead
of a ``ctypes`` list.

View File

@ -358,6 +358,7 @@ class Atom2D(Atom):
@property
def velocity(self):
"""Access to velocity of an atom
:getter: Return velocity of atom
:setter: Set velocity of atom
:type: numpy.array (float, float)
@ -372,6 +373,7 @@ class Atom2D(Atom):
@property
def force(self):
"""Access to force of an atom
:getter: Return force of atom
:setter: Set force of atom
:type: numpy.array (float, float)
@ -418,7 +420,7 @@ class PyLammps(object):
"""
This is a Python wrapper class around the lower-level
:py:class:`lammps` class, exposing a more Python-like,
object-oriented interface for prototyping system inside of IPython and
object-oriented interface for prototyping systems inside of IPython and
Jupyter notebooks.
It either creates its own instance of :py:class:`lammps` or can be
@ -463,13 +465,19 @@ class PyLammps(object):
self.lmp = lammps(name=name,cmdargs=cmdargs,ptr=ptr,comm=comm)
else:
self.lmp = lammps(name=name,cmdargs=cmdargs,ptr=None,comm=comm)
print("LAMMPS output is captured by PyLammps wrapper")
self.comm_nprocs = self.lmp.extract_setting("world_size")
self.comm_me = self.lmp.extract_setting("world_rank")
if self.comm_me == 0:
print("LAMMPS output is captured by PyLammps wrapper")
if self.comm_nprocs > 1:
print("WARNING: Using PyLammps with multiple MPI ranks is experimental. Not all functionality is supported.")
self._cmd_history = []
self._enable_cmd_history = False
self.runs = []
if not self.lmp.has_package("PYTHON"):
print("WARNING: run thermo data not captured since PYTHON LAMMPS package is not enabled")
if self.comm_me == 0:
print("WARNING: run thermo data not captured since PYTHON LAMMPS package is not enabled")
def __enter__(self):
return self
@ -550,7 +558,7 @@ class PyLammps(object):
Commands will be added to the command history but not executed.
Add `commands` only to the command history, but do not execute them, so that you can
conveniently create Lammps input files, using
conveniently create LAMMPS input files, using
:py:meth:`PyLammps.write_script()`.
"""
self._cmd_history.append(cmd)
@ -727,7 +735,15 @@ class PyLammps(object):
def eval(self, expr):
"""
Evaluate expression
Evaluate LAMMPS input file expression.
This is equivalent to using immediate variable expressions in the format "$(...)"
in the LAMMPS input and will return the result of that expression.
.. warning::
This function is only supported on MPI rank 0. Calling it from a different
MPI rank will raise an exception.
:param expr: the expression string that should be evaluated inside of LAMMPS
:type expr: string
@ -735,6 +751,9 @@ class PyLammps(object):
:return: the value of the evaluated expression
:rtype: float if numeric, string otherwise
"""
if self.comm_me > 0:
raise Exception("PyLammps.eval() may only be used on MPI rank 0")
value = self.lmp_print('"$(%s)"' % expr).strip()
try:
return float(value)
@ -796,18 +815,16 @@ class PyLammps(object):
comm = {}
comm['nprocs'] = self.lmp.extract_setting("world_size")
comm['nthreads'] = self.lmp.extract_setting("nthreads")
comm['proc_grid'] = comm['procgrid'] = self.lmp.extract_global("procgrid")
idx = self.lmp.extract_setting("comm_style")
comm['comm_style'] = ('brick', 'tiled')[idx]
idx = self.lmp.extract_setting("comm_style")
comm['comm_layout'] = ('uniform', 'nonuniform', 'irregular')[idx]
comm['ghost_velocity'] = self.lmp.extract_setting("ghost_velocity") == 1
for line in output:
if line.startswith("MPI library"):
comm['mpi_version'] = line.split(':')[1].strip()
elif line.startswith("Comm style"):
parts = self._split_values(line)
comm['comm_style'] = self._get_pair(parts[0])[1]
comm['comm_layout'] = self._get_pair(parts[1])[1]
elif line.startswith("Processor grid"):
comm['proc_grid'] = [int(x) for x in self._get_pair(line)[1].split('x')]
elif line.startswith("Communicate velocities for ghost atoms"):
comm['ghost_velocity'] = (self._get_pair(line)[1] == "yes")
return comm
def _parse_element_list(self, output):
@ -893,7 +910,7 @@ class PyLammps(object):
class IPyLammps(PyLammps):
"""
IPython wrapper for LAMMPS which adds embedded graphics capabilities to PyLammmps interface
IPython wrapper for LAMMPS which adds embedded graphics capabilities to PyLammps interface
It either creates its own instance of :py:class:`lammps` or can be
initialized with an existing instance. The arguments are the same of the