Previous Section - LAMMPS WWW Site - LAMMPS Documentation - LAMMPS Commands - Next Section

8. Modifying & extending LAMMPS

LAMMPS is designed in a modular fashion so as to be easy to modify and extend with new functionality. In this section, changes and additions users can make are listed along with some minimal instructions. Realistically, the best way to add a new feature is to find a similar feature in LAMMPS and look at the corresponding source and header files to figure out what it does. You will need some knowledge of C++ to be able to understand the hi-level structure of LAMMPS and its class organization, but functions (class methods) that do actual computations are written in vanilla C-style code and operate on simple C-style data structures (vectors and arrays).

Most of the new features described in this section require you to write a new C++ class (except for dump, thermo, and variable options, described below, where you can make small edits to existing files). Creating a new class requires 2 files, a source code file (*.cpp) and a header file (*.h). Their contents are briefly discussed below. Enabling LAMMPS to invoke the new class is as simple as adding two definition lines to the style_user.h file, in the same syntax as the existing LAMMPS classes are defined in the style.h file.

The power of C++ and its object-orientation is that usually, all the code and variables needed to define the new feature are contained in the 2 files you write, and thus shouldn't make the rest of the code more complex or cause side-effect bugs.

Here is a concrete example. Suppose you write 2 files pair_foo.cpp and pair_foo.h that define a new class PairFoo that computes pairwise potentials described in the classic 1997 paper by Foo, et. al. If you wish to invoke those potentials in a LAMMPS input script with a command like

pair_style foo 0.1 3.5 

you simply need to put your 2 files in the LAMMPS src directory, add 2 lines to the style_user.h file, and re-make the code.

The first line added to style_user.h would be

PairStyle(foo,PairFoo) 

in the #ifdef PairClass section, where "foo" is the style keyword in the pair_style command, and PairFoo is the class name in your C++ files.

The 2nd line added to style_user.h would be

#include "pair_foo.h" 

in the #ifdef PairInclude section, where pair_foo.h is the name of your new include file.

When you re-make LAMMPS, your new pairwise potential becomes part of the executable and can be invoked with a pair_style command like the example above. Arguments like 0.1 and 3.5 can be defined and processed by your new class.

Note that if you are using Makefile.list instead of Makefile to build LAMMPS, you will need to explicitly add the names of your new .cpp and .h file to Makefile.list.

Here is a list of the kinds of new features that can be added in this way. The dump and thermo options do not typically require new styles; LAMMPS can simply be recompiled after new code is added to dump_custom.cpp or thermo_custom.cpp.

As illustrated by the pairwise example, these options are referred to in the LAMMPS documentation as the "style" of a particular command.

The instructions below for each category will list the header file for the parent class that these styles are sub-classes of. Public variables in that file are ones used and set by the sub-classes which are also used by the parent class. Sometimes they are also used by the rest of LAMMPS. Virtual functions in the header file which are set = 0 are ones you must define in your new class to give it the functionality LAMMPS expects. Virtual functions that are not set to 0 are functions you can optionally define.

Here are some additional guidelines for modifying LAMMPS and adding new functionality:

Think about whether what you want to do would be better as a pre- or post-processing step. Many computations are more easily and more quickly done that way.

Don't do anything within the timestepping of a run that isn't parallel. E.g. don't accumulate a bunch of data on a single processor and analyze it. You run the risk of seriously degrading the parallel efficiency.

If your new feature reads arguments or writes output, make sure you follow the unit conventions discussed by the units command.

If you add something you think is truly useful and doesn't impact LAMMPS performance when it isn't used, send me an email. We might be interested in adding it to the LAMMPS distribution.


Pairwise potentials

All classes that compute pairwise interactions are sub-classes of the Pair class. See the pair.h file for a list of methods this class defines.

Pair_lj_cut.cpp and pair_lj_cut.h are the simplest example of a Pair class. They implement the lj/cut style of the pair_style command.

