Merge branch 'master' into collected-small-changes

This commit is contained in:
Axel Kohlmeyer
2020-09-18 16:11:47 -04:00
27 changed files with 1546 additions and 299 deletions

View File

@ -3,11 +3,16 @@
# #
# Requires latest gcovr (for GCC 8.1 support):# # Requires latest gcovr (for GCC 8.1 support):#
# pip install git+https://github.com/gcovr/gcovr.git # pip install git+https://github.com/gcovr/gcovr.git
#
# For Python coverage the coverage package needs to be installed
############################################################################### ###############################################################################
if(ENABLE_COVERAGE) if(ENABLE_COVERAGE)
find_program(GCOVR_BINARY gcovr) find_program(GCOVR_BINARY gcovr)
find_package_handle_standard_args(GCOVR DEFAULT_MSG GCOVR_BINARY) find_package_handle_standard_args(GCOVR DEFAULT_MSG GCOVR_BINARY)
find_program(COVERAGE_BINARY coverage)
find_package_handle_standard_args(COVERAGE DEFAULT_MSG COVERAGE_BINARY)
if(GCOVR_FOUND) if(GCOVR_FOUND)
get_filename_component(ABSOLUTE_LAMMPS_SOURCE_DIR ${LAMMPS_SOURCE_DIR} ABSOLUTE) get_filename_component(ABSOLUTE_LAMMPS_SOURCE_DIR ${LAMMPS_SOURCE_DIR} ABSOLUTE)
@ -46,4 +51,30 @@ if(ENABLE_COVERAGE)
) )
add_dependencies(reset_coverage clean_coverage_html) add_dependencies(reset_coverage clean_coverage_html)
endif() endif()
if(COVERAGE_FOUND)
set(PYTHON_COVERAGE_HTML_DIR ${CMAKE_BINARY_DIR}/python_coverage_html)
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/unittest/python/.coverage
COMMAND ${COVERAGE_BINARY} combine
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/unittest/python
COMMENT "Combine Python coverage files..."
)
add_custom_target(
gen_python_coverage_html
COMMAND ${COVERAGE_BINARY} html -d ${PYTHON_COVERAGE_HTML_DIR}
DEPENDS ${CMAKE_BINARY_DIR}/unittest/python/.coverage
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/unittest/python
COMMENT "Generating HTML Python coverage report..."
)
add_custom_target(
gen_python_coverage_xml
COMMAND ${COVERAGE_BINARY} xml -o ${CMAKE_BINARY_DIR}/python_coverage.xml
DEPENDS ${CMAKE_BINARY_DIR}/unittest/python/.coverage
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/unittest/python
COMMENT "Generating XML Python coverage report..."
)
endif()
endif() endif()

View File

@ -26,6 +26,8 @@ computes, fixes, or variables in LAMMPS.
----------------------- -----------------------
.. doxygenenum:: _LMP_DATATYPE_CONST
.. doxygenenum:: _LMP_STYLE_CONST .. doxygenenum:: _LMP_STYLE_CONST
.. doxygenenum:: _LMP_TYPE_CONST .. doxygenenum:: _LMP_TYPE_CONST

View File

@ -84,11 +84,22 @@ event as atoms are migrating between sub-domains.
----------------------- -----------------------
.. doxygenfunction:: lammps_extract_global_datatype
:project: progguide
-----------------------
.. doxygenfunction:: lammps_extract_global .. doxygenfunction:: lammps_extract_global
:project: progguide :project: progguide
----------------------- -----------------------
.. doxygenfunction:: lammps_extract_atom_datatype
:project: progguide
-----------------------
.. doxygenfunction:: lammps_extract_atom .. doxygenfunction:: lammps_extract_atom
:project: progguide :project: progguide

View File

@ -28,16 +28,58 @@ There are multiple Python interface classes in the :py:mod:`lammps` module:
---------- ----------
Setting up a Python virtual environment
***************************************
LAMMPS and its Python module can be installed together into a Python virtual
environment. This lets you isolate your customized Python environment from
your user or system installation. The following is a minimal working example:
.. code-block:: bash
# create and change into build directory
mkdir build
cd build
# create virtual environment
virtualenv myenv
# Add venv lib folder to LD_LIBRARY_PATH when activating it
echo 'export LD_LIBRARY_PATH=$VIRTUAL_ENV/lib:$LD_LIBRARY_PATH' >> myenv/bin/activate
# Add LAMMPS_POTENTIALS path when activating venv
echo 'export LAMMPS_POTENTIALS=$VIRTUAL_ENV/share/lammps/potentials' >> myenv/bin/activate
# activate environment
source myenv/bin/activate
# configure LAMMPS compilation
# compiles as shared library with PYTHON package and C++ exceptions
# and installs into myvenv
(myenv)$ cmake -C ../cmake/presets/minimal.cmake \
-D BUILD_SHARED_LIBS=on \
-D PKG_PYTHON=on \
-D LAMMPS_EXCEPTIONS=on \
-D CMAKE_INSTALL_PREFIX=$VIRTUAL_ENV \
../cmake
# compile LAMMPS
(myenv)$ cmake --build . --parallel
# install LAMMPS into myvenv
(myenv)$ cmake --install .
Creating or deleting a LAMMPS object Creating or deleting a LAMMPS object
************************************ ************************************
With the Python interface the creation of a :cpp:class:`LAMMPS With the Python interface the creation of a :cpp:class:`LAMMPS
<LAMMPS_NS::LAMMPS>` instance is included in the constructor for the <LAMMPS_NS::LAMMPS>` instance is included in the constructors for the
:py:func:`lammps <lammps.lammps>` class. Internally it will call either :py:meth:`lammps <lammps.lammps.__init__()>`, :py:meth:`PyLammps <lammps.PyLammps.__init__()>`,
:cpp:func:`lammps_open` or :cpp:func:`lammps_open_no_mpi` from the C and :py:meth:`PyLammps <lammps.IPyLammps.__init__()>` classes.
Internally it will call either :cpp:func:`lammps_open` or :cpp:func:`lammps_open_no_mpi` from the C
library API to create the class instance. library API to create the class instance.
All arguments are optional. The *name* argument is to allow loading a All arguments are optional. The *name* argument allows loading a
LAMMPS shared library that is named ``liblammps_machine.so`` instead of LAMMPS shared library that is named ``liblammps_machine.so`` instead of
the default name of ``liblammps.so``. In most cases the latter will be the default name of ``liblammps.so``. In most cases the latter will be
installed or used. The *ptr* argument is for use of the installed or used. The *ptr* argument is for use of the
@ -48,22 +90,99 @@ to the Python class and used instead of creating a new instance. The
*comm* argument may be used in combination with the `mpi4py <mpi4py_url_>`_ *comm* argument may be used in combination with the `mpi4py <mpi4py_url_>`_
module to pass an MPI communicator to LAMMPS and thus it is possible module to pass an MPI communicator to LAMMPS and thus it is possible
to run the Python module like the library interface on a subset of the to run the Python module like the library interface on a subset of the
MPI ranks after splitting the communicator. Here is a simple example: MPI ranks after splitting the communicator.
.. code-block:: python
from lammps import lammps Here are simple examples using all three Python interfaces:
# NOTE: argv[0] is set by the Python module .. tabs::
args = ["-log", "none"]
# create LAMMPS instance
lmp = lammps(cmdargs=args)
# get and print numerical version code
print("LAMMPS Version: ", lmp.version())
# explicitly close and delete LAMMPS instance (optional)
lmp.close()
Same as with the :ref:`C library API <lammps_c_api>` this will use the .. tab:: lammps API
.. code-block:: python
from lammps import lammps
# NOTE: argv[0] is set by the lammps class constructor
args = ["-log", "none"]
# create LAMMPS instance
lmp = lammps(cmdargs=args)
# get and print numerical version code
print("LAMMPS Version: ", lmp.version())
# explicitly close and delete LAMMPS instance (optional)
lmp.close()
.. tab:: PyLammps API
The :py:class:`PyLammps` class is a wrapper around the
:py:class:`lammps` class and all of its lower level functions.
By default, it will create a new instance of :py:class:`lammps` passing
along all arguments to the constructor of :py:class:`lammps`.
.. code-block:: python
from lammps import PyLammps
# NOTE: argv[0] is set by the lammps class constructor
args = ["-log", "none"]
# create LAMMPS instance
L = PyLammps(cmdargs=args)
# get and print numerical version code
print("LAMMPS Version: ", L.version())
# explicitly close and delete LAMMPS instance (optional)
L.close()
:py:class:`PyLammps` objects can also be created on top of an existing :py:class:`lammps` object:
.. code-block:: Python
from lammps import lammps, PyLammps
...
# create LAMMPS instance
lmp = lammps(cmdargs=args)
# create PyLammps instance using previously created LAMMPS instance
L = PyLammps(ptr=lmp)
This is useful if you have to create the :py:class:`lammps <lammps.lammps>`
instance is a specific way, but want to take advantage of the
:py:class:`PyLammps <lammps.PyLammps>` interface.
.. tab:: IPyLammps API
The :py:class:`IPyLammps` class is an extension of the
:py:class:`PyLammps` class. It has the same construction behavior. By
default, it will create a new instance of :py:class:`lammps` passing
along all arguments to the constructor of :py:class:`lammps`.
.. code-block:: python
from lammps import IPyLammps
# NOTE: argv[0] is set by the lammps class constructor
args = ["-log", "none"]
# create LAMMPS instance
L = IPyLammps(cmdargs=args)
# get and print numerical version code
print("LAMMPS Version: ", L.version())
# explicitly close and delete LAMMPS instance (optional)
L.close()
You can also initialize IPyLammps on top of an existing :py:class:`lammps` or :py:class:`PyLammps` object:
.. code-block:: Python
from lammps import lammps, IPyLammps
...
# create LAMMPS instance
lmp = lammps(cmdargs=args)
# create PyLammps instance using previously created LAMMPS instance
L = PyLammps(ptr=lmp)
This is useful if you have to create the :py:class:`lammps <lammps.lammps>`
instance is a specific way, but want to take advantage of the
:py:class:`IPyLammps <lammps.IPyLammps>` interface.
In all of the above cases, same as with the :ref:`C library API <lammps_c_api>`, this will use the
``MPI_COMM_WORLD`` communicator for the MPI library that LAMMPS was ``MPI_COMM_WORLD`` communicator for the MPI library that LAMMPS was
compiled with. The :py:func:`lmp.close() <lammps.lammps.close>` call is compiled with. The :py:func:`lmp.close() <lammps.lammps.close>` call is
optional since the LAMMPS class instance will also be deleted optional since the LAMMPS class instance will also be deleted
@ -73,39 +192,109 @@ destructor.
Executing LAMMPS commands Executing LAMMPS commands
************************* *************************
Once an instance of the :py:class:`lammps <lammps.lammps>` class is Once an instance of the :py:class:`lammps`, :py:class:`PyLammps`, or
created, there are multiple ways to "feed" it commands. In a way that is :py:class:`IPyLammps` class is created, there are multiple ways to "feed" it
not very different from running a LAMMPS input script, except that commands. In a way that is not very different from running a LAMMPS input
Python has many more facilities for structured programming than the script, except that Python has many more facilities for structured
LAMMPS input script syntax. Furthermore it is possible to "compute" programming than the LAMMPS input script syntax. Furthermore it is possible
what the next LAMMPS command should be. Same as in the equivalent `C to "compute" what the next LAMMPS command should be.
library functions <pg_lib_execute>`, commands can be read from a file, a
single string, a list of strings and a block of commands in a single
multi-line string. They are processed under the same boundary conditions
as the C library counterparts. The example below demonstrates the use
of :py:func:`lammps.file`, :py:func:`lammps.command`,
:py:func:`lammps.commands_list`, and :py:func:`lammps.commands_string`:
.. code-block:: python .. tabs::
from lammps import lammps .. tab:: lammps API
lmp = lammps() Same as in the equivalent
# read commands from file 'in.melt' :doc:`C library functions <pg_lib_execute>`, commands can be read from a file, a
lmp.file('in.melt') single string, a list of strings and a block of commands in a single
# issue a single command multi-line string. They are processed under the same boundary conditions
lmp.command('variable zpos index 1.0') as the C library counterparts. The example below demonstrates the use
# create 10 groups with 10 atoms each of :py:func:`lammps.file`, :py:func:`lammps.command`,
cmds = ["group g{} id {}:{}".format(i,10*i+1,10*(i+1)) for i in range(10)] :py:func:`lammps.commands_list`, and :py:func:`lammps.commands_string`:
lmp.commands_list(cmds)
# run commands from a multi-line string .. code-block:: python
block = """
clear from lammps import lammps
region box block 0 2 0 2 0 2 lmp = lammps()
create_box 1 box # read commands from file 'in.melt'
create_atoms 1 single 1.0 1.0 ${zpos} lmp.file('in.melt')
""" # issue a single command
lmp.commands_string(block) lmp.command('variable zpos index 1.0')
# create 10 groups with 10 atoms each
cmds = ["group g{} id {}:{}".format(i,10*i+1,10*(i+1)) for i in range(10)]
lmp.commands_list(cmds)
# run commands from a multi-line string
block = """
clear
region box block 0 2 0 2 0 2
create_box 1 box
create_atoms 1 single 1.0 1.0 ${zpos}
"""
lmp.commands_string(block)
.. tab:: PyLammps/IPyLammps API
Unlike the lammps API, the PyLammps/IPyLammps APIs allow running LAMMPS
commands by calling equivalent member functions.
For instance, the following LAMMPS command
.. code-block:: LAMMPS
region box block 0 10 0 5 -0.5 0.5
can be executed using the following Python code if *L* is a :py:class:`lammps` instance:
.. code-block:: Python
L.command("region box block 0 10 0 5 -0.5 0.5")
With the PyLammps interface, any LAMMPS command can be split up into arbitrary parts.
These parts are then passed to a member function with the name of the command.
For the ``region`` command that means the :code:`region` method can be called.
The arguments of the command can be passed as one string, or
individually.
.. code-block:: Python
L.region("box block", 0, 10, 0, 5, -0.5, 0.5)
In this example all parameters except the first are Python floating-point literals. The
PyLammps interface takes the entire parameter list and transparently
merges it to a single command string.
The benefit of this approach is avoiding redundant command calls and easier
parameterization. In the original interface parameterization this needed to be done
manually by creating formatted strings.
.. code-block:: Python
L.command("region box block %f %f %f %f %f %f" % (xlo, xhi, ylo, yhi, zlo, zhi))
In contrast, methods of PyLammps accept parameters directly and will convert
them automatically to a final command string.
.. code-block:: Python
L.region("box block", xlo, xhi, ylo, yhi, zlo, zhi)
Using these facilities, the example shown for the lammps API can be rewritten as follows:
.. code-block:: python
from lammps import PyLammps
L = PyLammps()
# read commands from file 'in.melt'
L.file('in.melt')
# issue a single command
L.variable('zpos', 'index', 1.0)
# create 10 groups with 10 atoms each
for i in range(10):
L.group(f"g{i}", "id", f"{10*i+1}:{10*(i+1)}")
L.clear()
L.region("box block", 0, 2, 0, 2, 0, 2)
L.create_box(1, "box")
L.create_atoms(1, "single", 1.0, 1.0, "${zpos}")
---------- ----------
@ -130,14 +319,34 @@ functions. Below is a detailed documentation of the API.
The ``PyLammps`` class API The ``PyLammps`` class API
************************** **************************
The :py:class:`PyLammps <lammps.PyLammps>` class is a wrapper that creates a
simpler, more "Pythonic" interface to common LAMMPS functionality. LAMMPS
data structures are exposed through objects and properties. This makes Python
scripts shorter and more concise. See the :doc:`PyLammps Tutorial
<Howto_pylammps>` for an introduction on how to use this interface.
.. autoclass:: lammps.PyLammps .. autoclass:: lammps.PyLammps
:members: :members:
.. autoclass:: lammps.AtomList
:members:
.. autoclass:: lammps.Atom
:members:
.. autoclass:: lammps.Atom2D
:members:
---------- ----------
The ``IPyLammps`` class API The ``IPyLammps`` class API
*************************** ***************************
The :py:class:`IPyLammps <lammps.PyLammps>` class is an extension of
:py:class:`PyLammps <lammps.PyLammps>`, adding additional functions to
quickly display visualizations such as images and videos inside of IPython.
See the :doc:`PyLammps Tutorial <Howto_pylammps>` for examples.
.. autoclass:: lammps.IPyLammps .. autoclass:: lammps.IPyLammps
:members: :members:
@ -150,14 +359,24 @@ The :py:mod:`lammps` module additionally contains several constants
and the :py:class:`NeighList <lammps.NeighList>` class: and the :py:class:`NeighList <lammps.NeighList>` class:
.. _py_data_constants: .. _py_data_constants:
.. py:data:: LAMMPS_INT, LAMMPS_DOUBLE, LAMMPS_BIGINT, LAMMPS_TAGINT, LAMMPS_STRING
Data Types
----------
.. py:data:: LAMMPS_INT, LAMMPS_INT_2D, LAMMPS_DOUBLE, LAMMPS_DOUBLE_2D, LAMMPS_INT64, LAMMPS_INT64_2D, LAMMPS_STRING
:type: int :type: int
Constants in the :py:mod:`lammps` module to indicate how to Constants in the :py:mod:`lammps` module to indicate how to
cast data when the C library function returns a void pointer. cast data when the C library function returns a void pointer.
Used in :py:func:`lammps.extract_global`. Used in :py:func:`lammps.extract_global` and :py:func:`lammps.extract_atom`.
See :cpp:enum:`_LMP_DATATYPE_CONST` for the equivalent constants in the
C library interface.
.. _py_style_constants: .. _py_style_constants:
Style Constants
---------------
.. py:data:: LMP_STYLE_GLOBAL, LMP_STYLE_ATOM, LMP_STYLE_LOCAL .. py:data:: LMP_STYLE_GLOBAL, LMP_STYLE_ATOM, LMP_STYLE_LOCAL
:type: int :type: int
@ -167,6 +386,10 @@ and the :py:class:`NeighList <lammps.NeighList>` class:
:py:func:`lammps.extract_compute` and :py:func:`lammps.extract_fix`. :py:func:`lammps.extract_compute` and :py:func:`lammps.extract_fix`.
.. _py_type_constants: .. _py_type_constants:
Type Constants
--------------
.. py:data:: LMP_TYPE_SCALAR, LMP_TYLE_VECTOR, LMP_TYPE_ARRAY, LMP_SIZE_VECTOR, LMP_SIZE_ROWS, LMP_SIZE_COLS .. py:data:: LMP_TYPE_SCALAR, LMP_TYLE_VECTOR, LMP_TYPE_ARRAY, LMP_SIZE_VECTOR, LMP_SIZE_ROWS, LMP_SIZE_COLS
:type: int :type: int
@ -176,13 +399,36 @@ and the :py:class:`NeighList <lammps.NeighList>` class:
:py:func:`lammps.extract_compute` and :py:func:`lammps.extract_fix`. :py:func:`lammps.extract_compute` and :py:func:`lammps.extract_fix`.
.. _py_var_constants: .. _py_var_constants:
Variable Style Constants
------------------------
.. py:data:: LMP_VAR_EQUAL, LMP_VAR_ATOM .. py:data:: LMP_VAR_EQUAL, LMP_VAR_ATOM
:type: int :type: int
Constants in the :py:mod:`lammps` module to select what style of Constants in the :py:mod:`lammps` module to select what style of
variable to query when calling :py:func:`lammps.extract_variable`. variable to query when calling :py:func:`lammps.extract_variable`.
Classes representing internal objects
-------------------------------------
.. autoclass:: lammps.NeighList .. autoclass:: lammps.NeighList
:members: :members:
:no-undoc-members: :no-undoc-members:
LAMMPS error handling in Python
*******************************
Compiling the shared library with :ref:`C++ exception support <exceptions>` provides a better error
handling experience. Without exceptions the LAMMPS code will terminate the
current Python process with an error message. C++ exceptions allow capturing
them on the C++ side and rethrowing them on the Python side. This way
LAMMPS errors can be handled through the Python exception handling mechanism.
.. warning::
Capturing a LAMMPS exception in Python can still mean that the
current LAMMPS process is in an illegal state and must be terminated. It is
advised to save your data and terminate the Python instance as quickly as
possible.

