Skip to content

Frequently Asked Questions

Peter Eastman edited this page Jul 24, 2023 · 10 revisions

Frequently Asked Questions (FAQ)

How do periodic boundary conditions work?

Why am I getting an error that says, "Particle position is NaN" or "Energy is NaN"?

Why am I getting an error that says, "No template found for residue"?

Why does it ignore changes I make to a System or Force?

Why am I getting an error that says, "The cutoff distance cannot be greater than half the periodic box size"?

Why am I getting an error about "CUDA_ERROR_UNSUPPORTED_PTX_VERSION"?

How do periodic boundary conditions work?

Periodic boundary conditions are a very common source of questions. Often they take the form of questions like, "Why are water molecules diffusing outside the periodic box?" or, "Why does my protein stick out past the edge of the water box?" Other times they relate to how specific forces apply (or don't apply) periodic boundary conditions. This is a much more complicated subject than it first appears, so let's start at the beginning.

Conceptually speaking, when you run a simulation with periodic boundary conditions, every "particle" really represents an infinite grid of particles repeating through space. If the periodic box is a 10 nm cube and a particle is located at (1, 2, 3), that means there also is a particle at (11, 2, 3), another at (21, 2, 3), another at (-9, 12, 103), and so on. It does not matter which of those positions is listed for the particle. Any one of them represents exactly the same infinite grid of particles.

Next there is a very important point to understand:

Periodic boundary conditions are applied to displacements, not positions.

Again using the example of a 10 nm cube, what is the displacement between two particles at (1, 0, 0) and (9, 0, 0)? Without periodic boundary conditions, the displacement would be (8, 0, 0), but with periodic boundary conditions it is (-2, 0, 0). If the second particle's position were instead (-1, 0, 0) or (99, 0, 0) it would not matter. The displacement would be the same. Periodic boundary conditions are applied while computing forces that depend on the displacements between particles.

Here is another very important point:

Periodic boundary conditions are applied only to nonbonded forces, not bonded ones.

Consider a bond between two particles. Remember that each of them really represents an infinite grid of particles, but that doesn't mean every copy of the first particle is bonded to every copy of the second one. Each copy of the first particle is bonded to one specific copy of the second particle and no other. If a different copy of the second particle somehow manages to come closer than the copy it is bonded to, that doesn't mean the bond should suddenly "break" and transfer to a different copy.

(Actually, it is possible to apply periodic boundary conditions to bonded forces if you really want it. Just call setUsesPeriodicBoundaryConditions() on the force. But this is an unusual thing to do, and is only appropriate in special situations. For example, if you want to simulate an infinite molecule where one end is bonded to the next periodic copy of the other end, you would do it.)

This also applies to constraints and to nonbonded exceptions, which are really a type of bonded force. Nonbonded interactions are usually omitted or reduced between particles that are bonded to each other (1-2, 1-3, and 1-4 interactions). This applies only to particles that are directly bonded, not to other periodic copies of those particles.

A consequence is that you must never break a molecule between two different periodic copies. I said it didn't matter whether a particle was at (9, 0, 0) or (-1, 0, 0). But if it is bonded to a particle at (1, 0, 0) then it does matter. One indicates a bond length of 8, while the other indicates a bond length of 2. Most MD packages follow this same requirement, so usually it is not a problem. But occasionally you may come across a file in which every atom has independently been wrapped to a single periodic box, causing molecules to be broken in half. There are tools that can be used to fix these files, such as the make_molecules_whole() function in MDTraj.

Everything so far has related to running simulations, but now let's talk about saving output files. To the MD engine, a molecule centered at (0, 0, 0) may be identical to one centered at (100, -30, -80), but if you are trying to visualize a trajectory, it is impossible to tell what is going on if different molecules are scattered across many different periodic boxes. For this reason, Context.getState() has an option to wrap all the returned particle positions into a single periodic box. Likewise, reporters do this automatically when writing trajectories for a periodic system. The wrapping is always done to whole molecules. The center of every molecule is in a single box, but some atoms in that molecule may extend outside it. This is why it often appears as if a protein is only partly surrounded by water.

Visualization is further complicated by the following important point:

There is no uniquely defined "periodic box".

For example, if the box width is 10, does that mean the particle positions should all be between 0 and 10? Or between -5 and 5? Both of those conventions are common in different programs. If you load an initial structure that has the protein centered at the origin, then write a trajectory that puts all the molecules between 0 and 10, it will appear that most of the protein is outside the water, and there is a large hole in the water box on the opposite side. Because this is such a common problem, many analysis programs have features specifically to deal with it, such as the image_molecules() function in MDTraj or the autoimage command in Cpptraj. These tools try to intelligently put all particles into a single box with the solutes in the center and water around them.

Why am I getting an error that says, "Particle position is NaN" or "Energy is NaN"?

NaN stands for "not a number". It is how computers represent results of computations that don't have well defined real values, like dividing by zero or taking the square root of a negative number. When you get this error, it generally means your simulation has blown up. That can happen for many reasons, but generally starts from a force or velocity being too large. That causes a particle to jump abruptly, which leads to even larger forces in the next time step, and within a few steps the position of every particle has turned into NaN.

Many types of problems can cause a simulation to blow up. Here are some of the more common ones.

  • The time step is too large. If it is only slightly too large, the simulation may seem to run successfully for a while before blowing up. If it is much too large, it will become unstable immediately.

  • The initial conformation was not energy minimized. For example, if you are using a crystal structure as your starting conformation, the coordinates are probably not at a minimum of the force field and will produce very large forces on the first time step. This can usually be fixed by running a local energy minimization.

  • The periodic box vectors were set incorrectly. For example, this is a common mistake when starting from Amber prmtop and inpcrd files. Both files specify periodic box vectors, and they may be different from each other. If the particle coordinates assume one box size, but the simulation is using a different box size, that can cause severe clashes between particles.

  • Constraints are defined incorrectly. Applying constraints involves inverting a matrix (sometimes explicitly, sometimes only implicitly). If that matrix is poorly conditioned, it will cause the simulation to blow up. A simple cause of this is having two different constraints between the same pair of particles. It also can happen when there are inconsistent constraints that cannot all be satisfied at the same time. Other, more complicated sets of constraints can also lead to a poorly conditioned matrix.

  • Forces have been defined incorrectly. This is most likely to be a problem when you are building the System directly, or adding extra forces to a System. If the forces you apply are too large, that can cause the simulation to blow up. Also, when creating custom forces be careful not to write expressions that may directly produce a NaN (such as by taking the square root of a value that is sometimes negative).

Since so many things can cause these errors, they can be difficult to debug. The first thing to look for is whether the simulation blows up immediately (within the first few time steps), or whether it runs for a long time first. If the problem is in either the initial conformation (such as not being minimized) or the system definition (such as constraints or forces), it will usually blow up immediately. Try running a local energy minimization and see if that fixes it. On the other hand, if your step size is too large, it may run for a while first. In that case, the first thing to try is reducing the step size.

If the error is reproducible rather than intermittent, you can usually track down exactly what is happening. Start by looking for any particles with very large forces on them. Then identify where those forces are coming from. Are they a result of bonded or nonbonded interactions? Can you identify the specific interactions? For example, perhaps two particles are too close together?

Another approach to debugging these errors is to systematically remove things that can cause them. Try removing forces one at a time. Try removing all constraints. Try disabling periodic boundary conditions. With a bit of persistence and patience, you can usually identify the cause.

Why am I getting an error that says, "No template found for residue"?

This error can occur when you call createSystem() on a ForceField. To understand what it means, you need to know a little about how force fields work in OpenMM.

The first step in using a force field to build a System is to identify the type of every atom. Atom types can be very specific. For example, not all carbon atoms are the same. A force field will probably use different parameters for an alpha carbon than for one in an aromatic side chain. Atom types are determined by matching residues to templates. A force field includes template definitions for all the types of residues it knows about. When you call createSystem() it loops over every residue in the Topology you passed in, tries to match each one to a template, then assigns the atom types specified in that template.

Residues are matched to templates based on two pieces of information: the element of each atom, and the set of bonds connecting them (including bonds that connect an atom in the residue to one in a different residue). It does not care about other information, such as the name of the residue. (However, as discussed below, incorrect residue names can still cause problems.) Only the elements and bonds matter.

There are many problems that can prevent it from finding a matching template for a residue. Here are some of the more common ones.

  • You omitted a force field XML file. This is one of the simplest possibilities. When you created your ForceField object and told it which files to load, you may have simply left one out. For example, the Amber14 and CHARMM36 definitions put water into a separate file from the rest of the force field, so that you can easily select between different water models. If you did not specify that file, the force field will not have a matching template for water molecules in your Topology.

  • You have a nonstandard residue. Standard force fields like Amber and CHARMM cover a variety of standard molecules, like proteins and nucleic acids. But if your Topology also includes a ligand or other small molecule, the force field will probably not have parameters (and hence no template) for it.

  • The Topology is missing atoms. For example, crystal structures usually do not include hydrogens, and often also have missing heavy atoms in flexible regions. Without those atoms, the Topology will not match the force field's templates. OpenMM-Setup can automatically fix many problems of this sort.

  • The water model is inconsistent with the Topology. Four and five site water models expect each water molecule to include "extra particles" for the virtual sites. If they are not there (or conversely, if they are there but you try to use a three site water model), the template will not match. Modeller.addExtraParticles() can be used to fix this problem.

  • A chain is terminated incorrectly. Proteins, and nucleic acids to an even greater extent, can be terminated in different ways. The residues at the ends of the chain are different from those in the middle and require different templates. Often a force field will only support one or two ways of terminating each chain. If your Topology has them terminated in a different way, they will not match.

  • You have a nonstandard PDB file. PDB is a standardized format, precisely defined by a specification. Unfortunately, many PDB files do not follow that specification, which causes all sorts of problems.

That last point requires discussion, because there are so many ways a PDB file could be nonstandard. Remember that templates are matched based on two things: the elements of the atoms and the bonds between them. Anything that causes one of those to be reconstructed incorrectly will cause errors in matching templates.

Each ATOM record in a PDB file has a field for specifying the element. Unfortunately, some files leave that field blank. In that case, the PDBFile class tries to guess the element based on the atom's name. Usually it can guess correctly, but not always.

Bonds are more complicated. The PDB specification distinguishes between standard residues and heterogens. Standard residues (amino acids, nucleotides, and water) do not have their bonds specified in the file. The reader is supposed to add them automatically. All other molecules are supposed to have their bonds explicitly given with CONECT records. If those records are missing, the PDB reader will not know which atoms are supposed to be bonded, and so the ForceField will be unable to match templates to them.

Standard residues present their own problems. They are recognized by name. The PDB Chemical Components Dictionary specifies standardized names for all residues and atoms. That is how the PDB reader knows, for example, that a residue called ALA should have a bond between the atom named CA and the atom named CB. Unfortunately, some files use nonstandard names for residues and atoms. In that case, PDBFile does not know where to add bonds.

This is why atom and residue names can matter. The ForceField does not use names when matching templates. But if a file has nonstandard names, the PDB reader will be unable to add the correct bonds, and that will prevent the ForceField from matching templates.

PDBFile does try to accommodate nonstandard names. It has a large table of commonly encountered nonstandard names that it will recognize. For example, the standard name for a water molecule is HOH, but it will also accept several other names like WAT, SOL, and TP3. Likewise, the standard name for the oxygen in a water molecule is O, but it also accepts the names OW and OH2. These are names that various programs have in the past incorrectly used in PDB files that they wrote, so OpenMM knows to look for them. But if your file uses some other nonstandard name that it does not know about, then bonds will not be added correctly, and so the ForceField will not be able to match templates.

Why does it ignore changes I make to a System or Force?

Once you create a Context, the System it was created for is treated as immutable. Any further changes to the System are ignored. That includes other objects contained within the System, such as Forces or VirtualSites.

Creating a Context is expensive. There is a lot of up-front work that needs to be done at the start of a simulation: compiling GPU kernels, fitting splines to tabulated functions, building and inverting a constraint matrix, etc. In the process, information from the System gets copied into many different places, often in a highly processed form. Any change to the System or Forces could potentially invalidate many internal data structures. Both for efficiency and to avoid confusion, OpenMM therefore follows the rule that existing Contexts ignore all changes made to the System. When you create a Context, it copies everything it needs from the System and then does not look at it again.

This rule can be limiting in some situations. There are times when you really need something to change over the course of a simulation. To deal with these situations, multiple mechanisms are provided that offer different tradeoffs between speed and flexibility.

The first mechanism is global parameters. Their values are stored in the Context rather than the System, so you can change them at any time by calling setParameter(). All custom forces allow you to define global parameters. If you define the energy in a way that depends on a parameter, you can easily change it at any time just by setting a new value of the parameter. NonbondedForce lets you do something similar with parameter offsets. You can define a global parameter that modifies a set of particles all at once in a coordinated way.

Second, most Forces provide an updateParametersInContext() method. This provides a controlled way to updated limited aspects of a System. For example, you might want to modify the charges of a set of particles. You would do this by calling setParticleParameters() on the NonbondedForce. Like any other change, this is ignored by existing Contexts. However if you then call updateParametersInContext(), the changed parameter values will be copied over to the existing Context. This is much slower than just changing a global parameter, but much faster than creating a completely new Context. Be aware that not all aspects of a Force can be changed in this way. See the API documentation for each updateParametersInContext() method for a description of what it does and does not update.

The final option is to call reinitialize() on the Context. This causes it to throw out all of its internal data structures and rebuild them from scratch. This is effectively the same as creating an entirely new Context. It is very slow, but it can change absolutely anything.

Because all the internal data structures are discarded, state information is also lost: positions, velocities, parameter values, etc. You can ask it to preserve that information by passing the argument reinitialize(preserveState=True). If you do this, it first creates a checkpoint in memory, then reinitializes the Context, and finally reloads the checkpoint. For most changes this provides an easy way to preserve state information, but if the System has changed in a way that makes the checkpoint no longer valid, it will fail and throw an exception. For example, if the number of particles in the System has changed, then the checkpoint can no longer be loaded, it will throw an exception, and state information will be lost.

Why am I getting an error that says, "The cutoff distance cannot be greater than half the periodic box size"?

When computing direct space nonbonded interactions with periodic boundary conditions, OpenMM uses the minimum image convention: of all possible periodic copies of an atom, it only considers the single copy nearest to the other atom it is interacting with. This is mathematically correct as long as there is only one periodic copy within the cutoff distance. But if two periodic copies of the first atom could simultaneously come within the cutoff distance of the second atom, then ignoring the second one would be an error. To ensure that never happens, the cutoff distance can never be greater than half of any dimension of the periodic box.

When you encounter this error, it generally means either that your periodic box is too small or that your cutoff is too large. Here are some common reasons it may happen.

  • You may be trying to simulate a tiny system. If you are simulating a crystal whose unit cell is only 0.5 nm across and you try to use a 1 nm cutoff, that clearly will not work. A possible solution would be to extend your system to include multiple unit cells. Or you might be simulating a tiny solute in a very small box of water. In that case, enlarging the water box will fix the problem (as well as giving a more realistic representation of bulk water). Also consider that if you use LJPME as your nonbonded method, then the cutoff distance has no physical meaning. It can be viewed as a free parameter that trades off work between the direct space and reciprocal space calculations without changing the final result. You therefore are free to make the cutoff distance smaller.

  • Make sure you have not made a mistake in units. For example, you might have specified nonbondedCutoff=10, intending this to produce a cutoff of 10 Å. But actually this means 10 nm (or 100 Å).

  • When using a barostat to simulate constant pressure, the box size can change with time. Initially it may run correctly, but then throw an exception when the box size momentarily becomes too small. Make sure you have specified the pressure correctly.

  • Note that the behavior of Modeller.addSolvent() changed between OpenMM 7.7 and 8.0. When using the padding argument to solvate very small molecules, it may produce a smaller water box than in earlier versions. In that case you may need to increase the padding distance, or alternatively use the boxSize argument to specify a fixed size water box.

Why am I getting an error about "CUDA_ERROR_UNSUPPORTED_PTX_VERSION"?

This indicates an incompatibility between your CUDA toolkit and driver. The solution is either to upgrade to a newer driver, or downgrade to an older toolkit.

When running a program that uses CUDA, there are a few pieces of software that all must be compatible with each other.

  • The CUDA driver. This contains the low level support for interacting with the GPU and executing CUDA kernels. It is system software. Only one driver can be installed at a time, and updating it requires administrator access.

  • The CUDA toolkit. This contains higher level libraries, as well as the compiler used to compile kernels to PTX, a low level description interpreted by the driver. The toolkit does not require special privileges to install, and you can have multiple toolkits on your computer at once. If you use conda to install OpenMM, it will automatically install a CUDA toolkit into your conda environment.

  • The program that uses CUDA, in this case OpenMM. It is compiled against a particular version of the CUDA toolkit.

The toolkit and the OpenMM package must be matched to each other. When you compile a program against a particular CUDA toolkit, it will only work with a limited range of similar toolkit versions. If the toolkit is too old or too new, it will not work. For this reason, multiple OpenMM packages are provided that are compiled against a selection of recent CUDA toolkits.

The driver is somewhat more forgiving: it provides backward compatibility over a large range of versions, but not forward compatibility. A newer driver will generally work with an older toolkit, but not with a newer toolkit.

In theory, conda is supposed to automatically install a toolkit and OpenMM build that are compatible with your driver. In practice, this is not reliable. It will sometimes select a toolkit version that is too new to work with your driver. This is especially common when installing on clusters that do not have GPUs (and hence do not have a CUDA driver) on the login nodes. When you install OpenMM, conda does not know what driver will be present on the compute nodes.

The best solution in this case is to upgrade your driver to the newest available version. Keeping your driver up to date is a good practice. In some cases this may not be an option, for example if you do not have administrator access to the computer. In that case, the solution is to downgrade to an earlier driver (and corresponding OpenMM build).

When installing OpenMM, you can tell conda to install a particular version of the CUDA toolkit. For example,

conda install -c conda-forge openmm cudatoolkit=12.0

This overrides the default method of selecting a toolkit version, and lets you select a version that is compatible with your driver.