PyLammps is a Python wrapper class for LAMMPS which can be created
on its own or use an existing lammps Python object. It creates a simpler,
more “pythonic” interface to common LAMMPS functionality, in contrast to
lammps.py wrapper for the C-style LAMMPS library interface which
is written using Python ctypes. The
is discussed on the Python library doc page.
Unlike the flat
ctypes interface, PyLammps exposes a discoverable
API. It no longer requires knowledge of the underlying C++ code
implementation. Finally, the
IPyLammps wrapper builds on top of
PyLammps and adds some additional features for
IPython integration into Jupyter notebooks,
e.g. for embedded visualization output from dump style image.
direct memory access to native C++ data
provides functions to send and receive data to LAMMPS
requires knowledge of how LAMMPS internally works (C pointers, etc)
higher-level abstraction built on top of original ctypes interface
manipulation of Python objects
communication with LAMMPS is hidden from API user
shorter, more concise Python
better IPython integration, designed for quick prototyping
PyLammps is part of the lammps Python package. To install it simply install that package into your current Python installation with:
Recompiling the shared library requires re-installing the Python package
You can use virtualenv to create a custom Python environment specifically tuned for your workflow.
isolation of your system Python installation from your development installation
installation can happen in your user directory without root access (useful for HPC clusters)
installing packages through pip allows you to get newer versions of packages than e.g., through apt-get or yum package managers (and without root access)
you can even install specific old versions of a package if necessary
Prerequisite (e.g. on Ubuntu)
apt-get install python-virtualenv
# create virtualenv named 'testing' virtualenv $HOME/python/testing # activate 'testing' environment source $HOME/python/testing/bin/activate
Now configure and compile the LAMMPS shared library as outlined above. When using CMake and the shared library has already been build, you need to re-run CMake to update the location of the python executable to the location in the virtual environment with:
cmake . -DPYTHON_EXECUTABLE=$(which python) # install LAMMPS package in virtualenv (testing) make install-python # install other useful packages (testing) pip install matplotlib jupyter mpi4py ... # return to original shell (testing) deactivate
To create a PyLammps object you need to first import the class from the lammps module. By using the default constructor, a new lammps instance is created.
from lammps import PyLammps L = PyLammps()
You can also initialize PyLammps on top of this existing lammps object:
from lammps import lammps, PyLammps lmp = lammps() L = PyLammps(ptr=lmp)
Sending a LAMMPS command with the existing library interfaces is done using the command method of the lammps object instance.
For instance, let’s take the following LAMMPS command:
region box block 0 10 0 5 -0.5 0.5
In the original interface this command can be executed with the following Python code if L was a lammps instance:
L.command("region box block 0 10 0 5 -0.5 0.5")
With the PyLammps interface, any command can be split up into arbitrary parts separated by white-space, passed as individual arguments to a region method.
L.region("box block", 0, 10, 0, 5, -0.5, 0.5)
Note that each parameter is set as Python literal floating-point number. In the PyLammps interface, each command takes an arbitrary parameter list and transparently merges it to a single command string, separating individual parameters by white-space.
The benefit of this approach is avoiding redundant command calls and easier parameterization. In the original interface parameterization needed to be done manually by creating formatted strings.
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.
L.region("box block", xlo, xhi, ylo, yhi, zlo, zhi)
In addition to dispatching commands directly through the PyLammps object, it also provides several properties which allow you to query the system state.
Is a dictionary describing the system such as the bounding box or number of atoms
- L.system.xlo, L.system.xhi
bounding box limits along x-axis
- L.system.ylo, L.system.yhi
bounding box limits along y-axis
- L.system.zlo, L.system.zhi
bounding box limits along z-axis
configuration of communication subsystem, such as the number of threads or processors
number of threads used by each LAMMPS process
number of MPI processes used by LAMMPS
List of fixes in the current system
List of active computes in the current system
List of active dumps in the current system
List of groups present in the current system
LAMMPS variables can be both defined and accessed via the PyLammps interface.
To define a variable you can use the variable command:
L.variable("a index 2")
A dictionary of all variables is returned by L.variables
you can access an individual variable by retrieving a variable object from the L.variables dictionary by name
a = L.variables['a']
The variable value can then be easily read and written by accessing the value property of this object.
print(a.value) a.value = 4
LAMMPS expressions can be immediately evaluated by using the eval method. The passed string parameter can be any expression containing global thermo values, variables, compute or fix data.
result = L.eval("ke") # kinetic energy result = L.eval("pe") # potential energy result = L.eval("v_t/2.0")
All atoms in the current simulation can be accessed by using the L.atoms list. Each element of this list is an object which exposes its properties (id, type, position, velocity, force, etc.).
# access first atom L.atoms.id L.atoms.type # access second atom L.atoms.position L.atoms.velocity L.atoms.force
Some properties can also be used to set:
# set position in 2D simulation L.atoms.position = (1.0, 0.0) # set position in 3D simulation L.atoms.position = (1.0, 0.0, 1.)
Each simulation run usually produces thermo output based on system state, computes, fixes or variables. The trajectories of these values can be queried after a run via the L.runs list. This list contains a growing list of run data. The first element is the output of the first run, the second element that of the second run.
L.run(1000) L.runs # data of first 1000 time steps L.run(1000) L.runs # data of second 1000 time steps
Each run contains a dictionary of all trajectories. Each trajectory is accessible through its thermo name:
L.runs.thermo.Step # list of time steps in first run L.runs.thermo.Ke # list of kinetic energy values in first run
Together with matplotlib plotting data out of LAMMPS becomes simple:
import matplotlib.plot as plt steps = L.runs.thermo.Step ke = L.runs.thermo.Ke plt.plot(steps, ke)
Compiling the shared library with C++ exception support 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 you can handle LAMMPS errors through the Python exception handling mechanism.
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.
If the LAMMPS Python package is installed for the same Python interpreter as IPython, you can use PyLammps directly inside of an IPython notebook inside of Jupyter. Jupyter is a powerful integrated development environment (IDE) for many dynamic languages like Python, Julia and others, which operates inside of any web browser. Besides auto-completion and syntax highlighting it allows you to create formatted documents using Markup, mathematical formulas, graphics and animations intermixed with executable Python code. It is a great format for tutorials and showcasing your latest research.
To launch an instance of Jupyter simply run the following command inside your Python environment (this assumes you followed the Quick Start instructions):
Examples of IPython notebooks can be found in the python/examples/pylammps sub-directory. To open these notebooks launch jupyter notebook inside this directory and navigate to one of them. If you compiled and installed a LAMMPS shared library with exceptions, PNG, JPEG and FFMPEG support you should be able to rerun all of these notebooks.
This example showcases how an IPython Notebook can be used to compare a simple LAMMPS simulation of a harmonic dihedral potential to its analytical solution. Four atoms are placed in the simulation and the dihedral potential is applied on them using a datafile. Then one of the atoms is rotated along the central axis by setting its position from Python, which changes the dihedral angle.
phi = [d \* math.pi / 180 for d in range(360)] pos = [(1.0, math.cos(p), math.sin(p)) for p in phi] pe =  for p in pos: L.atoms.position = p L.run(0) pe.append(L.eval("pe"))
By evaluating the potential energy for each position we can verify that trajectory with the analytical formula. To compare both solutions, we plot both trajectories over each other using matplotlib, which embeds the generated plot inside the IPython notebook.
This second example shows how to use PyLammps to create a 2D Monte Carlo Relaxation simulation, computing and plotting energy terms and even embedding video output.
Initially, a 2D system is created in a state with minimal energy.
It is then disordered by moving each atom by a random delta.
random.seed(27848) deltaperturb = 0.2 for i in range(L.system.natoms): x, y = L.atoms[i].position dx = deltaperturb \* random.uniform(-1, 1) dy = deltaperturb \* random.uniform(-1, 1) L.atoms[i].position = (x+dx, y+dy) L.run(0)
Finally, the Monte Carlo algorithm is implemented in Python. It continuously moves random atoms by a random delta and only accepts certain moves.
estart = L.eval("pe") elast = estart naccept = 0 energies = [estart] niterations = 3000 deltamove = 0.1 kT = 0.05 natoms = L.system.natoms for i in range(niterations): iatom = random.randrange(0, natoms) current_atom = L.atoms[iatom] x0, y0 = current_atom.position dx = deltamove \* random.uniform(-1, 1) dy = deltamove \* random.uniform(-1, 1) current_atom.position = (x0+dx, y0+dy) L.run(1, "pre no post no") e = L.eval("pe") energies.append(e) if e <= elast: naccept += 1 elast = e elif random.random() <= math.exp(natoms\*(elast-e)/kT): naccept += 1 elast = e else: current_atom.position = (x0, y0)
The energies of each iteration are collected in a Python list and finally plotted using matplotlib.
The IPython notebook also shows how to use dump commands and embed video files inside of the IPython notebook.
PyLammps can be run in parallel using mpi4py. This python package can be installed using
pip install mpi4py
The following is a short example which reads in an existing LAMMPS input file and executes it in parallel. You can find in.melt in the examples/melt folder.
from mpi4py import MPI from lammps import PyLammps L = PyLammps() L.file("in.melt") if MPI.COMM_WORLD.rank == 0: print("Potential energy: ", L.eval("pe")) MPI.Finalize()
To run this script (melt.py) in parallel using 4 MPI processes we invoke the following mpirun command:
mpirun -np 4 python melt.py
Any command must be executed by all MPI processes. However, evaluations and querying the system state is only available on rank 0.
If you find this Python interface useful, please feel free to provide feedback and ideas on how to improve it to Richard Berger (firstname.lastname@example.org). We also want to encourage people to write tutorial style IPython notebooks showcasing LAMMPS usage and maybe their latest research results.