From 835cbe254594a5af5830a49fb16b6b2c2c9cb788 Mon Sep 17 00:00:00 2001 From: Josh Horton Date: Fri, 30 Oct 2026 16:24:08 +0000 Subject: [PATCH 1/3] fix example errors rendering, add plainMD validation to the troubleshooting guide. --- docs/guide/protocols/plainmd.rst | 4 ++ docs/guide/troubleshooting.rst | 71 ++++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/docs/guide/protocols/plainmd.rst b/docs/guide/protocols/plainmd.rst index 6c3850364..71f0df87a 100644 --- a/docs/guide/protocols/plainmd.rst +++ b/docs/guide/protocols/plainmd.rst @@ -57,6 +57,10 @@ See Also * :any:`MD tutorial <../../tutorials/md_tutorial>` +**Guides** + +* :ref:`Validating your inputs with a plain MD simulation ` + **API Documentation** * :ref:`OpenMM plain MD protocol ` diff --git a/docs/guide/troubleshooting.rst b/docs/guide/troubleshooting.rst index 4f82b2560..7506aa9b4 100644 --- a/docs/guide/troubleshooting.rst +++ b/docs/guide/troubleshooting.rst @@ -52,7 +52,9 @@ SMIRNOFF force field parameter assignment errors You may see errors like: -- ``openff.interchange.exceptions.UnassignedBondError: BondHandler was not able to find parameters for the following valence terms: - Topology indices (0, 1): names and elements (C1 C), (Si1 Si),`` +.. code:: bash + + openff.interchange.exceptions.UnassignedBondError: BondHandler was not able to find parameters for the following valence terms: - Topology indices (0, 1): names and elements (C1 C), (Si1 Si) This usually means the SMIRNOFF-style force field does not have parameters for part of the chemistry in the system. In the example above, the force field was unable to assign bond parameters for bonds involving silicon. @@ -75,8 +77,14 @@ If the error lists specific bonds or valence terms, those terms are the ones tha You may see errors like: -- ``openmm.OpenMMException: Particle coordinate is NaN.`` -- ``openmmtools.multistate.utils.SimulationNaNError: Propagating replica 0 at state 10 resulted in a NaN!`` + +.. code:: bash + + openmm.OpenMMException: Particle coordinate is NaN. + +.. code:: bash + + openmmtools.multistate.utils.SimulationNaNError: Propagating replica 0 at state 10 resulted in a NaN! For more information on the OpenMM error, see the OpenMM FAQ entry on NaNs: https://github.com/openmm/openmm/wiki/Frequently-Asked-Questions#nan @@ -110,7 +118,61 @@ diagnostics, including agreement between independent repeats. The state of the system and integrator before the error are often saved in a ``nan-error-logs`` directory, which can help with debugging. -.. _troubleshooting-log-debug-information: +.. _troubleshooting-validate-inputs-with-plain-md: + +Validating your inputs with a plain MD simulation +------------------------------------------------- + +Many of the errors above (e.g. parameter assignment failures, ``NaN`` errors during simulation, box size errors) are due problems with the input structures which are not identified until protocol execution. +A quick way to check this *before* running an expensive free energy protocol is to run a short, MD simulation of your system with the :class:`.PlainMDProtocol` (see the :doc:`Plain MD protocol guide `). + +This confirms that: + +- The receptor can be parameterized together with the ligand (e.g. no missing residues, caps or unsupported chemistry) by the same force fields you intend to use in your production protocol. +- The system can be minimized and equilibrated producing a stable simulation without clashes or ``NaN`` errors. + +If a system is stable in plain MD it is much more likely to be suitable for other protocols, for failures see sections :ref:`troubleshooting-nan-errors-during-simulation` and :ref:`troubleshooting-smirnoff-force-field-parameter-assignment-errors` for next steps. +We recommend running this for a representative subset of the ligands with the receptor, using the same force fields and solvent settings you intend to use in your production protocol. + +The simplest way to run this check is via the Python API with the example below which uses short equilibration and production lengths, which is enough to catch most setup problems: + +.. code-block:: python + + import openfe + from gufe.protocols import execute_DAG + from openff.units import unit + from openfe.protocols.openmm_md.plain_md_methods import PlainMDProtocol + import pathlib + + ligand = openfe.SmallMoleculeComponent.from_sdf_file("ligand.sdf") + protein = openfe.ProteinComponent.from_pdb_file("protein.pdb") + solvent = openfe.SolventComponent() # use the same settings as your production run + + system = openfe.ChemicalSystem( + {"ligand": ligand, "protein": protein, "solvent": solvent}, + name="validation", + ) + + settings = PlainMDProtocol.default_settings() + settings.simulation_settings.equilibration_length_nvt = 1 * unit.picosecond + settings.simulation_settings.equilibration_length = 1 * unit.picosecond + settings.simulation_settings.production_length = 1 * unit.picosecond + settings.engine_settings.compute_platform = "CUDA" + + protocol = PlainMDProtocol(settings=settings) + dag = protocol.create(stateA=system, stateB=system, mapping=None) + output_dir = pathlib.Path("plain_md_check") + output_dir.mkdir(exist_ok=True, parents=True) + + result = execute_DAG( + dag, + shared_basedir=output_dir, + scratch_basedir=output_dir, + keep_shared=False, + raise_error=True, + n_retries=0 + ) + Resuming Interrupted Simulations -------------------------------- @@ -119,6 +181,7 @@ In the case where your ``openfe quickrun`` execution has timed out, was cancelle See :ref:`Resuming Interrupted Simulations ` for more details. +.. _troubleshooting-log-debug-information: Log Debug information --------------------- From 8f865d0c6ff1f0e0f9a11bb395ff3d981d1b19c5 Mon Sep 17 00:00:00 2001 From: Josh Horton Date: Mon, 2 Nov 2026 10:14:38 +0000 Subject: [PATCH 2/3] Apply batched suggestions from code review Co-authored-by: Hannah Baumann <43765638+hannahbaumann@users.noreply.github.com> --- docs/guide/protocols/plainmd.rst | 2 +- docs/guide/troubleshooting.rst | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/guide/protocols/plainmd.rst b/docs/guide/protocols/plainmd.rst index 71f0df87a..27d514335 100644 --- a/docs/guide/protocols/plainmd.rst +++ b/docs/guide/protocols/plainmd.rst @@ -57,7 +57,7 @@ See Also * :any:`MD tutorial <../../tutorials/md_tutorial>` -**Guides** +**User Guide** * :ref:`Validating your inputs with a plain MD simulation ` diff --git a/docs/guide/troubleshooting.rst b/docs/guide/troubleshooting.rst index 7506aa9b4..a07baffa4 100644 --- a/docs/guide/troubleshooting.rst +++ b/docs/guide/troubleshooting.rst @@ -123,8 +123,8 @@ The state of the system and integrator before the error are often saved in a ``n Validating your inputs with a plain MD simulation ------------------------------------------------- -Many of the errors above (e.g. parameter assignment failures, ``NaN`` errors during simulation, box size errors) are due problems with the input structures which are not identified until protocol execution. -A quick way to check this *before* running an expensive free energy protocol is to run a short, MD simulation of your system with the :class:`.PlainMDProtocol` (see the :doc:`Plain MD protocol guide `). +Many of the errors above (e.g. parameter assignment failures, ``NaN`` errors during simulation, box size errors) are due to problems with the input structures which are not identified until protocol execution. +A quick way to check this *before* running an expensive free energy protocol is to run a short MD simulation of your system with the :class:`.PlainMDProtocol` (see the :doc:`Plain MD protocol guide `). This confirms that: @@ -134,7 +134,7 @@ This confirms that: If a system is stable in plain MD it is much more likely to be suitable for other protocols, for failures see sections :ref:`troubleshooting-nan-errors-during-simulation` and :ref:`troubleshooting-smirnoff-force-field-parameter-assignment-errors` for next steps. We recommend running this for a representative subset of the ligands with the receptor, using the same force fields and solvent settings you intend to use in your production protocol. -The simplest way to run this check is via the Python API with the example below which uses short equilibration and production lengths, which is enough to catch most setup problems: +The simplest way to run this check is via the Python API with the example below which uses short equilibration and production lengths, which can catch common setup problems: .. code-block:: python From cf91c0b218ba3f343e20047f2816534fa35dc7bc Mon Sep 17 00:00:00 2001 From: Josh Horton Date: Mon, 2 Nov 2026 11:07:40 +0000 Subject: [PATCH 3/3] feedback on MD validation --- docs/guide/troubleshooting.rst | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/docs/guide/troubleshooting.rst b/docs/guide/troubleshooting.rst index a07baffa4..03c5e9339 100644 --- a/docs/guide/troubleshooting.rst +++ b/docs/guide/troubleshooting.rst @@ -126,10 +126,10 @@ Validating your inputs with a plain MD simulation Many of the errors above (e.g. parameter assignment failures, ``NaN`` errors during simulation, box size errors) are due to problems with the input structures which are not identified until protocol execution. A quick way to check this *before* running an expensive free energy protocol is to run a short MD simulation of your system with the :class:`.PlainMDProtocol` (see the :doc:`Plain MD protocol guide `). -This confirms that: +This helps check that: - The receptor can be parameterized together with the ligand (e.g. no missing residues, caps or unsupported chemistry) by the same force fields you intend to use in your production protocol. -- The system can be minimized and equilibrated producing a stable simulation without clashes or ``NaN`` errors. +- The **non-alchemical** system can be minimized and equilibrated producing a stable simulation without clashes or ``NaN`` errors. If a system is stable in plain MD it is much more likely to be suitable for other protocols, for failures see sections :ref:`troubleshooting-nan-errors-during-simulation` and :ref:`troubleshooting-smirnoff-force-field-parameter-assignment-errors` for next steps. We recommend running this for a representative subset of the ligands with the receptor, using the same force fields and solvent settings you intend to use in your production protocol. @@ -144,12 +144,19 @@ The simplest way to run this check is via the Python API with the example below from openfe.protocols.openmm_md.plain_md_methods import PlainMDProtocol import pathlib - ligand = openfe.SmallMoleculeComponent.from_sdf_file("ligand.sdf") - protein = openfe.ProteinComponent.from_pdb_file("protein.pdb") + ligand = openfe.SmallMoleculeComponent.from_sdf_file("ligand.sdf") # load your ligand from your SDF file + protein = openfe.ProteinComponent.from_pdb_file("protein.pdb") # load your protein from your PDB file solvent = openfe.SolventComponent() # use the same settings as your production run + # If you have cofactors in the system load them here as SmallMoleculeComponents and include them in the ChemicalSystem below. + # cofactor = openfe.SmallMoleculeComponent.from_sdf_file("cofactor.sdf") system = openfe.ChemicalSystem( - {"ligand": ligand, "protein": protein, "solvent": solvent}, + { + "ligand": ligand, + "protein": protein, + "solvent": solvent, + # "cofactor": cofactor # include any cofactors here + }, name="validation", ) @@ -157,6 +164,7 @@ The simplest way to run this check is via the Python API with the example below settings.simulation_settings.equilibration_length_nvt = 1 * unit.picosecond settings.simulation_settings.equilibration_length = 1 * unit.picosecond settings.simulation_settings.production_length = 1 * unit.picosecond + # You will need a GPU with CUDA installed to run this quick check, we recommend using the same platform as your production simulations. settings.engine_settings.compute_platform = "CUDA" protocol = PlainMDProtocol(settings=settings) @@ -168,11 +176,17 @@ The simplest way to run this check is via the Python API with the example below dag, shared_basedir=output_dir, scratch_basedir=output_dir, - keep_shared=False, + keep_shared=True, raise_error=True, n_retries=0 ) + # Check for any errors in the execution + if not result.ok(): + for failure in result.protocol_unit_failures: + # If we find a failure print the traceback to help with debugging + print(f"Protocol unit {failure.name} failed with error: {failure.traceback}") + Resuming Interrupted Simulations --------------------------------