View File

@ -2018,6 +2018,7 @@ Nakano
nall nall
namespace namespace
namespaces namespaces
namedtuple
nan nan
NaN NaN
Nandor Nandor

View File

@ -23,12 +23,12 @@ from lammps import lammps
def end_of_step_callback(lmp): def end_of_step_callback(lmp):
L = lammps(ptr=lmp) L = lammps(ptr=lmp)
t = L.extract_global("ntimestep", 0) t = L.extract_global("ntimestep")
print("### END OF STEP ###", t) print("### END OF STEP ###", t)
def post_force_callback(lmp, v): def post_force_callback(lmp, v):
L = lammps(ptr=lmp) L = lammps(ptr=lmp)
t = L.extract_global("ntimestep", 0) t = L.extract_global("ntimestep")
print("### POST_FORCE ###", t) print("### POST_FORCE ###", t)
""" """

View File

@ -35,14 +35,13 @@ def post_force_callback(lmp, v):
#mylist = L.get_neighlist(0) #mylist = L.get_neighlist(0)
mylist = L.find_pair_neighlist("lj/cut", request=0) mylist = L.find_pair_neighlist("lj/cut", request=0)
print(pid_prefix, mylist) print(pid_prefix, mylist)
nlocal = L.extract_global("nlocal", 0) nlocal = L.extract_global("nlocal")
nghost = L.extract_global("nghost", 0) nghost = L.extract_global("nghost")
ntypes = L.extract_global("ntypes", 0) mass = L.numpy.extract_atom("mass")
mass = L.numpy.extract_atom_darray("mass", ntypes+1) atype = L.numpy.extract_atom("type", nelem=nlocal+nghost)
atype = L.numpy.extract_atom_iarray("type", nlocal+nghost) x = L.numpy.extract_atom("x", nelem=nlocal+nghost, dim=3)
x = L.numpy.extract_atom_darray("x", nlocal+nghost, dim=3) v = L.numpy.extract_atom("v", nelem=nlocal+nghost, dim=3)
v = L.numpy.extract_atom_darray("v", nlocal+nghost, dim=3) f = L.numpy.extract_atom("f", nelem=nlocal+nghost, dim=3)
f = L.numpy.extract_atom_darray("f", nlocal+nghost, dim=3)
for iatom, numneigh, neighs in mylist: for iatom, numneigh, neighs in mylist:
print(pid_prefix, "- {}".format(iatom), x[iatom], v[iatom], f[iatom], " : ", numneigh, "Neighbors") print(pid_prefix, "- {}".format(iatom), x[iatom], v[iatom], f[iatom], " : ", numneigh, "Neighbors")

View File

@ -1,12 +1,12 @@
from __future__ import print_function from __future__ import print_function
import lammps from lammps import lammps, LAMMPS_INT, LAMMPS_DOUBLE
import ctypes import ctypes
import traceback import traceback
import numpy as np import numpy as np
class LAMMPSFix(object): class LAMMPSFix(object):
def __init__(self, ptr, group_name="all"): def __init__(self, ptr, group_name="all"):
self.lmp = lammps.lammps(ptr=ptr) self.lmp = lammps(ptr=ptr)
self.group_name = group_name self.group_name = group_name
class LAMMPSFixMove(LAMMPSFix): class LAMMPSFixMove(LAMMPSFix):
@ -39,19 +39,18 @@ class NVE(LAMMPSFixMove):
assert(self.group_name == "all") assert(self.group_name == "all")
def init(self): def init(self):
dt = self.lmp.extract_global("dt", 1) dt = self.lmp.extract_global("dt")
ftm2v = self.lmp.extract_global("ftm2v", 1) ftm2v = self.lmp.extract_global("ftm2v")
self.ntypes = self.lmp.extract_global("ntypes", 0) self.ntypes = self.lmp.extract_global("ntypes")
self.dtv = dt self.dtv = dt
self.dtf = 0.5 * dt * ftm2v self.dtf = 0.5 * dt * ftm2v
def initial_integrate(self, vflag): def initial_integrate(self, vflag):
nlocal = self.lmp.extract_global("nlocal", 0) mass = self.lmp.numpy.extract_atom("mass")
mass = self.lmp.numpy.extract_atom_darray("mass", self.ntypes+1) atype = self.lmp.numpy.extract_atom("type")
atype = self.lmp.numpy.extract_atom_iarray("type", nlocal) x = self.lmp.numpy.extract_atom("x")
x = self.lmp.numpy.extract_atom_darray("x", nlocal, dim=3) v = self.lmp.numpy.extract_atom("v")
v = self.lmp.numpy.extract_atom_darray("v", nlocal, dim=3) f = self.lmp.numpy.extract_atom("f")
f = self.lmp.numpy.extract_atom_darray("f", nlocal, dim=3)
for i in range(x.shape[0]): for i in range(x.shape[0]):
dtfm = self.dtf / mass[int(atype[i])] dtfm = self.dtf / mass[int(atype[i])]
@ -59,11 +58,10 @@ class NVE(LAMMPSFixMove):
x[i,:] += self.dtv * v[i,:] x[i,:] += self.dtv * v[i,:]
def final_integrate(self): def final_integrate(self):
nlocal = self.lmp.extract_global("nlocal", 0) mass = self.lmp.numpy.extract_atom("mass")
mass = self.lmp.numpy.extract_atom_darray("mass", self.ntypes+1) atype = self.lmp.numpy.extract_atom("type")
atype = self.lmp.numpy.extract_atom_iarray("type", nlocal) v = self.lmp.numpy.extract_atom("v")
v = self.lmp.numpy.extract_atom_darray("v", nlocal, dim=3) f = self.lmp.numpy.extract_atom("f")
f = self.lmp.numpy.extract_atom_darray("f", nlocal, dim=3)
for i in range(v.shape[0]): for i in range(v.shape[0]):
dtfm = self.dtf / mass[int(atype[i])] dtfm = self.dtf / mass[int(atype[i])]
@ -77,19 +75,19 @@ class NVE_Opt(LAMMPSFixMove):
assert(self.group_name == "all") assert(self.group_name == "all")
def init(self): def init(self):
dt = self.lmp.extract_global("dt", 1) dt = self.lmp.extract_global("dt")
ftm2v = self.lmp.extract_global("ftm2v", 1) ftm2v = self.lmp.extract_global("ftm2v")
self.ntypes = self.lmp.extract_global("ntypes", 0) self.ntypes = self.lmp.extract_global("ntypes")
self.dtv = dt self.dtv = dt
self.dtf = 0.5 * dt * ftm2v self.dtf = 0.5 * dt * ftm2v
self.mass = self.lmp.numpy.extract_atom_darray("mass", self.ntypes+1) self.mass = self.lmp.numpy.extract_atom("mass")
def initial_integrate(self, vflag): def initial_integrate(self, vflag):
nlocal = self.lmp.extract_global("nlocal", 0) nlocal = self.lmp.extract_global("nlocal")
atype = self.lmp.numpy.extract_atom_iarray("type", nlocal) atype = self.lmp.numpy.extract_atom("type")
x = self.lmp.numpy.extract_atom_darray("x", nlocal, dim=3) x = self.lmp.numpy.extract_atom("x")
v = self.lmp.numpy.extract_atom_darray("v", nlocal, dim=3) v = self.lmp.numpy.extract_atom("v")
f = self.lmp.numpy.extract_atom_darray("f", nlocal, dim=3) f = self.lmp.numpy.extract_atom("f")
dtf = self.dtf dtf = self.dtf
dtv = self.dtv dtv = self.dtv
mass = self.mass mass = self.mass
@ -102,13 +100,12 @@ class NVE_Opt(LAMMPSFixMove):
x[:,d] += dtv * v[:,d] x[:,d] += dtv * v[:,d]
def final_integrate(self): def final_integrate(self):
nlocal = self.lmp.extract_global("nlocal", 0) nlocal = self.lmp.extract_global("nlocal")
mass = self.lmp.numpy.extract_atom_darray("mass", self.ntypes+1) mass = self.lmp.numpy.extract_atom("mass")
atype = self.lmp.numpy.extract_atom_iarray("type", nlocal) atype = self.lmp.numpy.extract_atom("type")
v = self.lmp.numpy.extract_atom_darray("v", nlocal, dim=3) v = self.lmp.numpy.extract_atom("v")
f = self.lmp.numpy.extract_atom_darray("f", nlocal, dim=3) f = self.lmp.numpy.extract_atom("f")
dtf = self.dtf dtf = self.dtf
dtv = self.dtv
mass = self.mass mass = self.mass
dtfm = dtf / np.take(mass, atype) dtfm = dtf / np.take(mass, atype)

View File

@ -16,7 +16,7 @@ if len(argv) != 1:
print("Syntax: demo.py") print("Syntax: demo.py")
sys.exit() sys.exit()
from lammps import lammps from lammps import lammps, LAMMPS_INT, LMP_STYLE_GLOBAL, LMP_VAR_EQUAL, LMP_VAR_ATOM
lmp = lammps() lmp = lammps()
# test out various library functions after running in.demo # test out various library functions after running in.demo
@ -25,18 +25,18 @@ lmp.file("in.demo")
print("\nPython output:") print("\nPython output:")
natoms = lmp.extract_global("natoms",0) natoms = lmp.extract_global("natoms")
mass = lmp.extract_atom("mass",2) mass = lmp.extract_atom("mass")
x = lmp.extract_atom("x",3) x = lmp.extract_atom("x")
print("Natoms, mass, x[0][0] coord =",natoms,mass[1],x[0][0]) print("Natoms, mass, x[0][0] coord =",natoms,mass[1],x[0][0])
temp = lmp.extract_compute("thermo_temp",0,0) temp = lmp.extract_compute("thermo_temp", LMP_STYLE_GLOBAL, LAMMPS_INT)
print("Temperature from compute =",temp) print("Temperature from compute =",temp)
eng = lmp.extract_variable("eng",None,0) eng = lmp.extract_variable("eng",None, LMP_VAR_EQUAL)
print("Energy from equal-style variable =",eng) print("Energy from equal-style variable =",eng)
vy = lmp.extract_variable("vy","all",1) vy = lmp.extract_variable("vy","all", LMP_VAR_ATOM)
print("Velocity component from atom-style variable =",vy[1]) print("Velocity component from atom-style variable =",vy[1])
vol = lmp.get_thermo("vol") vol = lmp.get_thermo("vol")