Here is a brief description of the class methods in pair.h:

compute the workhorse routine that computes the pairwise interactions
settings reads the input script line with any arguments you define
coeff set coefficients for one i,j type pair
init_one perform initialization for one i,j type pair
write & read_restart write/read i,j pair coeffs to restart files
write & read_restart_settings write/read global settings to restart files
single force and energy of a single pairwise interaction between 2 atoms
compute_inner/middle/outer versions of compute used by rRESPA

The inner/middle/outer routines are optional. Only a few of the pairwise potentials use these in conjunction with rRESPA as set by the run_style command.


Bond, angle, dihedral, improper potentials

All classes that compute molecular interactions are sub-classes of the Bond, Angle, Dihedral, and Improper classes. See the bond.h, angle.h, dihedral.h, and improper.h file for a list of methods these classes defines.

Bond_harmonic.cpp and bond_harmonic.h are the simplest example of a Bond class. Ditto for the harmonic forms of the angle, dihedral, and improper style commands. The bond_harmonic files implement the harmonic style of the bond_style command.

Here is a brief description of the class methods in bond.h, angle.h, etc:

compute the workhorse routine that computes the molecular interactions
coeff set coefficients for one bond type
equilibrium_distance length of bond, used by SHAKE
write & read_restart writes/reads coeffs to restart files
single force and energy of a single bond

Dump options

There are several classes that print dump files (snapshots of atoms) that are sub-classes of the Dump class. These include the dump_atom.cpp, dump_bond.cpp, and dump_custom.cpp files.

New dump classes can be added, but it is typically simpler to modify the DumpCustom class contained in the dump_custom.cpp file. See the dump command and its custom style for a list of what atom information can already be dumped by DumpCustom. If the attribute you want to dump is not in the list, or if you define a new atom style with new attributes (e.g. atoms that store their own magnetic moment), here is how to dump it out in a snapshot file:

Search the dump_custom.cpp and dump_custom.h files for the word "customize". It appears in roughly half a dozen locations. In each of the locations you can add a bit of code that will extend the DumpCustom class to enable it to dump a new quantity. E.g. you will add a keyword, add an if test, add a new small method that packs the requested data into a buffer, etc. For the latter, you can perform a modest amount of computation in this method; see the pack_xs() function for an example.

If desired, a dump custom option can also compute more complicated quantities by invoking a fix that computed quantities at the end of a timestep (should be the same timestep the dump is invoked on). See the ENERGY, CENTRO, and stress options (SXX, SYY, etc) in dump_custom.cpp for examples.

When you re-make LAMMPS, your new option should now be useable via the dump custom command.


Thermodynamic output options

There is only one class that computes and prints thermodynamic information to the screen and log file, although the thermo_style command treats its options as styles.

There are several styles defined in thermo.cpp: "one", "multi", and "granular". There is also a flexible "custom" style which allows you to specify what quantities will be printed each timestep where thermodynamics is computed. See the thermo_style command for a list of pre-defined quantities.

Here is how you can extend the thermo output capabilities. Search the thermo.cpp and thermo.h files for the word "customize" which will tell you where to make these additions. Note that fixes can also print-out thermodynamic quantities via the fix_modify command, so you do not need to modify thermo.cpp to print fix information.

If you want to create a new style (like "one" or "granular") that prints a collection of pre-defined quantities, you add a few lines that define the new style to thermo.cpp. First, add a #DEFINE line at the top of the file which lists the quantities to print. Then add the style name you have chosen to the if test in the constructor to copy the defined string to the line variable.

