"LAMMPS WWW Site"_lws - "LAMMPS Documentation"_ld - "LAMMPS Commands"_lc :c

:link(lws,http://lammps.sandia.gov)
:link(ld,Manual.html)
:link(lc,Section_commands.html#comm)

:line

PyLammps Tutorial :h1

<!-- RST
.. contents::

END_RST -->

Overview :h2

PyLammps is a Python wrapper class which can be created on its own or use an
existing lammps Python object. It creates a simpler, Python-like interface to
common LAMMPS functionality. Unlike the original flat C-types interface, it
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 IPython
notebooks, e.g. for embedded visualization output from dump/image.

Comparison of lammps and PyLammps interfaces :h3

lammps.lammps :h4

uses C-Types
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) :ul

lammps.PyLammps :h4

higher-level abstraction built on top of original C-Types interface
manipulation of Python objects
communication with LAMMPS is hidden from API user 
shorter, more concise Python
better IPython integration, designed for quick prototyping :ul


Quick Start :h2

System-wide Installation :h3

Step 1: Building LAMMPS as a shared library :h4

To use LAMMPS inside of Python it has to be compiled as shared library. This
library is then loaded by the Python interface. In this example, we use the
Make.py utility to create a Makefile with C++ exceptions, PNG, JPEG and FFMPEG
output support enabled. Finally, we also enable the MOLECULE package and compile
using the generated {auto} Makefile.

cd $LAMMPS_DIR/src :pre

# generate custom Makefile
python Make.py -jpg -png -s ffmpeg exceptions -m mpi -a file :pre

# add packages if necessary
make yes-MOLECULE :pre

# compile shared library using Makefile
make mode=shlib auto :pre

Step 2: Installing the LAMMPS Python package :h4

PyLammps is part of the lammps Python package. To install it simply install
that package into your current Python installation.

cd $LAMMPS_DIR/python
python install.py :pre

NOTE: Recompiling the shared library requires reinstalling the Python package


Installation inside of a virtualenv :h3

You can use virtualenv to create a custom Python environment specifically tuned
for your workflow.

Benefits of using a virtualenv :h4

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 :ul

[Prerequisite (e.g. on Ubuntu)]

apt-get install python-virtualenv :pre

Creating a virtualenv with lammps installed :h4

# create virtualenv name 'testing' :pre

# activate 'testing' environment
source testing/bin/activate :pre

# install LAMMPS package in virtualenv
(testing) cd $LAMMPS_DIR/python
(testing) python install.py :pre

# install other useful packages
(testing) pip install matplotlib jupyter mpi4py :pre

... :pre

# return to original shell
(testing) deactivate :pre


Creating a new instance of PyLammps :h2

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() :pre

You can also initialize PyLammps on top of this existing {lammps} object:

from lammps import lammps, PyLammps
lmp = lammps()
L = PyLammps(ptr=lmp) :pre

Commands :h2

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 :pre

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") :pre

With the PyLammps interface, any command can be split up into arbitrary parts
separated by whitespace, passed as individual arguments to a region method.

L.region("box block", 0, 10, 0, 5, -0.5, 0.5) :pre

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 whitespace.

The benefit of this approach is avoiding redundant command calls and easier
parameterization. In the original interface parametrization 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)) :pre

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) :pre

System state :h2

In addition to dispatching commands directly through the PyLammps object, it
also provides several properties which allow you to query the system state.

:dlb

L.system :dt

Is a dictionary describing the system such as the bounding box or number of atoms :dd

L.system.xlo, L.system.xhi :dt

bounding box limits along x-axis :dd

L.system.ylo, L.system.yhi :dt

bounding box limits along y-axis :dd

L.system.zlo, L.system.zhi :dt

bounding box limits along z-axis :dd

L.communication :dt

configuration of communication subsystem, such as the number of threads or processors :dd

L.communication.nthreads :dt

number of threads used by each LAMMPS process :dd

L.communication.nprocs :dt

number of MPI processes used by LAMMPS :dd

L.fixes :dt

List of fixes in the current system :dd

L.computes :dt

List of active computes in the current system :dd

L.dump :dt

List of active dumps in the current system :dd

L.groups :dt

List of groups present in the current system :dd

:dle

Working with LAMMPS variables :h2