View File

@ -27,7 +27,7 @@ if len(argv) != 2:
infile = sys.argv[1] infile = sys.argv[1]
from lammps import lammps from lammps import lammps, LAMMPS_INT, LMP_STYLE_GLOBAL, LMP_VAR_EQUAL
lmp = lammps() lmp = lammps()
# run infile one line at a time # run infile one line at a time
@ -42,14 +42,14 @@ lmp.command("variable e equal pe")
lmp.command("run 0") lmp.command("run 0")
natoms = lmp.extract_global("natoms",0) natoms = lmp.extract_global("natoms")
emin = lmp.extract_compute("thermo_pe",0,0) / natoms emin = lmp.extract_compute("thermo_pe",LMP_STYLE_GLOBAL,LAMMPS_INT) / natoms
lmp.command("variable emin equal $e") lmp.command("variable emin equal $e")
# disorder the system # disorder the system
# estart = initial energy # estart = initial energy
x = lmp.extract_atom("x",3) x = lmp.extract_atom("x")
for i in range(natoms): for i in range(natoms):
x[i][0] += deltaperturb * (2*random.random()-1) x[i][0] += deltaperturb * (2*random.random()-1)
@ -58,10 +58,10 @@ for i in range(natoms):
lmp.command("variable elast equal $e") lmp.command("variable elast equal $e")
lmp.command("thermo_style custom step v_emin v_elast pe") lmp.command("thermo_style custom step v_emin v_elast pe")
lmp.command("run 0") lmp.command("run 0")
x = lmp.extract_atom("x",3) x = lmp.extract_atom("x")
lmp.command("variable elast equal $e") lmp.command("variable elast equal $e")
estart = lmp.extract_compute("thermo_pe",0,0) / natoms estart = lmp.extract_compute("thermo_pe", LMP_STYLE_GLOBAL, LAMMPS_INT) / natoms
# loop over Monte Carlo moves # loop over Monte Carlo moves
# extract x after every run, in case reneighboring changed ptr in LAMMPS # extract x after every run, in case reneighboring changed ptr in LAMMPS
@ -78,8 +78,8 @@ for i in range(nloop):
x[iatom][1] += deltamove * (2*random.random()-1) x[iatom][1] += deltamove * (2*random.random()-1)
lmp.command("run 1 pre no post no") lmp.command("run 1 pre no post no")
x = lmp.extract_atom("x",3) x = lmp.extract_atom("x")
e = lmp.extract_compute("thermo_pe",0,0) / natoms e = lmp.extract_compute("thermo_pe", LMP_STYLE_GLOBAL, LAMMPS_INT) / natoms
if e <= elast: if e <= elast:
elast = e elast = e
@ -96,10 +96,10 @@ for i in range(nloop):
# final energy and stats # final energy and stats
lmp.command("variable nbuild equal nbuild") lmp.command("variable nbuild equal nbuild")
nbuild = lmp.extract_variable("nbuild",None,0) nbuild = lmp.extract_variable("nbuild", None, LMP_VAR_EQUAL)
lmp.command("run 0") lmp.command("run 0")
estop = lmp.extract_compute("thermo_pe",0,0) / natoms estop = lmp.extract_compute("thermo_pe", LMP_STYLE_GLOBAL, LAMMPS_INT) / natoms
print("MC stats:") print("MC stats:")
print(" starting energy =",estart) print(" starting energy =",estart)

View File

@ -61,7 +61,7 @@ lmp.command("run 1");
# extract force on single atom two different ways # extract force on single atom two different ways
f = lmp.extract_atom("f",3) f = lmp.extract_atom("f")
print("Force on 1 atom via extract_atom: ",f[0][0]) print("Force on 1 atom via extract_atom: ",f[0][0])
fx = lmp.extract_variable("fx","all",1) fx = lmp.extract_variable("fx","all",1)

View File

@ -57,7 +57,7 @@ if color == 0:
lmp.scatter_atoms("x",1,3,x) lmp.scatter_atoms("x",1,3,x)
lmp.command("run 1"); lmp.command("run 1");
f = lmp.extract_atom("f",3) f = lmp.extract_atom("f")
print("Force on 1 atom via extract_atom: ",f[0][0]) print("Force on 1 atom via extract_atom: ",f[0][0])
fx = lmp.extract_variable("fx","all",1) fx = lmp.extract_variable("fx","all",1)

View File