You can also add new quantities to the custom list. Add your new keyword to the if test in the parse_fields() function where the call to addfield() specifies the text string (8 character max) that will be printed with the quantity, the function that will compute it, and the data type (INT,FLOAT) of the quantity. Then at the bottom of the file, add a function compute_*() which computes the quantity you wish to print. The function assigns the quantity to the variable "dvalue" if it is a floating-point quantity, or to "ivalue" if it is an integer. See the other compute_*() functions for examples of how various quantities can be accessed, computed, summed across processors, normalized as per-atom values, etc. Also, if it makes sense to allow the quantity to be stored in a variable in the input script, add a couple of lines to the compute_value() function that is called when a variable is evaluated. Finally, add a prototype for your new compute method to thermo.h.


Temperature computation options

All classes that compute the temperature of the system are sub-classes of the Temperature class. See the temperature.h file for a list of methods these classes defines. Temperatures are computed by LAMMPS when velocities are set, when thermodynamics are computed, and when temperature is controlled by various thermostats like the fix nvt of fix langevin commands.

Temp_full.cpp and temp_full.h are the simplest example of a Temperature class. They implement the full style of the temperature command.

Here is a brief description of the class methods in temperature.h:

init setup the temperature computation
compute compute and return temperature

Region geometry options

All classes that define geometric regions are sub-classes of the Region class. See the region.h file for a list of methods these classes defines. Regions are used elsewhere in LAMMPS to group atoms, delete atoms to create a void, insert atoms in a specified region, etc.

Region_sphere.cpp and region_sphere.h are the simplest example of a Region class. They implement the sphere style of the region command.

Here is a brief description of the single class method required:

match determine whether a point is in the region

Fix options

In LAMMPS, a "fix" is any operation that is computed during timestepping that alters some property of the system. Essentially everything that happens during a simulation besides force computation, neighbor list manipulation, and output, is a "fix". This includes time integration (update of velocity and coordinates), force constraints (SHAKE or walls), and diagnostics (compute a diffusion coefficient). See the fix.h file for a list of methods these classes defines.

There are dozens of fix options in LAMMPS; choose one as a template that is similar to what you want to implement. They can be as simple as zeroing out forces (see fix enforce2d which corresponds to the enforce2d style) or as complicated as applying SHAKE constraints on bonds and angles (see fix shake which corresponds to the shake style) which involves many extra computations.

Here is a brief description of the class methods in fix.h:

setmask determines when the fix is called during the timestep
init initialization before a run
setup called immediately before the 1st timestep
initial_integrate called at very beginning of each timestep
pre_exchange called before atom exchange on re-neighboring steps
pre_neighbor called before neighbor list build
post_force called after pair & molecular forces are computed
final_integrate called at end of each timestep
end_of_step called at very end of timestep
write_restart dumps fix info to restart file
restart uses info from restart file to re-initialize the fix
grow_arrays allocate memory for atom-based arrays used by fix
copy_arrays copy atom info when an atom migrates to a new processor
memory_usage report memory used by fix
pack_exchange store atom's data in a buffer
unpack_exchange retrieve atom's data from a buffer
pack_restart store atom's data for writing to restart file
unpack_restart retrieve atom's data from a restart file buffer
size_restart size of atom's data
maxsize_restart max size of atom's data
initial_integrate_respa same as initial_integrate, but for rRESPA
post_force_respa same as post_force, but for rRESPA
final_integrate_respa same as final_integrate, but for rRESPA
pack_comm pack a buffer to communicate a per-atom quantity
unpack_comm unpack a buffer to communicate a per-atom quantity
pack_reverse_comm pack a buffer to reverse communicate a per-atom quantity
unpack_reverse_comm unpack a buffer to reverse communicate a per-atom quantity
thermo_fields define quantities for thermodynamic output
thermo_compute compute thermodynamic quantities

Typically, only a small fraction of these methods are defined for a particular fix. Setmask is mandatory, as it determines when the fix will be invoked during the timestep. Fixes that perform time integration (nve, nvt, npt) implement initial_integrate and final_integrate to perform velocity Verlet updates. Fixes that constrain forces implement post_force. Fixes that perform diagnostics typically implement end_of_step.

