Skip to content

Commit

Permalink
Merge pull request #1042 from openforcefield/export_docstr_tweaks
Browse files Browse the repository at this point in the history
Clarify `to_openmm_simulation` docstring and rename `to_lammps(file_path)` arg to `prefix`
  • Loading branch information
mattwthompson authored Oct 7, 2024
2 parents eefe66d + cdae295 commit 1879735
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 43 deletions.
37 changes: 29 additions & 8 deletions examples/lammps/lammps.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
"from openmm.app import PDBFile\n",
"\n",
"from openff.interchange import Interchange\n",
"from openff.interchange.components.mdconfig import MDConfig\n",
"\n",
"# Read a structure from the Toolkit's test suite into a Topology\n",
"pdbfile = PDBFile(get_data_file_path(\"systems/packmol_boxes/propane_methane_butanol_0.2_0.3_0.5.pdb\"))\n",
Expand Down Expand Up @@ -87,14 +86,14 @@
"metadata": {},
"outputs": [],
"source": [
"interchange.to_lammps(\"interchange.lmp\")"
"interchange.to_lammps_datafile(\"interchange.lmp\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we need to write an input file for LAMMPS. Parts of these input files depend on force field parameters, so we should use a sample input file written for our interchange as a starting point. We can generate such a sample file from `MDConfig`:"
"Now we need to write an input file for LAMMPS. Parts of these input files depend on force field parameters, so we should use a sample input file written for our interchange as a starting point. We can generate such a sample file with the `to_lammps_input()` method:"
]
},
{
Expand All @@ -103,17 +102,32 @@
"metadata": {},
"outputs": [],
"source": [
"mdconfig = MDConfig.from_interchange(interchange)\n",
"mdconfig.write_lammps_input(input_file=\"auto_generated.in\", interchange=interchange)\n",
"with open(\"auto_generated.in\") as f:\n",
"interchange.to_lammps_input(\"interchange_pointenergy.in\", data_file=\"interchange.lmp\")\n",
"with open(\"interchange_pointenergy.in\") as f:\n",
" print(f.read())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"That sample file will only perform a single point energy calculation; here's a more complete file that includes the above parameters but will run an actual MD simulation. Note that `out.lmp` is changed to `interchange.lmp` to reflect the filename we used earlier:\n",
"Note that the `read_data` line must match the data file name - in this case, `interchange.lmp`. That's why `to_lammps_input` needs the data file name. We could alternatively use the `to_lammps` method to write both files in a consistent way:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"interchange.to_lammps(\"interchange\") # writes interchange.lmp and interchange_pointenergy.in"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"That sample file will only perform a single point energy calculation; here's a more complete file that includes the above parameters but will run an actual MD simulation. \n",
"\n",
"<div class=\"alert alert-warning\" style=\"max-width: 700px; margin-left: auto; margin-right: auto;\">\n",
" <b>⚠️ Don't use example input files in production</b><br />\n",
Expand Down Expand Up @@ -208,6 +222,13 @@
"traj.make_molecules_whole()\n",
"nglview.show_mdtraj(traj)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
Expand All @@ -227,7 +248,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.14"
"version": "3.11.0"
}
},
"nbformat": 4,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

from openff.interchange import Interchange
from openff.interchange._tests import MoleculeWithConformer, needs_lmp
from openff.interchange.components.mdconfig import MDConfig
from openff.interchange.drivers import get_lammps_energies, get_openmm_energies

rng = numpy.random.default_rng(821)
Expand Down Expand Up @@ -105,24 +104,17 @@ def test_unique_lammps_mol_ids(
topology.box_vectors = Quantity([4, 4, 4], "nanometer")

with temporary_cd():
lammps_input_path = Path.cwd() / "temp.in"
lammps_data_path = Path.cwd() / "out.lmp"
lammps_prefix = Path.cwd() / "lammps_test"

interchange = sage_unconstrained.create_interchange(topology)
interchange.to_lammps(lammps_data_path)

mdconfig = MDConfig.from_interchange(interchange)
mdconfig.write_lammps_input(
interchange=interchange,
input_file=lammps_input_path,
)
interchange.to_lammps(lammps_prefix)

# Extract molecule IDs from data file
with lammps.lammps(
cmdargs=["-screen", "none", "-log", "none"],
) as lmp:
lmp.file(
"temp.in",
"lammps_test_pointenergy.in",
)
written_mol_ids = {
mol_id
Expand Down
33 changes: 17 additions & 16 deletions openff/interchange/components/interchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,24 +426,20 @@ def to_gro(self, file_path: Path | str, decimal: int = 3):
gro_file=file_path,
).to_gro(decimal=decimal)

def to_lammps(self, file_path: Path | str):
def to_lammps(self, prefix: str):
"""
Export this ``Interchange`` to LAMMPS data and run input files.
Parameters
----------
file_path
The prefix to use for the LAMMPS data and run input files. If a path
ending in ".lmp" is given, the extension will be dropped to generate
the prefix. For example, both "foo" and "foo.lmp" will produce files
named "foo.lmp" and "foo_pointenergy.in".
"""
# TODO: Rename `file_path` to `prefix` (breaking change)
prefix = str(file_path)
if prefix.endswith(".lmp"):
prefix = prefix[:-4]
prefix
The prefix to use for the LAMMPS data and run input files. For
example, "foo" will produce files named "foo.lmp" and
"foo_pointenergy.in".
"""
prefix = str(prefix)
datafile_path = prefix + ".lmp"
self.to_lammps_datafile(datafile_path)
self.to_lammps_input(
Expand Down Expand Up @@ -563,6 +559,14 @@ def to_openmm_simulation(
Positions are set on the `Simulation` if present on the `Interchange`.
Additional forces, such as a barostat, should be added with the
``additional_forces`` argument to avoid having to re-initialize
the ``Context``. Re-initializing the ``Context`` after adding a
``Force`` is necessary due to `implementation details`_
in OpenMM.
.. _implementation details: https:/openmm/openmm/wiki/Frequently-Asked-Questions#why-does-it-ignore-changes-i-make-to-a-system-or-force
Parameters
----------
integrator : subclass of openmm.Integrator
Expand All @@ -575,7 +579,7 @@ def to_openmm_simulation(
If True, add valence forces that might be overridden by constraints, i.e. call `addBond` or `addAngle`
on a bond or angle that is fully constrained.
additional_forces : Iterable[openmm.Force], default=tuple()
Additional forces to be added to the system, i.e. barostats that are not
Additional forces to be added to the system, e.g. barostats, that are not
added by the force field.
**kwargs
Further keyword parameters are passed on to
Expand All @@ -588,6 +592,7 @@ def to_openmm_simulation(
Examples
--------
Create an OpenMM simulation with a Langevin integrator and a Monte Carlo barostat:
>>> import openmm
Expand All @@ -608,10 +613,6 @@ def to_openmm_simulation(
... additional_forces=[barostat],
... )
Re-initializing the `Context` after adding a `Force` is necessary due to implementation details in OpenMM.
For more, see
https:/openmm/openmm/wiki/Frequently-Asked-Questions#why-does-it-ignore-changes-i-make-to-a-system-or-force
"""
import openmm.app

Expand Down
10 changes: 2 additions & 8 deletions openff/interchange/drivers/lammps.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from openff.utilities import MissingOptionalDependencyError, requires_package

from openff.interchange import Interchange
from openff.interchange.components.mdconfig import MDConfig
from openff.interchange.drivers.report import EnergyReport
from openff.interchange.exceptions import LAMMPSNotFoundError, LAMMPSRunError

Expand Down Expand Up @@ -63,20 +62,15 @@ def _get_lammps_energies(
)

with tempfile.TemporaryDirectory():
interchange.to_lammps("out.lmp")
mdconfig = MDConfig.from_interchange(interchange)
mdconfig.write_lammps_input(
interchange=interchange,
input_file="tmp.in",
)
interchange.to_lammps("out")

# By default, LAMMPS spits out logs to the screen, turn it off
# https://matsci.org/t/how-to-remove-or-redirect-python-lammps-stdout/38075/5
# not that this is not sent to STDOUT, so `contextlib.redirect_stdout` won't work
runner = lammps.lammps(cmdargs=["-screen", "none", "-nocite"])

try:
runner.file("tmp.in")
runner.file("out_pointenergy.in")
# LAMMPS does not raise a custom exception :(
except Exception as error:
raise LAMMPSRunError from error
Expand Down

0 comments on commit 1879735

Please sign in to comment.