LAMMPS variables can be both defined and accessed via the PyLammps interface.

To define a variable you can use the "variable"_variable.html command:

L.variable("a index 2") :pre

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'\] :pre

The variable value can then be easily read and written by accessing the value
property of this object.

print(a.value)
a.value = 4 :pre

Retrieving the value of an arbitrary LAMMPS expressions :h2

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 :pre

result = L.eval("v_t/2.0") :pre

Accessing atom data :h2

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\[0\].id
L.atoms\[0\].type :pre

# access second atom
L.atoms\[1\].position
L.atoms\[1\].velocity
L.atoms\[1\].force :pre

Some properties can also be used to set:

# set position in 2D simulation
L.atoms\[0\].position = (1.0, 0.0) :pre

# set position in 3D simulation
L.atoms\[0\].position = (1.0, 0.0, 1.) :pre

Evaluating thermo data :h2

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\[0\] # data of first 1000 time steps :pre

L.run(1000)
L.runs\[1\] # data of second 1000 time steps :pre

Each run contains a dictionary of all trajectories. Each trajectory is
accessible through its thermo name:

L.runs\[0\].step # list of time steps in first run
L.runs\[0\].ke   # list of kinetic energy values in first run :pre

Together with matplotlib plotting data out of LAMMPS becomes simple:

import matplotlib.plot as plt

steps = L.runs\[0\].step
ke    = L.runs\[0\].ke
plt.plot(steps, ke) :pre

Error handling with PyLammps :h2

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.

IMPORTANT NOTE: 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.

Using PyLammps in IPython notebooks and Jupyter :h2

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):

jupyter notebook :pre

IPyLammps Examples :h2

Examples of IPython notebooks can be found in the python/examples/pylammps
subdirectory. 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.

Validating a dihedral potential :h3

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)\] :pre

pos = \[(1.0, math.cos(p), math.sin(p)) for p in phi\] :pre

pe = \[\]
for p in pos:
    L.atoms\[3\].position = p
    L.run(0)
    pe.append(L.eval("pe")) :pre

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.

:c,image(JPG/pylammps_dihedral.jpg)

Running a Monte Carlo relaxation :h3

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.

:c,image(JPG/pylammps_mc_minimum.jpg)

It is then disordered by moving each atom by a random delta.

random.seed(27848)
deltaperturb = 0.2 :pre

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) :pre

L.run(0) :pre

:c,image(JPG/pylammps_mc_disordered.jpg)

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 :pre

naccept = 0
energies = \[estart\] :pre

niterations = 3000
deltamove = 0.1
kT = 0.05 :pre

natoms = L.system.natoms :pre

for i in range(niterations):
    iatom = random.randrange(0, natoms)
    current_atom = L.atoms\[iatom\] :pre
    
    x0, y0 = current_atom.position :pre
    
    dx = deltamove * random.uniform(-1, 1)
    dy = deltamove * random.uniform(-1, 1) :pre
    
    current_atom.position = (x0+dx, y0+dy) :pre
    
    L.run(1, "pre no post no") :pre
    
    e = L.eval("pe")
    energies.append(e) :pre
    
    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) :pre

The energies of each iteration are collected in a Python list and finally plotted using matplotlib.

:c,image(JPG/pylammps_mc_energies_plot.jpg)

The IPython notebook also shows how to use dump commands and embed video files
inside of the IPython notebook.

Using PyLammps and mpi4py (Experimental) :h2

PyLammps can be run in parallel using mpi4py. This python package can be installed using

pip install mpi4py :pre

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 :pre

L = PyLammps()
L.file("in.melt") :pre

if MPI.COMM_WORLD.rank == 0:
    print("Potential energy: ", L.eval("pe")) :pre

MPI.Finalize() :pre

To run this script (melt.py) in parallel using 4 MPI processes we invoke the
following mpirun command:

mpirun -np 4 python melt.py :pre

IMPORTANT NOTE: Any command must be executed by all MPI processes. However, evaluations and querying the system state is only available on rank 0.

Feedback and Contributing :h2

If you find this Python interface useful, please feel free to provide feedback
and ideas on how to improve it to Richard Berger (richard.berger@temple.edu). We also
want to encourage people to write tutorial style IPython notebooks showcasing LAMMPS usage
and maybe their latest research results. 