If the fix needs to store information for each atom that persists from timestep to timestep, it can manage that memory and migrate it with the atoms as they move from processors to processor by implementing the grow_arrays, copy_arrays, pack_exchange, and unpack_exchange methods. Similary, the pack_restart and unpack_restart methods can be implemented to store information about the fix in restart files. If you wish a integrator or force constraint fix to work with rRESPA (see the run_style command), the initial_integrate, post_force_integrate, and final_integrate_respa methods can be implemented. The thermo_fields and thermo_compute methods enable a fix to contribute values to thermodynamic output, as printed quantities and/or to be summed to the potential energy of the system.


Atom options

All classes that define an atom style are sub-classes of the Atom class. See the atom.h file for a list of methods these classes defines. The atom style determines what quantities are associated with an atom in a LAMMPS simulation. If one of the existing atom styles does not define all the arrays you need to store with an atom, then a new atom class can be created.

Atom_atomic.cpp and atom_atomic.h are the simplest example of an Atom class. They implement the atomic style of the atom_style command.

Here is a brief description of the class methods in atom.h:

copy copy info for one atom to another atom's array location
pack_comm store an atom's info in a buffer communicated every timestep
unpack_comm retrieve an atom's info from the buffer
pack_reverse store an atom's info in a buffer communicating partial forces
unpack_reverse retrieve an atom's info from the buffer
pack_border store an atom's info in a buffer communicated on neighbor re-builds
unpack_border retrieve an atom's info from the buffer
pack_exchange store all an atom's info to migrate to another processor
unpack_exchange retrieve an atom's info from the buffer

There are also several methods in atom.cpp you will need to augment with information about your new atom class, following the patterns of the other atom styles. These routines are so similar for all classes, that it was simpler to just have one master routine for all classes.

constructor create style variable and atom array ptrs to NULL
destructor free memory for atom arrays
set_style set style variable
check_style check for pure style vs hybrid style
style2arg convert style variables to keywords
grow re-allocate atom arrays to longer lengths
unpack_data parse atom lines from data file
create_one create an individual atom of this style
size_restart number of restart quantities associated with proc's atoms
pack_restart pack atom quantities into a buffer
unpack_restart unpack atom quantities from a buffer
memory_usage memory allocated by atom arrays

Variable options

The variable class stores and evaluates input script variables $a, $b, ... $z, as described in this section. Equal-style variables are defined by an equation that is evaulated each time the variable is used. The equation can include functions, vectors, keywords, and numbers as described in the variable command. The list of valid functions, vectors, and keywords, can be extended by adding a few lines of code to the evaluate() method at the end of the variable.cpp file. Search for the word "customize" to find the correct locations for adding code.

A new function (e.g. foo(arg1,arg2,...)) can be added in the section that starts with the comment

// customize by adding function to this list and to if statement 

A new vector (e.g. q) can be added in the section that starts with the comment

// customize by adding vector to this list and to if statement 

A new keyword (e.g. mysum) can be added in the section that starts with the comment

// customize by adding keyword to this list and to if statement 

Note that keywords supported by the thermo_style custom command are evaluated by the thermo routines, so do not need to be added to variable.cpp.


New top-level commands

It is possible to add a new command to a LAMMPS input script as opposed to adding a new style to an existing command (atom_style, pair_style, fix, etc). For example the create_atoms, read_data, velocity, and run commands are all top-level LAMMPS commands that are listed in the Command section of style.h. When such a command is encountered in the LAMMPS input script, the topmost level of LAMMPS (lammps.cpp) simply creates a class with the corresponding name, invokes the "command" method of the class, and passes it the arguments from the input script. The command method can perform whatever operations it wishes on the LAMMPS data structures.

Thus to add a new command, you simply need to add a *.cpp and *.h file containing a single class:

command operations performed by the new command

Of course, the new class can define other methods and variables that it uses internally.


(Foo) Foo, Morefoo, and Maxfoo, J of Classic Potentials, 75, 345 (1997).