@ -19,6 +19,7 @@ from __future__ import print_function
# imports for simple LAMMPS python wrapper module "lammps" # imports for simple LAMMPS python wrapper module "lammps"
import sys,traceback,types import sys,traceback,types
import warnings
from ctypes import * from ctypes import *
from os.path import dirname,abspath,join from os.path import dirname,abspath,join
from inspect import getsourcefile from inspect import getsourcefile
@ -33,12 +34,13 @@ import sys
# various symbolic constants to be used # various symbolic constants to be used
# in certain calls to select data formats # in certain calls to select data formats
LAMMPS_AUTODETECT = None
LAMMPS_INT = 0 LAMMPS_INT = 0
LAMMPS_INT2D = 1 LAMMPS_INT_2D = 1
LAMMPS_DOUBLE = 2 LAMMPS_DOUBLE = 2
LAMMPS_DOUBLE2D = 3 LAMMPS_DOUBLE_2D = 3
LAMMPS_BIGINT = 4 LAMMPS_INT64 = 4
LAMMPS_TAGINT = 5 LAMMPS_INT64_2D = 5
LAMMPS_STRING = 6 LAMMPS_STRING = 6
# these must be kept in sync with the enums in library.h # these must be kept in sync with the enums in library.h
@ -313,6 +315,8 @@ class lammps(object):
self.lib.lammps_get_last_error_message.restype = c_int self.lib.lammps_get_last_error_message.restype = c_int
self.lib.lammps_extract_global.argtypes = [c_void_p, c_char_p] 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_compute.argtypes = [c_void_p, c_char_p, c_int, c_int] self.lib.lammps_extract_compute.argtypes = [c_void_p, c_char_p, c_int, c_int]
self.lib.lammps_get_thermo.argtypes = [c_void_p, c_char_p] self.lib.lammps_get_thermo.argtypes = [c_void_p, c_char_p]
@ -337,6 +341,8 @@ class lammps(object):
self.lib.lammps_decode_image_flags.argtypes = [self.c_imageint, POINTER(c_int*3)] self.lib.lammps_decode_image_flags.argtypes = [self.c_imageint, POINTER(c_int*3)]
self.lib.lammps_extract_atom.argtypes = [c_void_p, c_char_p] 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_fix.argtypes = [c_void_p, c_char_p, c_int, c_int, c_int, c_int] self.lib.lammps_extract_fix.argtypes = [c_void_p, c_char_p, c_int, c_int, c_int, c_int]
@ -473,7 +479,73 @@ class lammps(object):
return np.int64 return np.int64
return np.intc return np.intc
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 :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.
For that purpose the :py:mod:`lammps` module contains the constants
``LAMMPS_INT``, ``LAMMPS_INT_2D``, ``LAMMPS_DOUBLE``,
``LAMMPS_DOUBLE_2D``, ``LAMMPS_INT64``, ``LAMMPS_INT64_2D``, and
``LAMMPS_STRING``.
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>`.
:param name: name of the property
:type name: string
:param dtype: type of the returned data (see :ref:`py_data_constants`)
:type dtype: int, optional
:param nelem: number of elements in array
:type nelem: int, optional
:param dim: dimension of each element
:type dim: int, optional
:return: requested data as NumPy array with direct access to C data
:rtype: numpy.array
"""
if dtype == LAMMPS_AUTODETECT:
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")
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", "angmom", "torque", "csforce", "vforce"):
dim = 3
else:
dim = 2
else:
dim = 1
raw_ptr = self.lmp.extract_atom(name, dtype)
if dtype in (LAMMPS_DOUBLE, LAMMPS_DOUBLE_2D):
return self.darray(raw_ptr, nelem, dim)
elif dtype in (LAMMPS_INT, LAMMPS_INT_2D):
return self.iarray(c_int32, raw_ptr, nelem, dim)
elif dtype in (LAMMPS_INT64, LAMMPS_INT64_2D):
return self.iarray(c_int64, raw_ptr, nelem, dim)
return raw_ptr
def extract_atom_iarray(self, name, nelem, dim=1): def extract_atom_iarray(self, name, nelem, dim=1):
warnings.warn("deprecated, use extract_atom instead", DeprecationWarning)
if name in ['id', 'molecule']: if name in ['id', 'molecule']:
c_int_type = self.lmp.c_tagint c_int_type = self.lmp.c_tagint
elif name in ['image']: elif name in ['image']:
@ -484,15 +556,17 @@ class lammps(object):
if dim == 1: if dim == 1:
raw_ptr = self.lmp.extract_atom(name, LAMMPS_INT) raw_ptr = self.lmp.extract_atom(name, LAMMPS_INT)
else: else:
raw_ptr = self.lmp.extract_atom(name, LAMMPS_INT2D) raw_ptr = self.lmp.extract_atom(name, LAMMPS_INT_2D)
return self.iarray(c_int_type, raw_ptr, nelem, dim) return self.iarray(c_int_type, raw_ptr, nelem, dim)
def extract_atom_darray(self, name, nelem, dim=1): def extract_atom_darray(self, name, nelem, dim=1):
warnings.warn("deprecated, use extract_atom instead", DeprecationWarning)
if dim == 1: if dim == 1:
raw_ptr = self.lmp.extract_atom(name, LAMMPS_DOUBLE) raw_ptr = self.lmp.extract_atom(name, LAMMPS_DOUBLE)
else: else:
raw_ptr = self.lmp.extract_atom(name, LAMMPS_DOUBLE2D) raw_ptr = self.lmp.extract_atom(name, LAMMPS_DOUBLE_2D)
return self.darray(raw_ptr, nelem, dim) return self.darray(raw_ptr, nelem, dim)
@ -705,9 +779,9 @@ class lammps(object):
underlying :cpp:func:`lammps_get_natoms` function returning a double. underlying :cpp:func:`lammps_get_natoms` function returning a double.
:return: number of atoms :return: number of atoms
:rtype: float :rtype: int
""" """
return self.lib.lammps_get_natoms(self.lmp) return int(self.lib.lammps_get_natoms(self.lmp))
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
@ -801,10 +875,35 @@ class lammps(object):
else: return None else: return None
return int(self.lib.lammps_extract_setting(self.lmp,name)) return int(self.lib.lammps_extract_setting(self.lmp,name))
# -------------------------------------------------------------------------
# extract global info datatype
def extract_global_datatype(self, name):
"""Retrieve global property datatype from LAMMPS
This is a wrapper around the :cpp:func:`lammps_extract_global_datatype`
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 a positive integer value that
corresponds to one of the constants define in the :py:mod:`lammps` module:
``LAMMPS_INT``, ``LAMMPS_INT_2D``, ``LAMMPS_DOUBLE``, ``LAMMPS_DOUBLE_2D``,
``LAMMPS_INT64``, ``LAMMPS_INT64_2D``, and ``LAMMPS_STRING``. These values
are equivalent to the ones defined in :cpp:enum:`_LMP_DATATYPE_CONST`.
:param name: name of the property
:type name: string
:return: datatype of global property
:rtype: int
"""
if name: name = name.encode()
else: return None
return self.lib.lammps_extract_global_datatype(self.lmp, name)
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
# extract global info # extract global info
def extract_global(self, name, type): def extract_global(self, name, dtype=LAMMPS_AUTODETECT):
"""Query LAMMPS about global settings of different types. """Query LAMMPS about global settings of different types.
This is a wrapper around the :cpp:func:`lammps_extract_global` This is a wrapper around the :cpp:func:`lammps_extract_global`
@ -814,55 +913,87 @@ class lammps(object):
of values. The :cpp:func:`lammps_extract_global` documentation of values. The :cpp:func:`lammps_extract_global` documentation
includes a list of the supported keywords and their data types. includes a list of the supported keywords and their data types.
Since Python needs to know the data type to be able to interpret Since Python needs to know the data type to be able to interpret
the result, the type has to be provided as an argument. For the result, by default, this function will try to auto-detect the data type
that purpose the :py:mod:`lammps` module contains the constants by asking the library. You can also force a specific data type. For that
``LAMMPS_INT``, ``LAMMPS_DOUBLE``, ``LAMMPS_BIGINT``, purpose the :py:mod:`lammps` module contains the constants ``LAMMPS_INT``,
``LAMMPS_TAGINT``, and ``LAMMPS_STRING``. ``LAMMPS_DOUBLE``, ``LAMMPS_INT64``, and ``LAMMPS_STRING``. These values
This function returns ``None`` if either the keyword is not are equivalent to the ones defined in :cpp:enum:`_LMP_DATATYPE_CONST`.
recognized, or an invalid data type constant is used. This function returns ``None`` if either the keyword is not recognized,
or an invalid data type constant is used.
:param name: name of the setting :param name: name of the property
:type name: string :type name: string
:param type: type of the returned data :param dtype: data type of the returned data (see :ref:`py_data_constants`)
:type type: int :type dtype: int, optional
:return: value of the setting :return: value of the property or None
:rtype: integer or double or string or None :rtype: int, float, or NoneType
"""
if dtype == LAMMPS_AUTODETECT:
dtype = self.extract_global_datatype(name)
if name: name = name.encode()
else: return None
if dtype == LAMMPS_INT:
self.lib.lammps_extract_global.restype = POINTER(c_int32)
target_type = int
elif dtype == LAMMPS_INT64:
self.lib.lammps_extract_global.restype = POINTER(c_int64)
target_type = int
elif dtype == LAMMPS_DOUBLE:
self.lib.lammps_extract_global.restype = POINTER(c_double)
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])
return None
# -------------------------------------------------------------------------
# extract per-atom info datatype
def extract_atom_datatype(self, name):
"""Retrieve per-atom property datatype from LAMMPS
This is a wrapper around the :cpp:func:`lammps_extract_atom_datatype`
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 that
corresponds to one of the constants define in the :py:mod:`lammps` module:
``LAMMPS_INT``, ``LAMMPS_INT_2D``, ``LAMMPS_DOUBLE``, ``LAMMPS_DOUBLE_2D``,
``LAMMPS_INT64``, ``LAMMPS_INT64_2D``, and ``LAMMPS_STRING``. These values
are equivalent to the ones defined in :cpp:enum:`_LMP_DATATYPE_CONST`.
:param name: name of the property
:type name: string
:return: data type of per-atom property (see :ref:`py_data_constants`)
:rtype: int
""" """
if name: name = name.encode() if name: name = name.encode()
else: return None else: return None
if type == LAMMPS_INT: return self.lib.lammps_extract_atom_datatype(self.lmp, name)
self.lib.lammps_extract_global.restype = POINTER(c_int)
elif type == LAMMPS_DOUBLE:
self.lib.lammps_extract_global.restype = POINTER(c_double)
elif type == LAMMPS_BIGINT:
self.lib.lammps_extract_global.restype = POINTER(self.c_bigint)
elif type == LAMMPS_TAGINT:
self.lib.lammps_extract_global.restype = POINTER(self.c_tagint)
elif type == LAMMPS_STRING:
self.lib.lammps_extract_global.restype = c_char_p
ptr = self.lib.lammps_extract_global(self.lmp,name)
return str(ptr,'ascii')
else: return None
ptr = self.lib.lammps_extract_global(self.lmp,name)
if ptr: return ptr[0]
else: return None
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
# extract per-atom info # extract per-atom info
# NOTE: need to insure are converting to/from correct Python type
# e.g. for Python list or NumPy or ctypes
def extract_atom(self,name,type): def extract_atom(self, name, dtype=LAMMPS_AUTODETECT):
"""Retrieve per-atom properties from LAMMPS """Retrieve per-atom properties from LAMMPS
This is a wrapper around the :cpp:func:`lammps_extract_atom` This is a wrapper around the :cpp:func:`lammps_extract_atom`
function of the C-library interface. Its documentation includes a function of the C-library interface. Its documentation includes a
list of the supported keywords and their data types. list of the supported keywords and their data types.
Since Python needs to know the data type to be able to interpret Since Python needs to know the data type to be able to interpret
the result, the type has to be provided as an argument. For 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 purpose the :py:mod:`lammps` module contains the constants that purpose the :py:mod:`lammps` module contains the constants
``LAMMPS_INT``, ``LAMMPS_INT2D``, ``LAMMPS_DOUBLE``, ``LAMMPS_INT``, ``LAMMPS_INT_2D``, ``LAMMPS_DOUBLE``, ``LAMMPS_DOUBLE_2D``,
and ``LAMMPS_DOUBLE2D``. ``LAMMPS_INT64``, ``LAMMPS_INT64_2D``, and ``LAMMPS_STRING``. These values
are equivalent to the ones defined in :cpp:enum:`_LMP_DATATYPE_CONST`.
This function returns ``None`` if either the keyword is not This function returns ``None`` if either the keyword is not
recognized, or an invalid data type constant is used. recognized, or an invalid data type constant is used.
@ -875,27 +1006,36 @@ class lammps(object):
atoms. In some cases, this depends on a LAMMPS setting, see atoms. In some cases, this depends on a LAMMPS setting, see
for example :doc:`comm_modify vel yes <comm_modify>`. for example :doc:`comm_modify vel yes <comm_modify>`.
:param name: name of the setting :param name: name of the property
:type name: string :type name: string
:param type: type of the returned data :param dtype: data type of the returned data (see :ref:`py_data_constants`)
:type type: int :type dtype: int, optional
:return: requested data :return: requested data or ``None``
:rtype: pointer to integer or double or None :rtype: ctypes.POINTER(ctypes.c_int32), ctypes.POINTER(ctypes.POINTER(ctypes.c_int32)),
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
""" """
ntypes = int(self.extract_setting('ntypes')) if dtype == LAMMPS_AUTODETECT:
nmax = int(self.extract_setting('nmax')) dtype = self.extract_atom_datatype(name)
if name: name = name.encode() if name: name = name.encode()
else: return None else: return None
if type == LAMMPS_INT:
self.lib.lammps_extract_atom.restype = POINTER(c_int) if dtype == LAMMPS_INT:
elif type == LAMMPS_INT2D: self.lib.lammps_extract_atom.restype = POINTER(c_int32)
self.lib.lammps_extract_atom.restype = POINTER(POINTER(c_int)) elif dtype == LAMMPS_INT_2D:
elif type == LAMMPS_DOUBLE: self.lib.lammps_extract_atom.restype = POINTER(POINTER(c_int32))
elif dtype == LAMMPS_DOUBLE:
self.lib.lammps_extract_atom.restype = POINTER(c_double) self.lib.lammps_extract_atom.restype = POINTER(c_double)
elif type == LAMMPS_DOUBLE2D: elif dtype == LAMMPS_DOUBLE_2D:
self.lib.lammps_extract_atom.restype = POINTER(POINTER(c_double)) self.lib.lammps_extract_atom.restype = POINTER(POINTER(c_double))
elif dtype == LAMMPS_INT64:
self.lib.lammps_extract_atom.restype = POINTER(c_int64)
elif dtype == LAMMPS_INT64_2D:
self.lib.lammps_extract_atom.restype = POINTER(POINTER(c_int64))
else: return None else: return None
ptr = self.lib.lammps_extract_atom(self.lmp,name) ptr = self.lib.lammps_extract_atom(self.lmp, name)
if ptr: return ptr if ptr: return ptr
else: return None else: return None
@ -1140,7 +1280,7 @@ class lammps(object):
def gather_atoms(self,name,type,count): def gather_atoms(self,name,type,count):
if name: name = name.encode() if name: name = name.encode()
natoms = self.lib.lammps_get_natoms(self.lmp) natoms = self.get_natoms()
if type == 0: if type == 0:
data = ((count*natoms)*c_int)() data = ((count*natoms)*c_int)()
self.lib.lammps_gather_atoms(self.lmp,name,type,count,data) self.lib.lammps_gather_atoms(self.lmp,name,type,count,data)
@ -1154,7 +1294,7 @@ class lammps(object):
def gather_atoms_concat(self,name,type,count): def gather_atoms_concat(self,name,type,count):
if name: name = name.encode() if name: name = name.encode()
natoms = self.lib.lammps_get_natoms(self.lmp) natoms = self.get_natoms()
if type == 0: if type == 0:
data = ((count*natoms)*c_int)() data = ((count*natoms)*c_int)()
self.lib.lammps_gather_atoms_concat(self.lmp,name,type,count,data) self.lib.lammps_gather_atoms_concat(self.lmp,name,type,count,data)
@ -1206,7 +1346,7 @@ class lammps(object):
# e.g. for Python list or NumPy or ctypes # e.g. for Python list or NumPy or ctypes
def gather(self,name,type,count): def gather(self,name,type,count):
if name: name = name.encode() if name: name = name.encode()
natoms = self.lib.lammps_get_natoms(self.lmp) natoms = self.get_natoms()
if type == 0: if type == 0:
data = ((count*natoms)*c_int)() data = ((count*natoms)*c_int)()
self.lib.lammps_gather(self.lmp,name,type,count,data) self.lib.lammps_gather(self.lmp,name,type,count,data)
@ -1218,7 +1358,7 @@ class lammps(object):
def gather_concat(self,name,type,count): def gather_concat(self,name,type,count):
if name: name = name.encode() if name: name = name.encode()
natoms = self.lib.lammps_get_natoms(self.lmp) natoms = self.get_natoms()
if type == 0: if type == 0:
data = ((count*natoms)*c_int)() data = ((count*natoms)*c_int)()
self.lib.lammps_gather_concat(self.lmp,name,type,count,data) self.lib.lammps_gather_concat(self.lmp,name,type,count,data)
@ -1525,8 +1665,8 @@ class lammps(object):
def available_styles(self, category): def available_styles(self, category):
"""Returns a list of styles available for a given category """Returns a list of styles available for a given category
This is a wrapper around the functions :cpp:func:`lammps_style_count` This is a wrapper around the functions :cpp:func:`lammps_style_count()`
and :cpp:func`lammps_style_name` of the library interface. and :cpp:func:`lammps_style_name()` of the library interface.
:param category: name of category :param category: name of category
:type category: string :type category: string
@ -1723,8 +1863,8 @@ class OutputCapture(object):
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
class Variable(object): class Variable(object):
def __init__(self, lammps_wrapper_instance, name, style, definition): def __init__(self, pylammps_instance, name, style, definition):
self.wrapper = lammps_wrapper_instance self._pylmp = pylammps_instance
self.name = name self.name = name
self.style = style self.style = style
self.definition = definition.split() self.definition = definition.split()
@ -1732,9 +1872,9 @@ class Variable(object):
@property @property
def value(self): def value(self):
if self.style == 'atom': if self.style == 'atom':
return list(self.wrapper.lmp.extract_variable(self.name, "all", 1)) return list(self._pylmp.lmp.extract_variable(self.name, "all", 1))
else: else:
value = self.wrapper.lmp_print('"${%s}"' % self.name).strip() value = self._pylmp.lmp_print('"${%s}"' % self.name).strip()
try: try:
return float(value) return float(value)
except ValueError: except ValueError:
@ -1743,103 +1883,136 @@ class Variable(object):
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
class AtomList(object): class AtomList(object):
def __init__(self, lammps_wrapper_instance): """
self.lmp = lammps_wrapper_instance A dynamic list of atoms that returns either an Atom or Atom2D instance for
self.natoms = self.lmp.system.natoms each atom. Instances are only allocated when accessed.
self.dimensions = self.lmp.system.dimensions
:ivar natoms: total number of atoms
:ivar dimensions: number of dimensions in system
"""
def __init__(self, pylammps_instance):
self._pylmp = pylammps_instance
self.natoms = self._pylmp.system.natoms
self.dimensions = self._pylmp.system.dimensions
self._loaded = {}
def __getitem__(self, index): def __getitem__(self, index):
if self.dimensions == 2: """
return Atom2D(self.lmp, index + 1) Return Atom with given local index
return Atom(self.lmp, index + 1)
:param index: Local index of atom
:type index: int
:rtype: Atom or Atom2D
"""
if index not in self._loaded:
if self.dimensions == 2:
atom = Atom2D(self._pylmp, index + 1)
else:
atom = Atom(self._pylmp, index + 1)
self._loaded[index] = atom
return self._loaded[index]
def __len__(self):
return self.natoms
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
class Atom(object): class Atom(object):
def __init__(self, lammps_wrapper_instance, index): """
self.lmp = lammps_wrapper_instance A wrapper class then represents a single atom inside of LAMMPS
It provides access to properties of the atom and allows you to change some of them.
"""
def __init__(self, pylammps_instance, index):
self._pylmp = pylammps_instance
self.index = index self.index = index
@property @property
def id(self): def id(self):
return int(self.lmp.eval("id[%d]" % self.index)) return int(self._pylmp.eval("id[%d]" % self.index))
@property @property
def type(self): def type(self):
return int(self.lmp.eval("type[%d]" % self.index)) return int(self._pylmp.eval("type[%d]" % self.index))
@property @property
def mol(self): def mol(self):
return self.lmp.eval("mol[%d]" % self.index) return self._pylmp.eval("mol[%d]" % self.index)
@property @property
def mass(self): def mass(self):
return self.lmp.eval("mass[%d]" % self.index) return self._pylmp.eval("mass[%d]" % self.index)
@property @property
def position(self): def position(self):
return (self.lmp.eval("x[%d]" % self.index), return (self._pylmp.eval("x[%d]" % self.index),
self.lmp.eval("y[%d]" % self.index), self._pylmp.eval("y[%d]" % self.index),
self.lmp.eval("z[%d]" % self.index)) self._pylmp.eval("z[%d]" % self.index))
@position.setter @position.setter
def position(self, value): def position(self, value):
self.lmp.set("atom", self.index, "x", value[0]) self._pylmp.set("atom", self.index, "x", value[0])
self.lmp.set("atom", self.index, "y", value[1]) self._pylmp.set("atom", self.index, "y", value[1])
self.lmp.set("atom", self.index, "z", value[2]) self._pylmp.set("atom", self.index, "z", value[2])
@property @property
def velocity(self): def velocity(self):
return (self.lmp.eval("vx[%d]" % self.index), return (self._pylmp.eval("vx[%d]" % self.index),
self.lmp.eval("vy[%d]" % self.index), self._pylmp.eval("vy[%d]" % self.index),
self.lmp.eval("vz[%d]" % self.index)) self._pylmp.eval("vz[%d]" % self.index))
@velocity.setter @velocity.setter
def velocity(self, value): def velocity(self, value):
self.lmp.set("atom", self.index, "vx", value[0]) self._pylmp.set("atom", self.index, "vx", value[0])
self.lmp.set("atom", self.index, "vy", value[1]) self._pylmp.set("atom", self.index, "vy", value[1])
self.lmp.set("atom", self.index, "vz", value[2]) self._pylmp.set("atom", self.index, "vz", value[2])
@property @property
def force(self): def force(self):
return (self.lmp.eval("fx[%d]" % self.index), return (self._pylmp.eval("fx[%d]" % self.index),
self.lmp.eval("fy[%d]" % self.index), self._pylmp.eval("fy[%d]" % self.index),
self.lmp.eval("fz[%d]" % self.index)) self._pylmp.eval("fz[%d]" % self.index))
@property @property
def charge(self): def charge(self):
return self.lmp.eval("q[%d]" % self.index) return self._pylmp.eval("q[%d]" % self.index)
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
class Atom2D(Atom): class Atom2D(Atom):
def __init__(self, lammps_wrapper_instance, index): """
super(Atom2D, self).__init__(lammps_wrapper_instance, index) A wrapper class then represents a single 2D atom inside of LAMMPS
It provides access to properties of the atom and allows you to change some of them.
"""
def __init__(self, pylammps_instance, index):
super(Atom2D, self).__init__(pylammps_instance, index)
@property @property
def position(self): def position(self):
return (self.lmp.eval("x[%d]" % self.index), return (self._pylmp.eval("x[%d]" % self.index),
self.lmp.eval("y[%d]" % self.index)) self._pylmp.eval("y[%d]" % self.index))
@position.setter @position.setter
def position(self, value): def position(self, value):
self.lmp.set("atom", self.index, "x", value[0]) self._pylmp.set("atom", self.index, "x", value[0])
self.lmp.set("atom", self.index, "y", value[1]) self._pylmp.set("atom", self.index, "y", value[1])
@property @property
def velocity(self): def velocity(self):
return (self.lmp.eval("vx[%d]" % self.index), return (self._pylmp.eval("vx[%d]" % self.index),
self.lmp.eval("vy[%d]" % self.index)) self._pylmp.eval("vy[%d]" % self.index))
@velocity.setter @velocity.setter
def velocity(self, value): def velocity(self, value):
self.lmp.set("atom", self.index, "vx", value[0]) self._pylmp.set("atom", self.index, "vx", value[0])
self.lmp.set("atom", self.index, "vy", value[1]) self._pylmp.set("atom", self.index, "vy", value[1])
@property @property
def force(self): def force(self):
return (self.lmp.eval("fx[%d]" % self.index), return (self._pylmp.eval("fx[%d]" % self.index),
self.lmp.eval("fy[%d]" % self.index)) self._pylmp.eval("fy[%d]" % self.index))
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
@ -1919,11 +2092,41 @@ def get_thermo_data(output):
class PyLammps(object): class PyLammps(object):
""" """
More Python-like wrapper for LAMMPS (e.g., for IPython) This is a Python wrapper class around the lower-level
See examples/ipython for usage :py:class:`lammps` class, exposing a more Python-like,
object-oriented interface for prototyping system inside of IPython and
Jupyter notebooks.
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
lower-level interface. The original interface can still be accessed via
:py:attr:`PyLammps.lmp`.
:param name: "machine" name of the shared LAMMPS library ("mpi" loads ``liblammps_mpi.so``, "" loads ``liblammps.so``)
:type name: string
:param cmdargs: list of command line arguments to be passed to the :cpp:func:`lammps_open` function. The executable name is automatically added.
:type cmdargs: list
:param ptr: pointer to a LAMMPS C++ class instance when called from an embedded Python interpreter. None means load symbols from shared library.
:type ptr: pointer
:param comm: MPI communicator (as provided by `mpi4py <mpi4py_docs_>`_). ``None`` means use ``MPI_COMM_WORLD`` implicitly.
:type comm: MPI_Comm
:ivar lmp: instance of original LAMMPS Python interface
:vartype lmp: :py:class:`lammps`
:ivar runs: list of completed runs, each storing the thermo output
:vartype run: list
""" """
def __init__(self,name="",cmdargs=None,ptr=None,comm=None): def __init__(self, name="", cmdargs=None, ptr=None, comm=None):
self.has_echo = False
if cmdargs:
if '-echo' in cmdargs:
idx = cmdargs.index('-echo')
# ensures that echo line is ignored during output capture
self.has_echo = idx+1 < len(cmdargs) and cmdargs[idx+1] in ('screen', 'both')
if ptr: if ptr:
if isinstance(ptr,PyLammps): if isinstance(ptr,PyLammps):
self.lmp = ptr.lmp self.lmp = ptr.lmp
@ -1942,26 +2145,65 @@ class PyLammps(object):
self.lmp = None self.lmp = None
def close(self): def close(self):
"""Explicitly delete a LAMMPS instance
This is a wrapper around the :py:meth:`lammps.close` of the Python interface.
"""
if self.lmp: self.lmp.close() if self.lmp: self.lmp.close()
self.lmp = None self.lmp = None
def version(self): def version(self):
"""Return a numerical representation of the LAMMPS version in use.
This is a wrapper around the :py:meth:`lammps.version` function of the Python interface.
:return: version number
:rtype: int
"""
return self.lmp.version() return self.lmp.version()
def file(self,file): def file(self, file):
"""Read LAMMPS commands from a file.
This is a wrapper around the :py:meth:`lammps.file` function of the Python interface.
:param path: Name of the file/path with LAMMPS commands
:type path: string
"""
self.lmp.file(file) self.lmp.file(file)
def write_script(self,filename): def write_script(self, filepath):
""" Write LAMMPS script file containing all commands executed up until now """ """
with open(filename, "w") as f: Write LAMMPS script file containing all commands executed up until now
for cmd in self._cmd_history:
f.write("%s\n" % cmd)
def command(self,cmd): :param filepath: path to script file that should be written
:type filepath: string
"""
with open(filepath, "w") as f:
for cmd in self._cmd_history:
print(cmd, file=f)
def command(self, cmd):
"""
Execute LAMMPS command
All commands executed will be stored in a command history which can be
written to a file using :py:meth:`PyLammps.write_script()`
:param cmd: command string that should be executed
:type: cmd: string
"""
self.lmp.command(cmd) self.lmp.command(cmd)
self._cmd_history.append(cmd) self._cmd_history.append(cmd)
def run(self, *args, **kwargs): def run(self, *args, **kwargs):
"""
Execute LAMMPS run command with given arguments
All thermo output during the run is captured and saved as new entry in
:py:attr:`PyLammps.runs`. The latest run can be retrieved by
:py:attr:`PyLammps.last_run`.
"""
output = self.__getattr__('run')(*args, **kwargs) output = self.__getattr__('run')(*args, **kwargs)
if(self.has_mpi4py): if(self.has_mpi4py):
@ -1972,48 +2214,102 @@ class PyLammps(object):
@property @property
def last_run(self): def last_run(self):
"""
Return data produced of last completed run command
:getter: Returns an object containing information about the last run command
:type: dict
"""
if len(self.runs) > 0: if len(self.runs) > 0:
return self.runs[-1] return self.runs[-1]
return None return None
@property @property
def atoms(self): def atoms(self):
"""
All atoms of this LAMMPS instance
:getter: Returns a list of atoms currently in the system
:type: AtomList
"""
return AtomList(self) return AtomList(self)
@property @property
def system(self): def system(self):
"""
The system state of this LAMMPS instance
:getter: Returns an object with properties storing the current system state
:type: namedtuple
"""
output = self.info("system") output = self.info("system")
d = self._parse_info_system(output) d = self._parse_info_system(output)
return namedtuple('System', d.keys())(*d.values()) return namedtuple('System', d.keys())(*d.values())
@property @property
def communication(self): def communication(self):
"""
The communication state of this LAMMPS instance
:getter: Returns an object with properties storing the current communication state
:type: namedtuple
"""
output = self.info("communication") output = self.info("communication")
d = self._parse_info_communication(output) d = self._parse_info_communication(output)
return namedtuple('Communication', d.keys())(*d.values()) return namedtuple('Communication', d.keys())(*d.values())
@property @property
def computes(self): def computes(self):
"""
The list of active computes of this LAMMPS instance
:getter: Returns a list of computes that are currently active in this LAMMPS instance
:type: list
"""
output = self.info("computes") output = self.info("computes")
return self._parse_element_list(output) return self._parse_element_list(output)
@property @property
def dumps(self): def dumps(self):
"""
The list of active dumps of this LAMMPS instance
:getter: Returns a list of dumps that are currently active in this LAMMPS instance
:type: list
"""
output = self.info("dumps") output = self.info("dumps")
return self._parse_element_list(output) return self._parse_element_list(output)
@property @property
def fixes(self): def fixes(self):
"""
The list of active fixes of this LAMMPS instance
:getter: Returns a list of fixes that are currently active in this LAMMPS instance
:type: list
"""
output = self.info("fixes") output = self.info("fixes")
return self._parse_element_list(output) return self._parse_element_list(output)
@property @property
def groups(self): def groups(self):
"""
The list of active atom groups of this LAMMPS instance
:getter: Returns a list of atom groups that are currently active in this LAMMPS instance
:type: list
"""
output = self.info("groups") output = self.info("groups")
return self._parse_groups(output) return self._parse_groups(output)
@property @property
def variables(self): def variables(self):
"""
Returns a dictionary of all variables defined in the current LAMMPS instance
:getter: Returns a dictionary of all variables that are defined in this LAMMPS instance
:type: dict
"""
output = self.info("variables") output = self.info("variables")
vars = {} vars = {}
for v in self._parse_element_list(output): for v in self._parse_element_list(output):
@ -2021,6 +2317,15 @@ class PyLammps(object):
return vars return vars
def eval(self, expr): def eval(self, expr):
"""
Evaluate expression
:param expr: the expression string that should be evaluated inside of LAMMPS
:type expr: string
:return: the value of the evaluated expression
:rtype: float if numeric, string otherwise
"""
value = self.lmp_print('"$(%s)"' % expr).strip() value = self.lmp_print('"$(%s)"' % expr).strip()
try: try:
return float(value) return float(value)
@ -2156,11 +2461,23 @@ class PyLammps(object):
'variable', 'velocity', 'write_restart'] 'variable', 'velocity', 'write_restart']
def __getattr__(self, name): def __getattr__(self, name):
"""
This method is where the Python 'magic' happens. If a method is not
defined by the class PyLammps, it assumes it is a LAMMPS command. It takes
all the arguments, concatinates them to a single string, and executes it using
:py:meth:`lammps.PyLammps.command()`.
:param verbose: Print output of command
:type verbose: bool
:return: line or list of lines of output, None if no output
:rtype: list or string
"""
def handler(*args, **kwargs): def handler(*args, **kwargs):
cmd_args = [name] + [str(x) for x in args] cmd_args = [name] + [str(x) for x in args]
with OutputCapture() as capture: with OutputCapture() as capture:
self.command(' '.join(cmd_args)) cmd = ' '.join(cmd_args)
self.command(cmd)
output = capture.output output = capture.output
if 'verbose' in kwargs and kwargs['verbose']: if 'verbose' in kwargs and kwargs['verbose']:
@ -2168,6 +2485,9 @@ class PyLammps(object):
lines = output.splitlines() lines = output.splitlines()
if self.has_echo:
lines = lines[1:]
if len(lines) > 1: if len(lines) > 1:
return lines return lines
elif len(lines) == 1: elif len(lines) == 1:
@ -2179,14 +2499,56 @@ class PyLammps(object):
class IPyLammps(PyLammps): class IPyLammps(PyLammps):
""" """
IPython wrapper for LAMMPS which adds embedded graphics capabilities IPython wrapper for LAMMPS which adds embedded graphics capabilities to PyLammmps 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
lower-level interface. The original interface can still be accessed via
:py:attr:`PyLammps.lmp`.
:param name: "machine" name of the shared LAMMPS library ("mpi" loads ``liblammps_mpi.so``, "" loads ``liblammps.so``)
:type name: string
:param cmdargs: list of command line arguments to be passed to the :cpp:func:`lammps_open` function. The executable name is automatically added.
:type cmdargs: list
:param ptr: pointer to a LAMMPS C++ class instance when called from an embedded Python interpreter. None means load symbols from shared library.
:type ptr: pointer
:param comm: MPI communicator (as provided by `mpi4py <mpi4py_docs_>`_). ``None`` means use ``MPI_COMM_WORLD`` implicitly.
:type comm: MPI_Comm
""" """
def __init__(self,name="",cmdargs=None,ptr=None,comm=None): def __init__(self,name="",cmdargs=None,ptr=None,comm=None):
super(IPyLammps, self).__init__(name=name,cmdargs=cmdargs,ptr=ptr,comm=comm) super(IPyLammps, self).__init__(name=name,cmdargs=cmdargs,ptr=ptr,comm=comm)
def image(self, filename="snapshot.png", group="all", color="type", diameter="type", def image(self, filename="snapshot.png", group="all", color="type", diameter="type",
size=None, view=None, center=None, up=None, zoom=1.0): size=None, view=None, center=None, up=None, zoom=1.0, background_color="white"):
""" Generate image using write_dump command and display it
See :doc:`dump image <dump_image>` for more information.
:param filename: Name of the image file that should be generated. The extension determines whether it is PNG or JPEG
:type filename: string
:param group: the group of atoms write_image should use
:type group: string
:param color: name of property used to determine color
:type color: string
:param diameter: name of property used to determine atom diameter
:type diameter: string
:param size: dimensions of image
:type size: tuple (width, height)
:param view: view parameters
:type view: tuple (theta, phi)
:param center: center parameters
:type center: tuple (flag, center_x, center_y, center_z)
:param up: vector pointing to up direction
:type up: tuple (up_x, up_y, up_z)
:param zoom: zoom factor
:type zoom: float
:param background_color: background color of scene
:type background_color: string
:return: Image instance used to display image in notebook
:rtype: :py:class:`IPython.core.display.Image`
"""
cmd_args = [group, "image", filename, color, diameter] cmd_args = [group, "image", filename, color, diameter]
if size: if size:
@ -2215,12 +2577,22 @@ class IPyLammps(PyLammps):
if zoom: if zoom:
cmd_args += ["zoom", zoom] cmd_args += ["zoom", zoom]
cmd_args.append("modify backcolor white") cmd_args.append("modify backcolor " + background_color)
self.write_dump(*cmd_args) self.write_dump(*cmd_args)
from IPython.core.display import Image from IPython.core.display import Image
return Image('snapshot.png') return Image(filename)
def video(self, filename): def video(self, filename):
"""
Load video from file
Can be used to visualize videos from :doc:`dump movie <dump_image>`.
:param filename: Path to video file
:type filename: string
:return: HTML Video Tag used by notebook to embed a video
:rtype: :py:class:`IPython.display.HTML`
"""
from IPython.display import HTML from IPython.display import HTML
return HTML("<video controls><source src=\"" + filename + "\"></video>") return HTML("<video controls><source src=\"" + filename + "\"></video>")

View File

@ -31,6 +31,8 @@
#include "update.h" #include "update.h"
#include "variable.h" #include "variable.h"
#include "library.h"
#include <algorithm> #include <algorithm>
#include <cstring> #include <cstring>
@ -2506,6 +2508,8 @@ length of the data area, and a short description.
:cpp:func:`lammps_extract_atom` :cpp:func:`lammps_extract_atom`
\endverbatim \endverbatim
*
* \sa extract_datatype
* *
* \param name string with the keyword of the desired property. * \param name string with the keyword of the desired property.
Typically the name of the pointer variable returned Typically the name of the pointer variable returned
@ -2515,6 +2519,8 @@ void *Atom::extract(const char *name)
{ {
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// 4th customization section: customize by adding new variable name // 4th customization section: customize by adding new variable name
// please see the following function to set the type of the data
// so that programs can detect it dynamically at run time.
/* NOTE: this array is only of length ntypes+1 */ /* NOTE: this array is only of length ntypes+1 */
if (strcmp(name,"mass") == 0) return (void *) mass; if (strcmp(name,"mass") == 0) return (void *) mass;
@ -2583,6 +2589,89 @@ void *Atom::extract(const char *name)
return nullptr; return nullptr;
} }
/** Provide data type info about internal data of the Atom class
*
\verbatim embed:rst
.. versionadded:: 18Sep2020
\endverbatim
*
* \sa extract
*
* \param name string with the keyword of the desired property.
* \return data type constant for desired property or -1 */
int Atom::extract_datatype(const char *name)
{
// --------------------------------------------------------------------
// 5th customization section: customize by adding new variable name
if (strcmp(name,"mass") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"id") == 0) return LAMMPS_TAGINT;
if (strcmp(name,"type") == 0) return LAMMPS_INT;
if (strcmp(name,"mask") == 0) return LAMMPS_INT;
if (strcmp(name,"image") == 0) return LAMMPS_TAGINT;
if (strcmp(name,"x") == 0) return LAMMPS_DOUBLE_2D;
if (strcmp(name,"v") == 0) return LAMMPS_DOUBLE_2D;
if (strcmp(name,"f") == 0) return LAMMPS_DOUBLE_2D;
if (strcmp(name,"molecule") == 0) return LAMMPS_TAGINT;
if (strcmp(name,"q") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"mu") == 0) return LAMMPS_DOUBLE_2D;
if (strcmp(name,"omega") == 0) return LAMMPS_DOUBLE_2D;
if (strcmp(name,"angmom") == 0) return LAMMPS_DOUBLE_2D;
if (strcmp(name,"torque") == 0) return LAMMPS_DOUBLE_2D;
if (strcmp(name,"radius") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"rmass") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"ellipsoid") == 0) return LAMMPS_INT;
if (strcmp(name,"line") == 0) return LAMMPS_INT;
if (strcmp(name,"tri") == 0) return LAMMPS_INT;
if (strcmp(name,"body") == 0) return LAMMPS_INT;
if (strcmp(name,"vfrac") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"s0") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"x0") == 0) return LAMMPS_DOUBLE_2D;
if (strcmp(name,"spin") == 0) return LAMMPS_INT;
if (strcmp(name,"eradius") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"ervel") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"erforce") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"ervelforce") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"cs") == 0) return LAMMPS_DOUBLE_2D;
if (strcmp(name,"csforce") == 0) return LAMMPS_DOUBLE_2D;
if (strcmp(name,"vforce") == 0) return LAMMPS_DOUBLE_2D;
if (strcmp(name,"etag") == 0) return LAMMPS_INT;
if (strcmp(name,"rho") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"drho") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"esph") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"desph") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"cv") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"vest") == 0) return LAMMPS_DOUBLE_2D;
// USER-MESONT package
if (strcmp(name,"length") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"buckling") == 0) return LAMMPS_INT;
if (strcmp(name,"bond_nt") == 0) return LAMMPS_TAGINT_2D;
if (strcmp(name, "contact_radius") == 0) return LAMMPS_DOUBLE;
if (strcmp(name, "smd_data_9") == 0) return LAMMPS_DOUBLE_2D;
if (strcmp(name, "smd_stress") == 0) return LAMMPS_DOUBLE_2D;
if (strcmp(name, "eff_plastic_strain") == 0) return LAMMPS_DOUBLE;
if (strcmp(name, "eff_plastic_strain_rate") == 0) return LAMMPS_DOUBLE;
if (strcmp(name, "damage") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"dpdTheta") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"edpd_temp") == 0) return LAMMPS_DOUBLE;
// end of customization section
// --------------------------------------------------------------------
return -1;
}
/* ---------------------------------------------------------------------- /* ----------------------------------------------------------------------
return # of bytes of allocated memory return # of bytes of allocated memory
call to avec tallies per-atom vectors call to avec tallies per-atom vectors

View File

@ -335,6 +335,7 @@ class Atom : protected Pointers {
virtual void sync_modify(ExecutionSpace, unsigned int, unsigned int) {} virtual void sync_modify(ExecutionSpace, unsigned int, unsigned int) {}
void *extract(const char *); void *extract(const char *);
int extract_datatype(const char *);
inline int* get_map_array() {return map_array;}; inline int* get_map_array() {return map_array;};
inline int get_map_size() {return map_tag_max+1;}; inline int get_map_size() {return map_tag_max+1;};

View File

@ -18,7 +18,7 @@
#include <vector> #include <vector>
// The fmt library version in the form major * 10000 + minor * 100 + patch. // The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 70002 #define FMT_VERSION 70003
#ifdef __clang__ #ifdef __clang__
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) # define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
@ -177,6 +177,12 @@
# endif # endif
#endif #endif
// LAMMPS customization
// use 'v7_lmp' namespace instead of 'v7' so that our
// bundled copy does not collide with linking other code
// using system wide installations which may be using
// a different version.
#ifndef FMT_BEGIN_NAMESPACE #ifndef FMT_BEGIN_NAMESPACE
# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \ # if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
FMT_MSC_VER >= 1900 FMT_MSC_VER >= 1900
@ -299,7 +305,7 @@ template <typename T> struct std_string_view {};
#ifdef FMT_USE_INT128 #ifdef FMT_USE_INT128
// Do nothing. // Do nothing.
#elif defined(__SIZEOF_INT128__) && !FMT_NVCC #elif defined(__SIZEOF_INT128__) && !FMT_NVCC && !(FMT_CLANG_VERSION && FMT_MSC_VER)
# define FMT_USE_INT128 1 # define FMT_USE_INT128 1
using int128_t = __int128_t; using int128_t = __int128_t;
using uint128_t = __uint128_t; using uint128_t = __uint128_t;
@ -489,6 +495,8 @@ constexpr basic_string_view<typename S::char_type> to_string_view(const S& s) {
return s; return s;
} }
// LAMMPS customization using 'v7_lmp' instead of 'v7'
namespace detail { namespace detail {
void to_string_view(...); void to_string_view(...);
using fmt::v7_lmp::to_string_view; using fmt::v7_lmp::to_string_view;
@ -1713,7 +1721,7 @@ template <typename Context> class basic_format_args {
} }
template <typename Char> int get_id(basic_string_view<Char> name) const { template <typename Char> int get_id(basic_string_view<Char> name) const {
if (!has_named_args()) return {}; if (!has_named_args()) return -1;
const auto& named_args = const auto& named_args =
(is_packed() ? values_[-1] : args_[-1].value_).named_args; (is_packed() ? values_[-1] : args_[-1].value_).named_args;
for (size_t i = 0; i < named_args.size; ++i) { for (size_t i = 0; i < named_args.size; ++i) {

View File

@ -69,6 +69,12 @@
# define FMT_NOINLINE # define FMT_NOINLINE
#endif #endif
// LAMMPS customizations:
// 1) Intel compilers on MacOS have __clang__ defined
// but fail to recognize [[clang::fallthrough]]
// 2) Intel compilers on Linux identify as GCC compatible
// but fail to recognize [[gnu::fallthrough]]
#if __cplusplus == 201103L || __cplusplus == 201402L #if __cplusplus == 201103L || __cplusplus == 201402L
# if defined(__clang__) && !defined(__INTEL_COMPILER) # if defined(__clang__) && !defined(__INTEL_COMPILER)
# define FMT_FALLTHROUGH [[clang::fallthrough]] # define FMT_FALLTHROUGH [[clang::fallthrough]]
@ -724,13 +730,18 @@ class FMT_API format_error : public std::runtime_error {
namespace detail { namespace detail {
template <typename T>
using is_signed =
std::integral_constant<bool, std::numeric_limits<T>::is_signed ||
std::is_same<T, int128_t>::value>;
// Returns true if value is negative, false otherwise. // Returns true if value is negative, false otherwise.
// Same as `value < 0` but doesn't produce warnings if T is an unsigned type. // Same as `value < 0` but doesn't produce warnings if T is an unsigned type.
template <typename T, FMT_ENABLE_IF(std::numeric_limits<T>::is_signed)> template <typename T, FMT_ENABLE_IF(is_signed<T>::value)>
FMT_CONSTEXPR bool is_negative(T value) { FMT_CONSTEXPR bool is_negative(T value) {
return value < 0; return value < 0;
} }
template <typename T, FMT_ENABLE_IF(!std::numeric_limits<T>::is_signed)> template <typename T, FMT_ENABLE_IF(!is_signed<T>::value)>
FMT_CONSTEXPR bool is_negative(T) { FMT_CONSTEXPR bool is_negative(T) {
return false; return false;
} }
@ -745,9 +756,9 @@ FMT_CONSTEXPR bool is_supported_floating_point(T) {
// Smallest of uint32_t, uint64_t, uint128_t that is large enough to // Smallest of uint32_t, uint64_t, uint128_t that is large enough to
// represent all values of T. // represent all values of T.
template <typename T> template <typename T>
using uint32_or_64_or_128_t = conditional_t< using uint32_or_64_or_128_t =
num_bits<T>() <= 32, uint32_t, conditional_t<num_bits<T>() <= 32, uint32_t,
conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>>; conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>>;
// Static data is placed in this class template for the header-only config. // Static data is placed in this class template for the header-only config.
template <typename T = void> struct FMT_EXTERN_TEMPLATE_API basic_data { template <typename T = void> struct FMT_EXTERN_TEMPLATE_API basic_data {
@ -1593,7 +1604,11 @@ template <typename OutputIt, typename Char, typename UInt> struct int_writer {
make_checked(p, s.size())); make_checked(p, s.size()));
} }
if (prefix_size != 0) p[-1] = static_cast<Char>('-'); if (prefix_size != 0) p[-1] = static_cast<Char>('-');
write(out, basic_string_view<Char>(buffer.data(), buffer.size()), specs); using iterator = remove_reference_t<decltype(reserve(out, 0))>;
auto data = buffer.data();
out = write_padded<align::right>(out, specs, size, size, [=](iterator it) {
return copy_str<Char>(data, data + size, it);
});
} }
void on_chr() { *out++ = static_cast<Char>(abs_value); } void on_chr() { *out++ = static_cast<Char>(abs_value); }

View File

@ -917,7 +917,7 @@ int lammps_extract_setting(void *handle, const char *keyword)
This function returns a pointer to the location of some global property This function returns a pointer to the location of some global property
stored in one of the constituent classes of a LAMMPS instance. The stored in one of the constituent classes of a LAMMPS instance. The
returned pointer is cast to ``void *`` and needs to be cast to a pointer returned pointer is cast to ``void *`` and needs to be cast to a pointer
of the type that the entity represents. The pointers returned by this of the type that the entity represents. The pointers returned by this
function are generally persistent; therefore it is not necessary to call function are generally persistent; therefore it is not necessary to call
the function again, unless a :doc:`clear` command is issued which wipes the function again, unless a :doc:`clear` command is issued which wipes
out and recreates the contents of the :cpp:class:`LAMMPS out and recreates the contents of the :cpp:class:`LAMMPS
@ -1227,7 +1227,7 @@ of data type that the entity represents.
A table with supported keywords is included in the documentation A table with supported keywords is included in the documentation
of the :cpp:func:`Atom::extract() <LAMMPS_NS::Atom::extract>` function. of the :cpp:func:`Atom::extract() <LAMMPS_NS::Atom::extract>` function.
.. note:: .. warning::
The pointers returned by this function are generally not persistent The pointers returned by this function are generally not persistent
since per-atom data may be re-distributed, re-allocated, and since per-atom data may be re-distributed, re-allocated, and
@ -1248,6 +1248,117 @@ void *lammps_extract_atom(void *handle, const char *name)
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
/** Get data type of internal global LAMMPS variables or arrays.
*
\verbatim embed:rst
This function returns an integer that encodes the data type of the global
property with the specified name. See :cpp:enum:`_LMP_DATATYPE_CONST` for valid
values. Callers of :cpp:func:`lammps_extract_global` can use this information
to then decide how to cast the (void*) pointer and access the data.
.. versionadded:: 18Sep2020
\endverbatim
*
* \param handle pointer to a previously created LAMMPS instance
* \param name string with the name of the extracted property
* \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)
{
LAMMPS *lmp = (LAMMPS *) handle;
if (strcmp(name,"units") == 0) return LAMMPS_STRING;
if (strcmp(name,"dt") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"ntimestep") == 0) return LAMMPS_BIGINT;
if (strcmp(name,"boxlo") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"boxhi") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"boxxlo") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"boxxhi") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"boxylo") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"boxyhi") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"boxzlo") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"boxzhi") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"periodicity") == 0) return LAMMPS_INT;
if (strcmp(name,"triclinic") == 0) return LAMMPS_INT;
if (strcmp(name,"xy") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"xz") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"yz") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"natoms") == 0) return LAMMPS_BIGINT;
if (strcmp(name,"nbonds") == 0) return LAMMPS_BIGINT;
if (strcmp(name,"nangles") == 0) return LAMMPS_BIGINT;
if (strcmp(name,"ndihedrals") == 0) return LAMMPS_BIGINT;
if (strcmp(name,"nimpropers") == 0) return LAMMPS_BIGINT;
if (strcmp(name,"nlocal") == 0) return LAMMPS_INT;
if (strcmp(name,"nghost") == 0) return LAMMPS_INT;
if (strcmp(name,"nmax") == 0) return LAMMPS_INT;
if (strcmp(name,"ntypes") == 0) return LAMMPS_INT;
if (strcmp(name,"q_flag") == 0) return LAMMPS_INT;
// update->atime can be referenced as a pointer
// thermo "timer" data cannot be, since it is computed on request
// lammps_get_thermo() can access all thermo keywords by value
if (strcmp(name,"atime") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"atimestep") == 0) return LAMMPS_BIGINT;
// global constants defined by units
if (strcmp(name,"boltz") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"hplanck") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"mvv2e") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"ftm2v") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"mv2d") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"nktv2p") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"qqr2e") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"qe2f") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"vxmu2f") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"xxt2kmu") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"dielectric") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"qqrd2e") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"e_mass") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"hhmrr2e") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"mvh2r") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"angstrom") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"femtosecond") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"qelectron") == 0) return LAMMPS_DOUBLE;
return -1;
}
/* ---------------------------------------------------------------------- */
/** Get data type of a LAMMPS per-atom property
*
\verbatim embed:rst
This function returns an integer that encodes the data type of the per-atom
property with the specified name. See :cpp:enum:`_LMP_DATATYPE_CONST` for valid
values. Callers of :cpp:func:`lammps_extract_atom` can use this information
to then decide how to cast the (void*) pointer and access the data.
.. versionadded:: 18Sep2020
\endverbatim
*
* \param handle pointer to a previously created LAMMPS instance
* \param name string with the name of the extracted property
* \return integer constant encoding the data type of the property
* or -1 if not found.
* */
int lammps_extract_atom_datatype(void *handle, const char *name)
{
LAMMPS *lmp = (LAMMPS *) handle;
return lmp->atom->extract_datatype(name);
}
/* ---------------------------------------------------------------------- */
/** Create N atoms from list of coordinates /** Create N atoms from list of coordinates
* *
\verbatim embed:rst \verbatim embed:rst
@ -1472,11 +1583,13 @@ lists the available options.
- ``int *`` - ``int *``
- Number of local data columns - Number of local data columns
The pointers returned by this function are generally not persistent .. warning::
since the computed data may be re-distributed, re-allocated, and
re-ordered at every invocation. It is advisable to re-invoke this The pointers returned by this function are generally not persistent
function before the data is accessed, or make a copy if the data shall since the computed data may be re-distributed, re-allocated, and
be used after other LAMMPS commands have been issued. re-ordered at every invocation. It is advisable to re-invoke this
function before the data is accessed, or make a copy if the data shall
be used after other LAMMPS commands have been issued.
.. note:: .. note::
@ -1656,12 +1769,14 @@ The following table lists the available options.
- ``int *`` - ``int *``
- Number of local data columns - Number of local data columns
The pointers returned by this function for per-atom or local data are .. warning::
generally not persistent, since the computed data may be re-distributed,
re-allocated, and re-ordered at every invocation of the fix. It is thus The pointers returned by this function for per-atom or local data are
advisable to re-invoke this function before the data is accessed, or generally not persistent, since the computed data may be re-distributed,
make a copy, if the data shall be used after other LAMMPS commands have re-allocated, and re-ordered at every invocation of the fix. It is thus
been issued. advisable to re-invoke this function before the data is accessed, or
make a copy, if the data shall be used after other LAMMPS commands have
been issued.
.. note:: .. note::

View File

@ -40,6 +40,20 @@
#include <inttypes.h> /* for int64_t */ #include <inttypes.h> /* for int64_t */
#endif #endif
/** Data type constants for extracting data from atoms, computes and fixes
*
* Must be kept in sync with the equivalent constants in lammps.py */
enum _LMP_DATATYPE_CONST {
LAMMPS_INT = 0, /*!< 32-bit integer (array) */
LAMMPS_INT_2D = 1, /*!< two-dimensional 32-bit integer array */
LAMMPS_DOUBLE = 2, /*!< 64-bit double (array) */
LAMMPS_DOUBLE_2D = 3, /*!< two-dimensional 64-bit double array */
LAMMPS_INT64 = 4, /*!< 64-bit integer (array) */
LAMMPS_INT64_2D = 5, /*!< two-dimensional 64-bit integer array */
LAMMPS_STRING = 6 /*!< C-String */
};
/** Style constants for extracting data from computes and fixes. /** Style constants for extracting data from computes and fixes.
* *
* Must be kept in sync with the equivalent constants in lammps.py */ * Must be kept in sync with the equivalent constants in lammps.py */
@ -113,6 +127,9 @@ int lammps_extract_setting(void *handle, const char *keyword);
void *lammps_extract_global(void *handle, const char *name); void *lammps_extract_global(void *handle, const char *name);
void *lammps_extract_atom(void *handle, const char *name); void *lammps_extract_atom(void *handle, const char *name);
int lammps_extract_global_datatype(void *handle, const char *name);
int lammps_extract_atom_datatype(void *handle, const char *name);
#if !defined(LAMMPS_BIGBIG) #if !defined(LAMMPS_BIGBIG)
int lammps_create_atoms(void *handle, int n, int *id, int *type, int lammps_create_atoms(void *handle, int n, int *id, int *type,
double *x, double *v, int *image, int bexpand); double *x, double *v, int *image, int bexpand);

View File

@ -102,6 +102,11 @@ typedef int64_t bigint;
#define ATOTAGINT atoi #define ATOTAGINT atoi
#define ATOBIGINT ATOLL #define ATOBIGINT ATOLL
#define LAMMPS_TAGINT LAMMPS_INT
#define LAMMPS_TAGINT_2D LAMMPS_INT_2D
#define LAMMPS_BIGINT LAMMPS_INT64
#define LAMMPS_BIGINT_2D LAMMPS_INT64_2D
#define IMGMASK 1023 #define IMGMASK 1023
#define IMGMAX 512 #define IMGMAX 512
#define IMGBITS 10 #define IMGBITS 10
@ -134,6 +139,11 @@ typedef int64_t bigint;
#define ATOTAGINT ATOLL #define ATOTAGINT ATOLL
#define ATOBIGINT ATOLL #define ATOBIGINT ATOLL
#define LAMMPS_TAGINT LAMMPS_INT64
#define LAMMPS_TAGINT_2D LAMMPS_INT64_2D
#define LAMMPS_BIGINT LAMMPS_INT64
#define LAMMPS_BIGINT_2D LAMMPS_INT64_2D
#define IMGMASK 2097151 #define IMGMASK 2097151
#define IMGMAX 1048576 #define IMGMAX 1048576
#define IMGBITS 21 #define IMGBITS 21
@ -165,6 +175,11 @@ typedef int bigint;
#define ATOTAGINT atoi #define ATOTAGINT atoi
#define ATOBIGINT atoi #define ATOBIGINT atoi
#define LAMMPS_TAGINT LAMMPS_INT
#define LAMMPS_TAGINT_2D LAMMPS_INT_2D
#define LAMMPS_BIGINT LAMMPS_INT
#define LAMMPS_BIGINT_2D LAMMPS_INT_2D
#define IMGMASK 1023 #define IMGMASK 1023
#define IMGMAX 512 #define IMGMAX 512
#define IMGBITS 10 #define IMGBITS 10

View File

@ -251,4 +251,90 @@ TEST_F(LibraryProperties, global)
EXPECT_EQ((*b_ptr), 2); EXPECT_EQ((*b_ptr), 2);
d_ptr = (double *)lammps_extract_global(lmp, "dt"); d_ptr = (double *)lammps_extract_global(lmp, "dt");
EXPECT_DOUBLE_EQ((*d_ptr), 0.1); EXPECT_DOUBLE_EQ((*d_ptr), 0.1);
int dtype = lammps_extract_global_datatype(lmp, "dt");
EXPECT_EQ(dtype, LAMMPS_DOUBLE);
}; };
class AtomProperties : public ::testing::Test {
protected:
void *lmp;
AtomProperties(){};
~AtomProperties() override{};
void SetUp() override
{
const char *args[] = {"LAMMPS_test", "-log", "none",
"-echo", "screen", "-nocite"};
char **argv = (char **)args;
int argc = sizeof(args) / sizeof(char *);
::testing::internal::CaptureStdout();
lmp = lammps_open_no_mpi(argc, argv, NULL);
std::string output = ::testing::internal::GetCapturedStdout();
if (verbose) std::cout << output;
EXPECT_THAT(output, StartsWith("LAMMPS ("));
::testing::internal::CaptureStdout();
lammps_command(lmp, "region box block 0 2 0 2 0 2");
lammps_command(lmp, "create_box 1 box");
lammps_command(lmp, "mass 1 3.0");
lammps_command(lmp, "create_atoms 1 single 1.0 1.0 1.5");
lammps_command(lmp, "create_atoms 1 single 0.2 0.1 0.1");
output = ::testing::internal::GetCapturedStdout();
if (verbose) std::cout << output;
}
void TearDown() override
{
::testing::internal::CaptureStdout();
lammps_close(lmp);
std::string output = ::testing::internal::GetCapturedStdout();
EXPECT_THAT(output, HasSubstr("Total wall time:"));
if (verbose) std::cout << output;
lmp = nullptr;
}
};
TEST_F(AtomProperties, invalid)
{
ASSERT_EQ(lammps_extract_atom(lmp, "UNKNOWN"), nullptr);
}
TEST_F(AtomProperties, mass)
{
EXPECT_EQ(lammps_extract_atom_datatype(lmp, "mass"), LAMMPS_DOUBLE);
double * mass = (double *)lammps_extract_atom(lmp, "mass");
ASSERT_NE(mass, nullptr);
ASSERT_DOUBLE_EQ(mass[1], 3.0);
}
TEST_F(AtomProperties, id)
{
EXPECT_EQ(lammps_extract_atom_datatype(lmp, "id"), LAMMPS_TAGINT);
LAMMPS_NS::tagint * id = (LAMMPS_NS::tagint *)lammps_extract_atom(lmp, "id");
ASSERT_NE(id, nullptr);
ASSERT_EQ(id[0], 1);
ASSERT_EQ(id[1], 2);
}
TEST_F(AtomProperties, type)
{
EXPECT_EQ(lammps_extract_atom_datatype(lmp, "type"), LAMMPS_INT);
int * type = (int *)lammps_extract_atom(lmp, "type");
ASSERT_NE(type, nullptr);
ASSERT_EQ(type[0], 1);
ASSERT_EQ(type[1], 1);
}
TEST_F(AtomProperties, position)
{
EXPECT_EQ(lammps_extract_atom_datatype(lmp, "x"), LAMMPS_DOUBLE_2D);
double ** x = (double **)lammps_extract_atom(lmp, "x");
ASSERT_NE(x, nullptr);
EXPECT_DOUBLE_EQ(x[0][0], 1.0);
EXPECT_DOUBLE_EQ(x[0][1], 1.0);
EXPECT_DOUBLE_EQ(x[0][2], 1.5);
EXPECT_DOUBLE_EQ(x[1][0], 0.2);
EXPECT_DOUBLE_EQ(x[1][1], 0.1);
EXPECT_DOUBLE_EQ(x[1][2], 0.1);
}

View File

@ -62,40 +62,40 @@ if (PKG_COMPRESS)
set_tests_properties(DumpLocalGZ PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR}") set_tests_properties(DumpLocalGZ PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR}")
set_tests_properties(DumpLocalGZ PROPERTIES ENVIRONMENT "GZIP_BINARY=${GZIP_BINARY}") set_tests_properties(DumpLocalGZ PROPERTIES ENVIRONMENT "GZIP_BINARY=${GZIP_BINARY}")
if(Zstd_FOUND) find_package(PkgConfig REQUIRED)
find_program(ZSTD_BINARY NAMES zstd) pkg_check_modules(Zstd IMPORTED_TARGET libzstd>=1.4)
find_program(ZSTD_BINARY NAMES zstd)
if (ZSTD_BINARY) if(Zstd_FOUND AND ZSTD_BINARY)
add_executable(test_dump_atom_zstd test_dump_atom_zstd.cpp) add_executable(test_dump_atom_zstd test_dump_atom_zstd.cpp)
target_link_libraries(test_dump_atom_zstd PRIVATE lammps GTest::GMock GTest::GTest) target_link_libraries(test_dump_atom_zstd PRIVATE lammps GTest::GMock GTest::GTest)
add_test(NAME DumpAtomZstd COMMAND test_dump_atom_zstd WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) add_test(NAME DumpAtomZstd COMMAND test_dump_atom_zstd WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set_tests_properties(DumpAtomZstd PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR}") set_tests_properties(DumpAtomZstd PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR}")
set_tests_properties(DumpAtomZstd PROPERTIES ENVIRONMENT "ZSTD_BINARY=${ZSTD_BINARY}") set_tests_properties(DumpAtomZstd PROPERTIES ENVIRONMENT "ZSTD_BINARY=${ZSTD_BINARY}")
add_executable(test_dump_custom_zstd test_dump_custom_zstd.cpp) add_executable(test_dump_custom_zstd test_dump_custom_zstd.cpp)
target_link_libraries(test_dump_custom_zstd PRIVATE lammps GTest::GMock GTest::GTest) target_link_libraries(test_dump_custom_zstd PRIVATE lammps GTest::GMock GTest::GTest)
add_test(NAME DumpCustomZstd COMMAND test_dump_custom_zstd WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) add_test(NAME DumpCustomZstd COMMAND test_dump_custom_zstd WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set_tests_properties(DumpCustomZstd PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR}") set_tests_properties(DumpCustomZstd PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR}")
set_tests_properties(DumpCustomZstd PROPERTIES ENVIRONMENT "ZSTD_BINARY=${ZSTD_BINARY}") set_tests_properties(DumpCustomZstd PROPERTIES ENVIRONMENT "ZSTD_BINARY=${ZSTD_BINARY}")
add_executable(test_dump_cfg_zstd test_dump_cfg_zstd.cpp) add_executable(test_dump_cfg_zstd test_dump_cfg_zstd.cpp)
target_link_libraries(test_dump_cfg_zstd PRIVATE lammps GTest::GMock GTest::GTest) target_link_libraries(test_dump_cfg_zstd PRIVATE lammps GTest::GMock GTest::GTest)
add_test(NAME DumpCfgZstd COMMAND test_dump_cfg_zstd WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) add_test(NAME DumpCfgZstd COMMAND test_dump_cfg_zstd WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set_tests_properties(DumpCfgZstd PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR}") set_tests_properties(DumpCfgZstd PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR}")
set_tests_properties(DumpCfgZstd PROPERTIES ENVIRONMENT "ZSTD_BINARY=${ZSTD_BINARY}") set_tests_properties(DumpCfgZstd PROPERTIES ENVIRONMENT "ZSTD_BINARY=${ZSTD_BINARY}")
add_executable(test_dump_xyz_zstd test_dump_xyz_zstd.cpp) add_executable(test_dump_xyz_zstd test_dump_xyz_zstd.cpp)
target_link_libraries(test_dump_xyz_zstd PRIVATE lammps GTest::GMock GTest::GTest) target_link_libraries(test_dump_xyz_zstd PRIVATE lammps GTest::GMock GTest::GTest)
add_test(NAME DumpXYZZstd COMMAND test_dump_xyz_zstd WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) add_test(NAME DumpXYZZstd COMMAND test_dump_xyz_zstd WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set_tests_properties(DumpXYZZstd PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR}") set_tests_properties(DumpXYZZstd PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR}")
set_tests_properties(DumpXYZZstd PROPERTIES ENVIRONMENT "ZSTD_BINARY=${ZSTD_BINARY}") set_tests_properties(DumpXYZZstd PROPERTIES ENVIRONMENT "ZSTD_BINARY=${ZSTD_BINARY}")
add_executable(test_dump_local_zstd test_dump_local_zstd.cpp) add_executable(test_dump_local_zstd test_dump_local_zstd.cpp)
target_link_libraries(test_dump_local_zstd PRIVATE lammps GTest::GMock GTest::GTest) target_link_libraries(test_dump_local_zstd PRIVATE lammps GTest::GMock GTest::GTest)
add_test(NAME DumpLocalZstd COMMAND test_dump_local_zstd WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) add_test(NAME DumpLocalZstd COMMAND test_dump_local_zstd WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set_tests_properties(DumpLocalZstd PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR}") set_tests_properties(DumpLocalZstd PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR}")
set_tests_properties(DumpLocalZstd PROPERTIES ENVIRONMENT "ZSTD_BINARY=${ZSTD_BINARY}") set_tests_properties(DumpLocalZstd PROPERTIES ENVIRONMENT "ZSTD_BINARY=${ZSTD_BINARY}")
endif()
endif() endif()
endif() endif()

View File

@ -17,9 +17,9 @@ if (Python_EXECUTABLE)
# prepare to augment the environment so that the LAMMPS python module and the shared library is found. # prepare to augment the environment so that the LAMMPS python module and the shared library is found.
set(PYTHON_TEST_ENVIRONMENT PYTHONPATH=${LAMMPS_PYTHON_DIR}:$ENV{PYTHONPATH}) set(PYTHON_TEST_ENVIRONMENT PYTHONPATH=${LAMMPS_PYTHON_DIR}:$ENV{PYTHONPATH})
if(APPLE) if(APPLE)
list(APPEND PYTHON_TEST_ENVIRONMENT DYLD_LIBRARY_PATH=${CMAKE_BINARY_DIR}:$ENV{DYLD_LIBRARY_PATH}) list(APPEND PYTHON_TEST_ENVIRONMENT "DYLD_LIBRARY_PATH=${CMAKE_BINARY_DIR}:$ENV{DYLD_LIBRARY_PATH};LAMMPS_CMAKE_CACHE=${CMAKE_BINARY_DIR}/CMakeCache.txt")
else() else()
list(APPEND PYTHON_TEST_ENVIRONMENT LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}:$ENV{LD_LIBRARY_PATH}) list(APPEND PYTHON_TEST_ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}:$ENV{LD_LIBRARY_PATH};LAMMPS_CMAKE_CACHE=${CMAKE_BINARY_DIR}/CMakeCache.txt")
endif() endif()
if(LAMMPS_MACHINE) if(LAMMPS_MACHINE)
# convert from '_machine' to 'machine' # convert from '_machine' to 'machine'
@ -27,20 +27,43 @@ if (Python_EXECUTABLE)
list(APPEND PYTHON_TEST_ENVIRONMENT LAMMPS_MACHINE_NAME=${LAMMPS_MACHINE_NAME}) list(APPEND PYTHON_TEST_ENVIRONMENT LAMMPS_MACHINE_NAME=${LAMMPS_MACHINE_NAME})
endif() endif()
if(ENABLE_COVERAGE)
find_program(COVERAGE_BINARY coverage)
find_package_handle_standard_args(COVERAGE DEFAULT_MSG COVERAGE_BINARY)
if(COVERAGE_FOUND)
set(PYTHON_TEST_RUNNER ${Python_EXECUTABLE} -u ${COVERAGE_BINARY} run --parallel-mode --include=${LAMMPS_PYTHON_DIR}/lammps.py --omit=${LAMMPS_PYTHON_DIR}/install.py)
else()
set(PYTHON_TEST_RUNNER ${Python_EXECUTABLE} -u)
endif()
else()
set(PYTHON_TEST_RUNNER ${Python_EXECUTABLE})
endif()
add_test(NAME PythonOpen add_test(NAME PythonOpen
COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/python-open.py -v COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-open.py -v
WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH})
set_tests_properties(PythonOpen PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") set_tests_properties(PythonOpen PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}")
add_test(NAME PythonCommands add_test(NAME PythonCommands
COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/python-commands.py -v COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-commands.py -v
WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH})
set_tests_properties(PythonCommands PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") set_tests_properties(PythonCommands PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}")
add_test(NAME PythonNumpy add_test(NAME PythonNumpy
COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/python-numpy.py -v COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-numpy.py -v
WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH})
set_tests_properties(PythonNumpy PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") set_tests_properties(PythonNumpy PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}")
add_test(NAME PythonCapabilities
COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-capabilities.py -v
WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH})
set_tests_properties(PythonCapabilities PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}")
add_test(NAME PythonPyLammps
COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-pylammps.py -v
WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH})
set_tests_properties(PythonPyLammps PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}")
else() else()
message(STATUS "Skipping Tests for the LAMMPS Python Module: no suitable Python interpreter") message(STATUS "Skipping Tests for the LAMMPS Python Module: no suitable Python interpreter")
endif() endif()

View File

@ -0,0 +1,62 @@
import sys,os,unittest
from lammps import lammps
class PythonCapabilities(unittest.TestCase):
def setUp(self):
machine = None
if 'LAMMPS_MACHINE_NAME' in os.environ:
machine=os.environ['LAMMPS_MACHINE_NAME']
self.lmp = lammps(name=machine, cmdargs=['-nocite', '-log','none', '-echo','screen'])
if 'LAMMPS_CMAKE_CACHE' in os.environ:
self.cmake_cache = {}
with open(os.environ['LAMMPS_CMAKE_CACHE'], 'r') as f:
for line in f:
line = line.strip()
if not line or line.startswith('#') or line.startswith('//'): continue
parts = line.split('=')
key, value_type = parts[0].split(':')
if len(parts) > 1:
value = parts[1]
if value_type == "BOOL":
value = (value.upper() == "ON")
else:
value = None
self.cmake_cache[key] = value
def tearDown(self):
del self.lmp
def test_version(self):
self.assertGreaterEqual(self.lmp.version(), 20200824)
def test_has_gzip_support(self):
self.assertEqual(self.lmp.has_gzip_support, self.cmake_cache['WITH_GZIP'])
def test_has_png_support(self):
self.assertEqual(self.lmp.has_png_support, self.cmake_cache['WITH_PNG'])
def test_has_jpeg_support(self):
self.assertEqual(self.lmp.has_jpeg_support, self.cmake_cache['WITH_JPEG'])
def test_has_ffmpeg_support(self):
self.assertEqual(self.lmp.has_ffmpeg_support, self.cmake_cache['WITH_FFMPEG'])
def test_installed_packages(self):
installed_packages = self.lmp.installed_packages
selected_packages = [key[4:] for key in self.cmake_cache.keys() if not key.startswith('PKG_CONFIG') and key.startswith('PKG_') and self.cmake_cache[key]]
for pkg in selected_packages:
self.assertIn(pkg, installed_packages)
def test_has_style(self):
self.assertTrue(self.lmp.has_style('pair', 'lj/cut'))
self.assertFalse(self.lmp.has_style('pair', 'lennard_jones'))
def test_available_styles(self):
pairs = self.lmp.available_styles('pair')
self.assertIn('lj/cut', pairs)
if __name__ == "__main__":
unittest.main()

View File

@ -43,46 +43,46 @@ create_atoms 1 single &
############################## ##############################
def testFile(self): def testFile(self):
"""Test reading commands from a file""" """Test reading commands from a file"""
natoms = int(self.lmp.get_natoms()) natoms = self.lmp.get_natoms()
self.assertEqual(natoms,0) self.assertEqual(natoms,0)
self.lmp.file(self.demo_file) self.lmp.file(self.demo_file)
natoms = int(self.lmp.get_natoms()) natoms = self.lmp.get_natoms()
self.assertEqual(natoms,1) self.assertEqual(natoms,1)
self.lmp.file(self.cont_file) self.lmp.file(self.cont_file)
natoms = int(self.lmp.get_natoms()) natoms = self.lmp.get_natoms()
self.assertEqual(natoms,2) self.assertEqual(natoms,2)
def testNoFile(self): def testNoFile(self):
"""Test (not) reading commands from no file""" """Test (not) reading commands from no file"""
self.lmp.file(None) self.lmp.file(None)
natoms = int(self.lmp.get_natoms()) natoms = self.lmp.get_natoms()
self.assertEqual(natoms,0) self.assertEqual(natoms,0)
def testCommand(self): def testCommand(self):
"""Test executing individual commands""" """Test executing individual commands"""
natoms = int(self.lmp.get_natoms()) natoms = self.lmp.get_natoms()
self.assertEqual(natoms,0) self.assertEqual(natoms,0)
cmds = self.demo_input.splitlines() cmds = self.demo_input.splitlines()
for cmd in cmds: for cmd in cmds:
self.lmp.command(cmd) self.lmp.command(cmd)
natoms = int(self.lmp.get_natoms()) natoms = self.lmp.get_natoms()
self.assertEqual(natoms,1) self.assertEqual(natoms,1)
def testCommandsList(self): def testCommandsList(self):
"""Test executing commands from list of strings""" """Test executing commands from list of strings"""
natoms = int(self.lmp.get_natoms()) natoms = self.lmp.get_natoms()
self.assertEqual(natoms,0) self.assertEqual(natoms,0)
cmds = self.demo_input.splitlines()+self.cont_input.splitlines() cmds = self.demo_input.splitlines()+self.cont_input.splitlines()
self.lmp.commands_list(cmds) self.lmp.commands_list(cmds)
natoms = int(self.lmp.get_natoms()) natoms = self.lmp.get_natoms()
self.assertEqual(natoms,2) self.assertEqual(natoms,2)
def testCommandsString(self): def testCommandsString(self):
"""Test executing block of commands from string""" """Test executing block of commands from string"""
natoms = int(self.lmp.get_natoms()) natoms = self.lmp.get_natoms()
self.assertEqual(natoms,0) self.assertEqual(natoms,0)
self.lmp.commands_string(self.demo_input+self.cont_input) self.lmp.commands_string(self.demo_input+self.cont_input)
natoms = int(self.lmp.get_natoms()) natoms = self.lmp.get_natoms()
self.assertEqual(natoms,2) self.assertEqual(natoms,2)
############################## ##############################

View File

@ -1,7 +1,14 @@
import sys,os,unittest import sys,os,unittest
from lammps import lammps, LMP_STYLE_GLOBAL, LMP_STYLE_LOCAL, LMP_STYLE_ATOM, LMP_TYPE_VECTOR, LMP_TYPE_SCALAR, LMP_TYPE_ARRAY from lammps import lammps, LAMMPS_INT, LMP_STYLE_GLOBAL, LMP_STYLE_LOCAL, LMP_STYLE_ATOM, LMP_TYPE_VECTOR, LMP_TYPE_SCALAR, LMP_TYPE_ARRAY
from ctypes import c_void_p from ctypes import c_void_p
try:
import numpy
NUMPY_INSTALLED = True
except ImportError:
NUMPY_INSTALLED = False
@unittest.skipIf(not NUMPY_INSTALLED, "numpy is not available")
class PythonNumpy(unittest.TestCase): class PythonNumpy(unittest.TestCase):
def setUp(self): def setUp(self):
machine = None machine = None
@ -25,7 +32,7 @@ class PythonNumpy(unittest.TestCase):
self.lmp.command("create_atoms 1 single 1.0 1.0 1.0") self.lmp.command("create_atoms 1 single 1.0 1.0 1.0")
self.lmp.command("create_atoms 1 single 1.0 1.0 1.5") self.lmp.command("create_atoms 1 single 1.0 1.0 1.5")
self.lmp.command("compute coordsum all reduce sum x y z") self.lmp.command("compute coordsum all reduce sum x y z")
natoms = int(self.lmp.get_natoms()) natoms = self.lmp.get_natoms()
self.assertEqual(natoms,2) self.assertEqual(natoms,2)
values = self.lmp.numpy.extract_compute("coordsum", LMP_STYLE_GLOBAL, LMP_TYPE_VECTOR) values = self.lmp.numpy.extract_compute("coordsum", LMP_STYLE_GLOBAL, LMP_TYPE_VECTOR)
self.assertEqual(len(values), 3) self.assertEqual(len(values), 3)
@ -43,7 +50,7 @@ class PythonNumpy(unittest.TestCase):
self.lmp.command("create_atoms 1 single 1.0 1.0 1.0") self.lmp.command("create_atoms 1 single 1.0 1.0 1.0")
self.lmp.command("create_atoms 1 single 1.0 1.0 1.5") self.lmp.command("create_atoms 1 single 1.0 1.0 1.5")
self.lmp.command("compute ke all ke/atom") self.lmp.command("compute ke all ke/atom")
natoms = int(self.lmp.get_natoms()) natoms = self.lmp.get_natoms()
self.assertEqual(natoms,2) self.assertEqual(natoms,2)
values = self.lmp.numpy.extract_compute("ke", LMP_STYLE_ATOM, LMP_TYPE_VECTOR) values = self.lmp.numpy.extract_compute("ke", LMP_STYLE_ATOM, LMP_TYPE_VECTOR)
self.assertEqual(len(values), 2) self.assertEqual(len(values), 2)
@ -66,5 +73,67 @@ class PythonNumpy(unittest.TestCase):
# TODO # TODO
pass pass
def testExtractAtomDeprecated(self):
self.lmp.command("units lj")
self.lmp.command("atom_style atomic")
self.lmp.command("atom_modify map array")
self.lmp.command("region box block 0 2 0 2 0 2")
self.lmp.command("create_box 1 box")
x = [
1.0, 1.0, 1.0,
1.0, 1.0, 1.5
]
types = [1, 1]
self.assertEqual(self.lmp.create_atoms(2, id=None, type=types, x=x), 2)
nlocal = self.lmp.extract_global("nlocal", LAMMPS_INT)
self.assertEqual(nlocal, 2)
ident = self.lmp.numpy.extract_atom_iarray("id", nlocal, dim=1)
self.assertEqual(len(ident), 2)
ntypes = self.lmp.extract_global("ntypes", LAMMPS_INT)
self.assertEqual(ntypes, 1)
x = self.lmp.numpy.extract_atom_darray("x", nlocal, dim=3)
v = self.lmp.numpy.extract_atom_darray("v", nlocal, dim=3)
self.assertEqual(len(x), 2)
self.assertTrue((x[0] == (1.0, 1.0, 1.0)).all())
self.assertTrue((x[1] == (1.0, 1.0, 1.5)).all())
self.assertEqual(len(v), 2)
def testExtractAtom(self):
self.lmp.command("units lj")
self.lmp.command("atom_style atomic")
self.lmp.command("atom_modify map array")
self.lmp.command("region box block 0 2 0 2 0 2")
self.lmp.command("create_box 1 box")
x = [
1.0, 1.0, 1.0,
1.0, 1.0, 1.5
]
types = [1, 1]
self.assertEqual(self.lmp.create_atoms(2, id=None, type=types, x=x), 2)
nlocal = self.lmp.extract_global("nlocal")
self.assertEqual(nlocal, 2)
ident = self.lmp.numpy.extract_atom("id")
self.assertEqual(len(ident), 2)
ntypes = self.lmp.extract_global("ntypes")
self.assertEqual(ntypes, 1)
x = self.lmp.numpy.extract_atom("x")
v = self.lmp.numpy.extract_atom("v")
self.assertEqual(len(x), 2)
self.assertTrue((x[0] == (1.0, 1.0, 1.0)).all())
self.assertTrue((x[1] == (1.0, 1.0, 1.5)).all())
self.assertEqual(len(v), 2)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -0,0 +1,88 @@
import sys,os,unittest
from lammps import PyLammps
class PythonPyLammps(unittest.TestCase):
def setUp(self):
machine = None
if 'LAMMPS_MACHINE_NAME' in os.environ:
machine=os.environ['LAMMPS_MACHINE_NAME']
self.pylmp = PyLammps(name=machine, cmdargs=['-nocite', '-log','none', '-echo', 'screen'])
self.pylmp.units("lj")
self.pylmp.atom_style("atomic")
self.pylmp.atom_modify("map array")
if 'LAMMPS_CMAKE_CACHE' in os.environ:
self.cmake_cache = {}
with open(os.environ['LAMMPS_CMAKE_CACHE'], 'r') as f:
for line in f:
line = line.strip()
if not line or line.startswith('#') or line.startswith('//'): continue
parts = line.split('=')
key, value_type = parts[0].split(':')
if len(parts) > 1:
value = parts[1]
if value_type == "BOOL":
value = (value.upper() == "ON")
else:
value = None
self.cmake_cache[key] = value
def tearDown(self):
self.pylmp.close()
del self.pylmp
def test_version(self):
self.assertGreaterEqual(self.pylmp.version(), 20200824)
def test_create_atoms(self):
self.pylmp.region("box block", 0, 2, 0, 2, 0, 2)
self.pylmp.create_box(1, "box")
x = [
1.0, 1.0, 1.0,
1.0, 1.0, 1.5
]
types = [1, 1]
self.assertEqual(self.pylmp.lmp.create_atoms(2, id=None, type=types, x=x), 2)
self.assertEqual(self.pylmp.system.natoms, 2)
self.assertEqual(len(self.pylmp.atoms), 2)
self.assertEqual(self.pylmp.atoms[0].position, tuple(x[0:3]))
self.assertEqual(self.pylmp.atoms[1].position, tuple(x[3:6]))
self.assertEqual(self.pylmp.last_run, None)
def test_write_script(self):
outfile = 'in.test_write_script'
self.pylmp.write_script(outfile)
self.assertTrue(os.path.exists(outfile))
os.remove(outfile)
def test_runs(self):
self.pylmp.lattice("fcc", 0.8442),
self.pylmp.region("box block", 0, 4, 0, 4, 0, 4)
self.pylmp.create_box(1, "box")
self.pylmp.create_atoms(1, "box")
self.pylmp.mass(1, 1.0)
self.pylmp.velocity("all create", 1.44, 87287, "loop geom")
self.pylmp.pair_style("lj/cut", 2.5)
self.pylmp.pair_coeff(1, 1, 1.0, 1.0, 2.5)
self.pylmp.neighbor(0.3, "bin")
self.pylmp.neigh_modify("delay 0 every 20 check no")
self.pylmp.fix("1 all nve")
self.pylmp.variable("fx atom fx")
self.pylmp.run(10)
self.assertEqual(len(self.pylmp.runs), 1)
self.assertEqual(self.pylmp.last_run, self.pylmp.runs[0])
self.assertEqual(len(self.pylmp.last_run.thermo.Step), 2)
self.assertEqual(len(self.pylmp.last_run.thermo.Temp), 2)
self.assertEqual(len(self.pylmp.last_run.thermo.E_pair), 2)
self.assertEqual(len(self.pylmp.last_run.thermo.E_mol), 2)
self.assertEqual(len(self.pylmp.last_run.thermo.TotEng), 2)
self.assertEqual(len(self.pylmp.last_run.thermo.Press), 2)
if __name__ == "__main__":
unittest.main()