From d7dcd143bbf9edcf5098e4098aead6e5c5c4ebf0 Mon Sep 17 00:00:00 2001 From: Josh Horton Date: Tue, 11 Aug 2026 12:32:49 +0100 Subject: [PATCH 01/21] add charge validation --- .../openmm_utils/system_validation.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/openfe/protocols/openmm_utils/system_validation.py b/src/openfe/protocols/openmm_utils/system_validation.py index 906999910..3fd08070f 100644 --- a/src/openfe/protocols/openmm_utils/system_validation.py +++ b/src/openfe/protocols/openmm_utils/system_validation.py @@ -22,7 +22,9 @@ SolventComponent, ) from gufe.components.errors import ComponentValidationError +from gufe.protocols.errors import ProtocolValidationError from openff.toolkit import Molecule as OFFMol +from openff.toolkit import ForceField logger = logging.getLogger(__name__) @@ -371,3 +373,53 @@ def validate_chemical_system(system: ChemicalSystem): except ComponentValidationError as e: errmsg = f"Component {entry} from ChemicalSystem {system.name} failed validation: {e}" raise ComponentValidationError(errmsg) + + +def validate_nondeterministic_charges( + system: ChemicalSystem, + small_molecule_forcefield: str +): + """" + Validate that the SmallMoleculeComponents of the system will have deterministic partial charges. + + This is determined by checking for charges on the molecules before checking what would be assigned by the force field. + + Parameters + ---------- + system : ChemicalSystem + The ChemicalSystem to validate with SmallMoleculeComponents. + small_molecule_forcefield : str + The force field to be used for the SmallMoleculeComponents. + + Raises + ------ + ProtocolValidationError + If any SmallMoleculeComponents in the system would have am1bcc charges generated at runtime. + """ + smcs: list[SmallMoleculeComponent] = system.get_components_of_type(SmallMoleculeComponent) + if "espaloma" in small_molecule_forcefield or "gaff" in small_molecule_forcefield: + # this will always generate charges at runtime, so raise an error for missing charges + ff = None + else: + # We do not check for offxml in the name as users can pass the force field contents as a string + ff = ForceField(small_molecule_forcefield) + + for smc in smcs: + offmol = smc.to_openff() + if offmol.partial_charges is not None and np.any(offmol.partial_charges): + continue + + # check the labels assigned for an openff force field + if ff is not None: + labels = ff.label_molecules(offmol.to_topology())[0] + else: + # return a dummy label as the gaff and espaloma should always give am1bcc charges + labels = {"LibraryCharges": {}} + + # We count library and nagl charges as deterministic + if len(labels["LibraryCharges"]) != offmol.n_atoms and "NAGLCharges" not in labels: + errmsg = (f"SmallMoleculeComponent {smc} from system {system.name} would have am1bcc charges generated at runtime which is non-deterministic. " + f"Please provide a molecule with pre-computed charges or use library charges instead.") + raise ProtocolValidationError(errmsg) + + From 84174f39f5697045cd0a012fad76496abe6316a3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 11:36:23 +0000 Subject: [PATCH 02/21] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../protocols/openmm_utils/system_validation.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/openfe/protocols/openmm_utils/system_validation.py b/src/openfe/protocols/openmm_utils/system_validation.py index 3fd08070f..1368f0cbc 100644 --- a/src/openfe/protocols/openmm_utils/system_validation.py +++ b/src/openfe/protocols/openmm_utils/system_validation.py @@ -23,8 +23,8 @@ ) from gufe.components.errors import ComponentValidationError from gufe.protocols.errors import ProtocolValidationError -from openff.toolkit import Molecule as OFFMol from openff.toolkit import ForceField +from openff.toolkit import Molecule as OFFMol logger = logging.getLogger(__name__) @@ -375,11 +375,8 @@ def validate_chemical_system(system: ChemicalSystem): raise ComponentValidationError(errmsg) -def validate_nondeterministic_charges( - system: ChemicalSystem, - small_molecule_forcefield: str -): - """" +def validate_nondeterministic_charges(system: ChemicalSystem, small_molecule_forcefield: str): + """ " Validate that the SmallMoleculeComponents of the system will have deterministic partial charges. This is determined by checking for charges on the molecules before checking what would be assigned by the force field. @@ -418,8 +415,8 @@ def validate_nondeterministic_charges( # We count library and nagl charges as deterministic if len(labels["LibraryCharges"]) != offmol.n_atoms and "NAGLCharges" not in labels: - errmsg = (f"SmallMoleculeComponent {smc} from system {system.name} would have am1bcc charges generated at runtime which is non-deterministic. " - f"Please provide a molecule with pre-computed charges or use library charges instead.") + errmsg = ( + f"SmallMoleculeComponent {smc} from system {system.name} would have am1bcc charges generated at runtime which is non-deterministic. " + f"Please provide a molecule with pre-computed charges or use library charges instead." + ) raise ProtocolValidationError(errmsg) - - From 3c76241a783a04bbb914a8f90b8c53f9a8ab2049 Mon Sep 17 00:00:00 2001 From: Josh Horton Date: Tue, 11 Aug 2026 13:49:13 +0100 Subject: [PATCH 03/21] add tests --- .../openmm_utils/system_validation.py | 4 +- .../tests/protocols/test_openmmutils.py | 77 +++++++++++++++++++ 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/src/openfe/protocols/openmm_utils/system_validation.py b/src/openfe/protocols/openmm_utils/system_validation.py index 1368f0cbc..6ba0bc47a 100644 --- a/src/openfe/protocols/openmm_utils/system_validation.py +++ b/src/openfe/protocols/openmm_utils/system_validation.py @@ -376,7 +376,7 @@ def validate_chemical_system(system: ChemicalSystem): def validate_nondeterministic_charges(system: ChemicalSystem, small_molecule_forcefield: str): - """ " + """ Validate that the SmallMoleculeComponents of the system will have deterministic partial charges. This is determined by checking for charges on the molecules before checking what would be assigned by the force field. @@ -416,7 +416,7 @@ def validate_nondeterministic_charges(system: ChemicalSystem, small_molecule_for # We count library and nagl charges as deterministic if len(labels["LibraryCharges"]) != offmol.n_atoms and "NAGLCharges" not in labels: errmsg = ( - f"SmallMoleculeComponent {smc} from system {system.name} would have am1bcc charges generated at runtime which is non-deterministic. " + f"{smc} from system {system.name} would have am1bcc charges generated at runtime which is non-deterministic. " f"Please provide a molecule with pre-computed charges or use library charges instead." ) raise ProtocolValidationError(errmsg) diff --git a/src/openfe/tests/protocols/test_openmmutils.py b/src/openfe/tests/protocols/test_openmmutils.py index d4c50a5da..d76cd7466 100644 --- a/src/openfe/tests/protocols/test_openmmutils.py +++ b/src/openfe/tests/protocols/test_openmmutils.py @@ -4,6 +4,7 @@ import gzip import logging import os +import re import sys from importlib import resources from pathlib import Path @@ -11,10 +12,13 @@ import numpy as np import pytest +from gufe import ChemicalSystem from gufe.components.errors import ComponentValidationError +from gufe.protocols.errors import ProtocolValidationError from gufe.settings import OpenMMSystemGeneratorFFSettings, ThermoSettings from numpy.testing import assert_allclose, assert_equal from openff.toolkit import Molecule as OFFMol +from openff.toolkit import ForceField from openff.toolkit.utils.toolkit_registry import ToolkitRegistry from openff.toolkit.utils.toolkits import RDKitToolkitWrapper from openff.units import unit @@ -1283,3 +1287,76 @@ def test_set_metadata_none_clears(): _set_offmol_metadata(mol, "residue_name", "LIG") _set_offmol_metadata(mol, "residue_name", None) assert all("residue_name" not in a.metadata for a in mol.atoms) + + +class TestChargeValidation: + """Test validation of nondeterministic partial charge assignment.""" + + @pytest.fixture + def benzene_charged_system(self, benzene_modifications): + return ChemicalSystem({"ligand": benzene_modifications["benzene"]}, name="charged") + + @pytest.fixture + def benzene_no_charge_system(self, benzene_modifications_uncharged): + return ChemicalSystem({"ligand": benzene_modifications_uncharged["benzene"]}, name="no charges") + + def test_gaff_with_molecule_charges(self, benzene_charged_system): + system_validation.validate_nondeterministic_charges( + benzene_charged_system, + small_molecule_forcefield="gaff-2.11" + ) + + def test_gaff_no_charges(self, benzene_no_charge_system): + with pytest.raises(ProtocolValidationError, match=re.escape("SmallMoleculeComponent(name=benzene) from system no charges would have am1bcc charges")): + system_validation.validate_nondeterministic_charges( + benzene_no_charge_system, + small_molecule_forcefield="gaff-2.11" + ) + + def test_espaloma_with_molecule_charges(self, benzene_charged_system): + system_validation.validate_nondeterministic_charges( + benzene_charged_system, + small_molecule_forcefield="espaloma-0.3.2" + ) + + def test_espaloma_no_charges(self, benzene_no_charge_system): + with pytest.raises(ProtocolValidationError, match=re.escape("SmallMoleculeComponent(name=benzene) from system no charges would have am1bcc charges")): + system_validation.validate_nondeterministic_charges( + benzene_no_charge_system, + small_molecule_forcefield="espaloma-0.3.2" + ) + + def test_openff_with_molecule_charges(self, benzene_charged_system): + system_validation.validate_nondeterministic_charges( + benzene_charged_system, + small_molecule_forcefield="openff-2.2.0.offxml" + ) + + def test_openff_no_charges(self, benzene_no_charge_system): + with pytest.raises(ProtocolValidationError, match=re.escape("SmallMoleculeComponent(name=benzene) from system no charges would have am1bcc charges")): + system_validation.validate_nondeterministic_charges( + benzene_no_charge_system, + small_molecule_forcefield="openff-2.2.0.offxml" + ) + + def test_openff_nagl_no_charges(self, benzene_no_charge_system): + system_validation.validate_nondeterministic_charges( + benzene_no_charge_system, + small_molecule_forcefield="openff-2.3.0.offxml" + ) + + def test_openff_lib_charges_no_charges(self, benzene_no_charge_system, benzene_modifications): + # add some library charges to the force field and test using a string + benzene = benzene_modifications["benzene"].to_openff() + # use a force field with an am1bcc handler + ff = ForceField("openff-2.2.0.offxml") + lib_charge_handler = ff.get_parameter_handler("LibraryCharges") + # make a new parameter + lib_charge = lib_charge_handler._INFOTYPE.from_molecule(benzene) + # add it to the handler + lib_charge_handler.add_parameter(parameter=lib_charge) + # run the validation + system_validation.validate_nondeterministic_charges( + benzene_no_charge_system, + small_molecule_forcefield=ff.to_string() + ) From 36784f96a6c517d4be2308a3789fd269ad8d77a0 Mon Sep 17 00:00:00 2001 From: Josh Horton Date: Fri, 14 Aug 2026 14:20:44 +0100 Subject: [PATCH 04/21] add validation to all protocols, remove runtime charge generation --- .../protocols/openmm_afe/base_afe_units.py | 33 +----------------- .../openmm_afe/equil_binding_afe_method.py | 5 +++ .../openmm_afe/equil_solvation_afe_method.py | 5 +++ .../protocols/openmm_md/plain_md_methods.py | 34 +++---------------- .../openmm_rfe/hybridtop_protocols.py | 5 +++ .../protocols/openmm_rfe/hybridtop_units.py | 30 ---------------- .../protocols/openmm_septop/base_units.py | 27 --------------- .../openmm_septop/equil_septop_method.py | 5 +++ .../protocols/openmm_septop/septop_units.py | 6 ---- .../openmm_utils/system_validation.py | 1 + 10 files changed, 26 insertions(+), 125 deletions(-) diff --git a/src/openfe/protocols/openmm_afe/base_afe_units.py b/src/openfe/protocols/openmm_afe/base_afe_units.py index 700e48541..7d927a5a3 100644 --- a/src/openfe/protocols/openmm_afe/base_afe_units.py +++ b/src/openfe/protocols/openmm_afe/base_afe_units.py @@ -67,7 +67,6 @@ ) from openfe.protocols.openmm_md.plain_md_methods import PlainMDSimulationUnit from openfe.protocols.openmm_utils import ( - charge_generation, multistate_analysis, omm_compute, settings_validation, @@ -349,33 +348,6 @@ def _pre_equilibrate( return equilibrated_positions, to_openmm(from_openmm(box)) - @staticmethod - def _assign_partial_charges( - partial_charge_settings: OpenFFPartialChargeSettings, - small_mols: dict[SmallMoleculeComponent, OFFMolecule], - ) -> None: - """ - Assign partial charges to the OpenFF Molecules associated with - all the SmallMoleculeComponents in the transformation. - - Parameters - ---------- - charge_settings : OpenFFPartialChargeSettings - Settings for controlling how the partial charges are assigned. - small_mols : dict[SmallMoleculeComponent, openff.toolkit.Molecule] - Dictionary of OpenFF Molecules to add, keyed by their - associated SmallMoleculeComponent. - """ - for mol in small_mols.values(): - charge_generation.assign_offmol_partial_charges( - offmol=mol, - overwrite=False, - method=partial_charge_settings.partial_charge_method, - toolkit_backend=partial_charge_settings.off_toolkit_backend, - generate_n_conformers=partial_charge_settings.number_of_conformers, - nagl_model=partial_charge_settings.nagl_model, - ) - @staticmethod def _get_system_generator( settings: dict[str, SettingsBaseModel], @@ -391,7 +363,7 @@ def _get_system_generator( ---------- settings : dict[str, SettingsBaseModel] A dictionary of settings object for the unit. - solvent_comp : BaseSolventComponent | None + solvent_component : BaseSolventComponent | None The solvent component of this system, if there is one. openff_molecules : list[openff.toolkit.Molecule] | None A list of OpenFF Molecules to generate templates for, if any. @@ -704,9 +676,6 @@ def run( # Get settings settings = self._get_settings() - # Assign partial charges now to avoid any discrepancies later - self._assign_partial_charges(settings["charge_settings"], small_mols) - # Get OpenMM topology, positions, system, and comp_resids omm_topology, omm_system, positions, comp_resids = self._get_omm_objects( settings=settings, diff --git a/src/openfe/protocols/openmm_afe/equil_binding_afe_method.py b/src/openfe/protocols/openmm_afe/equil_binding_afe_method.py index 06dcbaed0..28161b2cb 100644 --- a/src/openfe/protocols/openmm_afe/equil_binding_afe_method.py +++ b/src/openfe/protocols/openmm_afe/equil_binding_afe_method.py @@ -403,6 +403,11 @@ def _validate( system_validation.validate_chemical_system(stateB) self._validate_endstates(stateA, stateB) + # validate small molecule charges + small_ff = self.settings.forcefield_settings.small_molecule_forcefield + system_validation.validate_nondeterministic_charges(stateA, small_ff) + system_validation.validate_nondeterministic_charges(stateB, small_ff) + # Validate the complex lambda schedule self._validate_lambda_schedule( self.settings.complex_lambda_settings, diff --git a/src/openfe/protocols/openmm_afe/equil_solvation_afe_method.py b/src/openfe/protocols/openmm_afe/equil_solvation_afe_method.py index 5b2069c0d..28216f40b 100644 --- a/src/openfe/protocols/openmm_afe/equil_solvation_afe_method.py +++ b/src/openfe/protocols/openmm_afe/equil_solvation_afe_method.py @@ -385,6 +385,11 @@ def _validate( system_validation.validate_chemical_system(stateB) self._validate_endstates(stateA, stateB) + # validate small molecule charges + small_ff = self.settings.forcefield_settings.small_molecule_forcefield + system_validation.validate_nondeterministic_charges(stateA, small_ff) + system_validation.validate_nondeterministic_charges(stateB, small_ff) + # Validate the lambda schedule for solv_sets in ( self.settings.solvent_simulation_settings, diff --git a/src/openfe/protocols/openmm_md/plain_md_methods.py b/src/openfe/protocols/openmm_md/plain_md_methods.py index c16c3d3cb..c9033949c 100644 --- a/src/openfe/protocols/openmm_md/plain_md_methods.py +++ b/src/openfe/protocols/openmm_md/plain_md_methods.py @@ -50,7 +50,6 @@ PlainMDProtocolSettings, ) from openfe.protocols.openmm_utils import ( - charge_generation, omm_compute, serialization, settings_validation, @@ -197,6 +196,10 @@ def _validate( # Validate the ChemicalSystem system_validation.validate_chemical_system(stateA) + # Validate small molecule charges + small_ff = self.settings.forcefield_settings.small_molecule_forcefield + system_validation.validate_nondeterministic_charges(stateA, small_ff) + # Validate solvent component if present nonbond = self.settings.forcefield_settings.nonbonded_method system_validation.validate_solvent(stateA, nonbond) @@ -333,32 +336,6 @@ class PlainMDSetupUnit(PlainMDUnitMixin, gufe.ProtocolUnit): Protocol setup unit for plain MD simulations which handles charging, system building and solvation. """ - @staticmethod - def _assign_partial_charges( - charge_settings: OpenFFPartialChargeSettings, - smc_components: dict[SmallMoleculeComponent, OFFMolecule], - ) -> None: - """ - Assign partial charges to SMCs. - - Parameters - ---------- - charge_settings : OpenFFPartialChargeSettings - Settings for controlling how the partial charges are assigned. - smc_components : dict[SmallMoleculeComponent, openff.toolkit.Molecule] - Dictionary of OpenFF Molecules to add, keyed by - SmallMoleculeComponent. - """ - for mol in smc_components.values(): - charge_generation.assign_offmol_partial_charges( - offmol=mol, - overwrite=False, - method=charge_settings.partial_charge_method, - toolkit_backend=charge_settings.off_toolkit_backend, - generate_n_conformers=charge_settings.number_of_conformers, - nagl_model=charge_settings.nagl_model, - ) - def run( self, *, @@ -450,9 +427,6 @@ def run( i: i.to_openff() for i in small_mols } - # a. assign partial charges to smcs - self._assign_partial_charges(charge_settings, smc_components) - # b. get a system generator if output_settings.forcefield_cache is not None: ffcache = self.shared_basepath / output_settings.forcefield_cache diff --git a/src/openfe/protocols/openmm_rfe/hybridtop_protocols.py b/src/openfe/protocols/openmm_rfe/hybridtop_protocols.py index a0cd85e88..5b2bf7552 100644 --- a/src/openfe/protocols/openmm_rfe/hybridtop_protocols.py +++ b/src/openfe/protocols/openmm_rfe/hybridtop_protocols.py @@ -567,6 +567,11 @@ def _validate( system_validation.validate_chemical_system(stateB) self._validate_endstates(stateA, stateB) + # Validate small molecule charges + small_ff = self.settings.forcefield_settings.small_molecule_forcefield + system_validation.validate_nondeterministic_charges(stateA, small_ff) + system_validation.validate_nondeterministic_charges(stateB, small_ff) + # Validate the mapping alchem_comps = system_validation.get_alchemical_components(stateA, stateB) self._validate_mapping(mapping, alchem_comps) diff --git a/src/openfe/protocols/openmm_rfe/hybridtop_units.py b/src/openfe/protocols/openmm_rfe/hybridtop_units.py index 8de7845f2..ff32e76ef 100644 --- a/src/openfe/protocols/openmm_rfe/hybridtop_units.py +++ b/src/openfe/protocols/openmm_rfe/hybridtop_units.py @@ -54,7 +54,6 @@ from ...analysis import plotting from ...utils import log_system_probe, without_oechem_backend from ..openmm_utils import ( - charge_generation, multistate_analysis, omm_compute, settings_validation, @@ -205,32 +204,6 @@ def _get_components( return solvent_comp, protein_comp, small_mols - @staticmethod - def _assign_partial_charges( - charge_settings: OpenFFPartialChargeSettings, - small_mols: dict[SmallMoleculeComponent, OFFMolecule], - ) -> None: - """ - Assign partial charges to the OpenFF Molecules associated with all - the SmallMoleculeComponents in the transformation. - - Parameters - ---------- - charge_settings : OpenFFPartialChargeSettings - Settings for controlling how the partial charges are assigned. - small_mols : dict[SmallMoleculeComponent, openff.toolkit.Molecule] - Dictionary of OpenFF Molecules to add, keyed by - their associated SmallMoleculeComponent. - """ - for smc, mol in small_mols.items(): - charge_generation.assign_offmol_partial_charges( - offmol=mol, - overwrite=False, - method=charge_settings.partial_charge_method, - toolkit_backend=charge_settings.off_toolkit_backend, - generate_n_conformers=charge_settings.number_of_conformers, - nagl_model=charge_settings.nagl_model, - ) @staticmethod def _get_system_generator( @@ -761,9 +734,6 @@ def run( # in the topology alchem_resnames = [assigned[alchem_comps["stateA"][0]]] - # Assign partial charges now to avoid any discrepancies later - self._assign_partial_charges(settings["charge_settings"], small_mols) - ( stateA_system, stateA_topology, diff --git a/src/openfe/protocols/openmm_septop/base_units.py b/src/openfe/protocols/openmm_septop/base_units.py index bf19f8dd5..33188386d 100644 --- a/src/openfe/protocols/openmm_septop/base_units.py +++ b/src/openfe/protocols/openmm_septop/base_units.py @@ -79,7 +79,6 @@ from openfe.utils import log_system_probe, without_oechem_backend from ..openmm_utils import ( - charge_generation, multistate_analysis, settings_validation, system_creation, @@ -467,32 +466,6 @@ def _get_system_generator( ) return system_generator - @staticmethod - def _assign_partial_charges( - partial_charge_settings: OpenFFPartialChargeSettings, - smc_components: dict[SmallMoleculeComponent, OFFMolecule], - ) -> None: - """ - Assign partial charges to OFFMolecules inplace. - - Parameters - ---------- - charge_settings : OpenFFPartialChargeSettings - Settings for controlling how the partial charges are assigned. - smc_components : dict[SmallMoleculeComponent, openff.toolkit.Molecule] - Dictionary of OpenFF Molecules to add, keyed by - SmallMoleculeComponent. - """ - for mol in smc_components.values(): - charge_generation.assign_offmol_partial_charges( - offmol=mol, - overwrite=False, - method=partial_charge_settings.partial_charge_method, - toolkit_backend=partial_charge_settings.off_toolkit_backend, - generate_n_conformers=partial_charge_settings.number_of_conformers, - nagl_model=partial_charge_settings.nagl_model, - ) - def _get_modeller( self, protein_component: Optional[ProteinComponent], diff --git a/src/openfe/protocols/openmm_septop/equil_septop_method.py b/src/openfe/protocols/openmm_septop/equil_septop_method.py index 7baf64273..66c5cde8a 100644 --- a/src/openfe/protocols/openmm_septop/equil_septop_method.py +++ b/src/openfe/protocols/openmm_septop/equil_septop_method.py @@ -485,6 +485,11 @@ def _validate( system_validation.validate_chemical_system(stateB) self._validate_endstates(stateA, stateB) + # Validate small molecule charges + small_ff = self.settings.forcefield_settings.small_molecule_forcefield + system_validation.validate_nondeterministic_charges(stateA, small_ff) + system_validation.validate_nondeterministic_charges(stateB, small_ff) + # Validate the lambda schedule self._validate_lambda_schedule( self.settings.solvent_lambda_settings, diff --git a/src/openfe/protocols/openmm_septop/septop_units.py b/src/openfe/protocols/openmm_septop/septop_units.py index b4d3efd68..cde6f88eb 100644 --- a/src/openfe/protocols/openmm_septop/septop_units.py +++ b/src/openfe/protocols/openmm_septop/septop_units.py @@ -700,9 +700,6 @@ def run( # 3. Get settings settings = self._get_settings() - # 4. Assign partial charges - self._assign_partial_charges(settings["charge_settings"], smc_comps_AB) - # 5. Get the OpenMM systems omm_system_A, omm_topology_A, positions_A, modeller_A, comp_resids_A = ( self.get_system( @@ -1069,9 +1066,6 @@ def run( # 2. Get settings settings = self._get_settings() - # 3. Assign partial charges - self._assign_partial_charges(settings["charge_settings"], smc_comps_AB) - # 4. Update the positions of ligand B: # - solvent: Offset ligand B with respect to ligand A offset = self._get_ligand_offset( diff --git a/src/openfe/protocols/openmm_utils/system_validation.py b/src/openfe/protocols/openmm_utils/system_validation.py index 6ba0bc47a..a28fd69e2 100644 --- a/src/openfe/protocols/openmm_utils/system_validation.py +++ b/src/openfe/protocols/openmm_utils/system_validation.py @@ -414,6 +414,7 @@ def validate_nondeterministic_charges(system: ChemicalSystem, small_molecule_for labels = {"LibraryCharges": {}} # We count library and nagl charges as deterministic + # While users could use a deterministic charge method with increments we don't currently support this if len(labels["LibraryCharges"]) != offmol.n_atoms and "NAGLCharges" not in labels: errmsg = ( f"{smc} from system {system.name} would have am1bcc charges generated at runtime which is non-deterministic. " From 251474c3088e874a3f38c7d3c49ae90b426da61f Mon Sep 17 00:00:00 2001 From: Josh Horton Date: Mon, 17 Aug 2026 11:56:16 +0100 Subject: [PATCH 05/21] fix tests --- .../openmm_afe/equil_solvation_afe_method.py | 2 +- .../openmm_utils/system_validation.py | 15 +- src/openfe/tests/conftest.py | 31 +--- .../openmm_md/test_plain_md_protocol.py | 133 ++---------------- .../openmm_rfe/test_hybrid_top_protocol.py | 67 +++++---- 5 files changed, 64 insertions(+), 184 deletions(-) diff --git a/src/openfe/protocols/openmm_afe/equil_solvation_afe_method.py b/src/openfe/protocols/openmm_afe/equil_solvation_afe_method.py index 28216f40b..c25765c03 100644 --- a/src/openfe/protocols/openmm_afe/equil_solvation_afe_method.py +++ b/src/openfe/protocols/openmm_afe/equil_solvation_afe_method.py @@ -386,7 +386,7 @@ def _validate( self._validate_endstates(stateA, stateB) # validate small molecule charges - small_ff = self.settings.forcefield_settings.small_molecule_forcefield + small_ff = self.settings.solvent_forcefield_settings.small_molecule_forcefield system_validation.validate_nondeterministic_charges(stateA, small_ff) system_validation.validate_nondeterministic_charges(stateB, small_ff) diff --git a/src/openfe/protocols/openmm_utils/system_validation.py b/src/openfe/protocols/openmm_utils/system_validation.py index a28fd69e2..9f5dd0870 100644 --- a/src/openfe/protocols/openmm_utils/system_validation.py +++ b/src/openfe/protocols/openmm_utils/system_validation.py @@ -398,8 +398,17 @@ def validate_nondeterministic_charges(system: ChemicalSystem, small_molecule_for # this will always generate charges at runtime, so raise an error for missing charges ff = None else: - # We do not check for offxml in the name as users can pass the force field contents as a string - ff = ForceField(small_molecule_forcefield) + try: + ff = ForceField(small_molecule_forcefield) + except OSError: + # try again but adding offxml to the end of the force field name if the user passed one of the installed force fields without the extension + try: + ff = ForceField(small_molecule_forcefield + ".offxml") + except OSError as e: + errmsg = ( + f"Could not load force field {small_molecule_forcefield} or {small_molecule_forcefield}.offxml: {e}" + ) + raise ProtocolValidationError(errmsg) for smc in smcs: offmol = smc.to_openff() @@ -415,7 +424,7 @@ def validate_nondeterministic_charges(system: ChemicalSystem, small_molecule_for # We count library and nagl charges as deterministic # While users could use a deterministic charge method with increments we don't currently support this - if len(labels["LibraryCharges"]) != offmol.n_atoms and "NAGLCharges" not in labels: + if not labels["LibraryCharges"] and "NAGLCharges" not in labels: errmsg = ( f"{smc} from system {system.name} would have am1bcc charges generated at runtime which is non-deterministic. " f"Please provide a molecule with pre-computed charges or use library charges instead." diff --git a/src/openfe/tests/conftest.py b/src/openfe/tests/conftest.py index 044ad8864..0ee4778a2 100644 --- a/src/openfe/tests/conftest.py +++ b/src/openfe/tests/conftest.py @@ -384,36 +384,7 @@ def CN_molecule() -> list[SmallMoleculeComponent]: A basic CH3NH2 molecule for quick testing. """ with resources.as_file(resources.files("openfe.tests.data")) as d: - fn = str(d / "CN.sdf") - supp = Chem.SDMolSupplier(str(fn), removeHs=False) - - smc = [SmallMoleculeComponent(i) for i in supp][0] - - return smc - - -@pytest.fixture(scope="function") -def am1bcc_ref_charges(): - ref_chgs = { - "ambertools":[ - 0.146957, -0.918943, 0.025557, 0.025557, - 0.025557, 0.347657, 0.347657 - ] * offunit.elementary_charge, - "openeye": [ - 0.14713, -0.92016, 0.02595, 0.02595, - 0.02595, 0.34759, 0.34759 - ] * offunit.elementary_charge, - "nagl": [ - 0.170413, -0.930417, 0.021593, 0.021593, - 0.021593, 0.347612, 0.347612 - ] * offunit.elementary_charge, - "espaloma": [ - 0.017702, -0.966793, 0.063076, 0.063076, - 0.063076, 0.379931, 0.379931 - ] * offunit.elementary_charge, - } # fmt: skip - return ref_chgs - + yield SmallMoleculeComponent.from_sdf_file(d / "CN.sdf") try: urllib.request.urlopen("https://www.google.com") diff --git a/src/openfe/tests/protocols/openmm_md/test_plain_md_protocol.py b/src/openfe/tests/protocols/openmm_md/test_plain_md_protocol.py index 279797536..16f42ec2e 100644 --- a/src/openfe/tests/protocols/openmm_md/test_plain_md_protocol.py +++ b/src/openfe/tests/protocols/openmm_md/test_plain_md_protocol.py @@ -3,7 +3,7 @@ import json import logging import pathlib -import sys +import re from unittest import mock import gufe @@ -11,13 +11,13 @@ import openmm import pytest from gufe import ChemicalSystem, LigandAtomMapping, SmallMoleculeComponent +from gufe.protocols.errors import ProtocolValidationError from numpy.testing import assert_allclose from openff.units import unit -from openff.units.openmm import from_openmm, to_openmm +from openff.units.openmm import to_openmm from openmm import MonteCarloBarostat, NonbondedForce from openmm import unit as omm_unit from openmmtools.states import ThermodynamicState -from pydantic import ValidationError import openfe from openfe.protocols import openmm_md @@ -28,11 +28,6 @@ PlainMDSimulationUnit, ) from openfe.protocols.openmm_utils import serialization -from openfe.protocols.openmm_utils.charge_generation import ( - HAS_ESPALOMA_CHARGE, - HAS_NAGL, - HAS_OPENEYE, -) from openfe.tests.conftest import HAS_ESPALOMA @@ -275,60 +270,20 @@ def test_dry_run_espaloma_vacuum_user_charges( assert_allclose(charges, expected_charges, rtol=1e-6) -@pytest.mark.parametrize( - "method, backend, ref_key", - [ - ("am1bcc", "ambertools", "ambertools"), - pytest.param( - "am1bcc", - "openeye", - "openeye", - marks=pytest.mark.skipif(not HAS_OPENEYE, reason="needs oechem"), - ), - pytest.param( - "nagl", - "rdkit", - "nagl", - marks=pytest.mark.skipif( - not HAS_NAGL or HAS_OPENEYE or sys.platform.startswith("darwin"), - reason="needs NAGL (without oechem) and/or on macos", - ), - ), - pytest.param( - "espaloma", - "rdkit", - "espaloma", - marks=pytest.mark.skipif(not HAS_ESPALOMA_CHARGE, reason="needs espaloma charge"), - ), - ], -) -def test_dry_run_charge_backends( - CN_molecule, tmp_path, method, backend, ref_key, vac_settings, am1bcc_ref_charges +def test_dry_run_missing_charges( + CN_molecule, tmp_path, vac_settings, ): - vac_settings.partial_charge_settings.partial_charge_method = method - vac_settings.partial_charge_settings.off_toolkit_backend = backend + # an error should be raised if a nondeterministic charge method would be used at run time + vac_settings.partial_charge_settings.partial_charge_method = "am1bcc" + vac_settings.partial_charge_settings.off_toolkit_backend = "ambertools" vac_settings.partial_charge_settings.nagl_model = "openff-gnn-am1bcc-0.1.0-rc.1.pt" protocol = PlainMDProtocol(settings=vac_settings) - csystem = openfe.ChemicalSystem({"ligand": CN_molecule}) + csystem = openfe.ChemicalSystem({"ligand": CN_molecule}, name="CN no charges") - dag = protocol.create(stateA=csystem, stateB=csystem, mapping=None) - md_unit = list(dag.protocol_units)[0] - - result = md_unit.run(dry=True, scratch_basepath=tmp_path, shared_basepath=tmp_path) - system = result["debug"]["system"] - - nonbond = [f for f in system.getForces() if isinstance(f, NonbondedForce)][0] - - charges = [] - for i in range(system.getNumParticles()): - c, s, e = nonbond.getParticleParameters(i) - charges.append(from_openmm(c)) - - charges = unit.Quantity.from_list(charges) - - assert_allclose(am1bcc_ref_charges[ref_key], charges, rtol=1e-4) + with pytest.raises(ProtocolValidationError, match=re.escape("SmallMoleculeComponent(name=) from system CN no charges would have am1bcc charges generated at runtime which is non-deterministic.")): + _ = protocol.create(stateA=csystem, stateB=csystem, mapping=None) def test_dry_many_molecules_solvent(benzene_many_solv_system, tmp_path): @@ -351,72 +306,6 @@ def test_dry_many_molecules_solvent(benzene_many_solv_system, tmp_path): dag_unit.run(dry=True, scratch_basepath=tmp_path, shared_basepath=tmp_path)["debug"]["system"] -BENZ = """\ -benzene - PyMOL2.5 3D 0 - - 12 12 0 0 0 0 0 0 0 0999 V2000 - 1.4045 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - 0.7022 1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - -0.7023 1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - -1.4045 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - -0.7023 -1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - 0.7023 -1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - 2.5079 -0.0000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - 1.2540 2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - -1.2540 2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - -2.5079 -0.0000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - -1.2540 -2.1719 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - 1.2540 -2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - 1 2 2 0 0 0 0 - 1 6 1 0 0 0 0 - 1 7 1 0 0 0 0 - 2 3 1 0 0 0 0 - 2 8 1 0 0 0 0 - 3 4 2 0 0 0 0 - 3 9 1 0 0 0 0 - 4 5 1 0 0 0 0 - 4 10 1 0 0 0 0 - 5 6 2 0 0 0 0 - 5 11 1 0 0 0 0 - 6 12 1 0 0 0 0 -M END -$$$$ -""" - - -PYRIDINE = """\ -pyridine - PyMOL2.5 3D 0 - - 11 11 0 0 0 0 0 0 0 0999 V2000 - 1.4045 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - -0.7023 1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - -1.4045 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - -0.7023 -1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - 0.7023 -1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - 2.4940 -0.0325 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - 1.2473 -2.1604 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - -1.2473 -2.1604 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - -2.4945 -0.0000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - -1.2753 2.1437 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - 0.7525 1.3034 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 - 1 5 1 0 0 0 0 - 1 6 1 0 0 0 0 - 1 11 2 0 0 0 0 - 2 3 2 0 0 0 0 - 2 10 1 0 0 0 0 - 3 4 1 0 0 0 0 - 3 9 1 0 0 0 0 - 4 5 2 0 0 0 0 - 4 8 1 0 0 0 0 - 5 7 1 0 0 0 0 - 2 11 1 0 0 0 0 -M END -$$$$ -""" - - def test_dry_run_ligand_tip4p(benzene_system, tmp_path): """ Test that we can create a system with virtual sites in the diff --git a/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py b/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py index 08b857380..5519c0c4d 100644 --- a/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py +++ b/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py @@ -410,7 +410,7 @@ def test_dry_many_molecules_solvent( BENZ = """\ benzene - PyMOL2.5 3D 0 + RDKit 3D 12 12 0 0 0 0 0 0 0 0999 V2000 1.4045 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 @@ -425,26 +425,32 @@ def test_dry_many_molecules_solvent( -2.5079 -0.0000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 -1.2540 -2.1719 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 1.2540 -2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - 1 2 2 0 0 0 0 - 1 6 1 0 0 0 0 - 1 7 1 0 0 0 0 - 2 3 1 0 0 0 0 - 2 8 1 0 0 0 0 - 3 4 2 0 0 0 0 - 3 9 1 0 0 0 0 - 4 5 1 0 0 0 0 - 4 10 1 0 0 0 0 - 5 6 2 0 0 0 0 - 5 11 1 0 0 0 0 - 6 12 1 0 0 0 0 + 1 2 2 0 + 1 6 1 0 + 1 7 1 0 + 2 3 1 0 + 2 8 1 0 + 3 4 2 0 + 3 9 1 0 + 4 5 1 0 + 4 10 1 0 + 5 6 2 0 + 5 11 1 0 + 6 12 1 0 M END + +> +benzene + +> +-0.13016000265876451 -0.13009999568263689 -0.13009999568263689 -0.13009999568263689 -0.13009999568263689 -0.13009999568263689 0.13010999684532484 0.13010999684532484 0.13010999684532484 0.13010999684532484 0.13010999684532484 0.13010999684532484 + $$$$ """ - PYRIDINE = """\ pyridine - PyMOL2.5 3D 0 + RDKit 3D 11 11 0 0 0 0 0 0 0 0999 V2000 1.4045 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 @@ -458,25 +464,30 @@ def test_dry_many_molecules_solvent( -2.4945 -0.0000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 -1.2753 2.1437 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 0.7525 1.3034 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 - 1 5 1 0 0 0 0 - 1 6 1 0 0 0 0 - 1 11 2 0 0 0 0 - 2 3 2 0 0 0 0 - 2 10 1 0 0 0 0 - 3 4 1 0 0 0 0 - 3 9 1 0 0 0 0 - 4 5 2 0 0 0 0 - 4 8 1 0 0 0 0 - 5 7 1 0 0 0 0 - 2 11 1 0 0 0 0 + 1 5 2 0 + 1 6 1 0 + 1 11 1 0 + 2 3 1 0 + 2 10 1 0 + 3 4 2 0 + 3 9 1 0 + 4 5 1 0 + 4 8 1 0 + 5 7 1 0 + 2 11 2 0 M END + +> +0.39228000661188905 0.39228000661188905 -0.24653000215237791 -0.093170007860118698 -0.24653000215237791 0.021279994229024105 0.14280999621207063 0.1373999955301935 0.14280999621207063 +0.021279994229024105 -0.66390997747128655 + $$$$ """ def test_setup_core_element_change(vac_settings, tmp_path): - benz = openfe.SmallMoleculeComponent(Chem.MolFromMolBlock(BENZ, removeHs=False)) - pyr = openfe.SmallMoleculeComponent(Chem.MolFromMolBlock(PYRIDINE, removeHs=False)) + benz = openfe.SmallMoleculeComponent.from_sdf_string(BENZ) + pyr = openfe.SmallMoleculeComponent.from_sdf_string(PYRIDINE) mapping = openfe.LigandAtomMapping( benz, pyr, {0: 0, 1: 10, 2: 1, 3: 2, 4: 3, 5: 4, 6: 5, 8: 9, 9: 8, 10: 7, 11: 6} From 045954d3d245d3af6e608c40ee28a79b85c21e1d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 17 Aug 2026 10:57:24 +0000 Subject: [PATCH 06/21] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../protocols/openmm_rfe/hybridtop_units.py | 1 - .../openmm_utils/system_validation.py | 6 +-- src/openfe/tests/conftest.py | 1 + .../openmm_md/test_plain_md_protocol.py | 11 +++- .../tests/protocols/test_openmmutils.py | 51 +++++++++++-------- 5 files changed, 42 insertions(+), 28 deletions(-) diff --git a/src/openfe/protocols/openmm_rfe/hybridtop_units.py b/src/openfe/protocols/openmm_rfe/hybridtop_units.py index ff32e76ef..63fbc511a 100644 --- a/src/openfe/protocols/openmm_rfe/hybridtop_units.py +++ b/src/openfe/protocols/openmm_rfe/hybridtop_units.py @@ -204,7 +204,6 @@ def _get_components( return solvent_comp, protein_comp, small_mols - @staticmethod def _get_system_generator( settings: dict[str, SettingsBaseModel], diff --git a/src/openfe/protocols/openmm_utils/system_validation.py b/src/openfe/protocols/openmm_utils/system_validation.py index 9f5dd0870..96b0bee82 100644 --- a/src/openfe/protocols/openmm_utils/system_validation.py +++ b/src/openfe/protocols/openmm_utils/system_validation.py @@ -405,9 +405,7 @@ def validate_nondeterministic_charges(system: ChemicalSystem, small_molecule_for try: ff = ForceField(small_molecule_forcefield + ".offxml") except OSError as e: - errmsg = ( - f"Could not load force field {small_molecule_forcefield} or {small_molecule_forcefield}.offxml: {e}" - ) + errmsg = f"Could not load force field {small_molecule_forcefield} or {small_molecule_forcefield}.offxml: {e}" raise ProtocolValidationError(errmsg) for smc in smcs: @@ -424,7 +422,7 @@ def validate_nondeterministic_charges(system: ChemicalSystem, small_molecule_for # We count library and nagl charges as deterministic # While users could use a deterministic charge method with increments we don't currently support this - if not labels["LibraryCharges"] and "NAGLCharges" not in labels: + if not labels["LibraryCharges"] and "NAGLCharges" not in labels: errmsg = ( f"{smc} from system {system.name} would have am1bcc charges generated at runtime which is non-deterministic. " f"Please provide a molecule with pre-computed charges or use library charges instead." diff --git a/src/openfe/tests/conftest.py b/src/openfe/tests/conftest.py index 0ee4778a2..0edcc533a 100644 --- a/src/openfe/tests/conftest.py +++ b/src/openfe/tests/conftest.py @@ -386,6 +386,7 @@ def CN_molecule() -> list[SmallMoleculeComponent]: with resources.as_file(resources.files("openfe.tests.data")) as d: yield SmallMoleculeComponent.from_sdf_file(d / "CN.sdf") + try: urllib.request.urlopen("https://www.google.com") except urllib.error.URLError: # -no-cov- diff --git a/src/openfe/tests/protocols/openmm_md/test_plain_md_protocol.py b/src/openfe/tests/protocols/openmm_md/test_plain_md_protocol.py index 16f42ec2e..98e67fa2e 100644 --- a/src/openfe/tests/protocols/openmm_md/test_plain_md_protocol.py +++ b/src/openfe/tests/protocols/openmm_md/test_plain_md_protocol.py @@ -271,7 +271,9 @@ def test_dry_run_espaloma_vacuum_user_charges( def test_dry_run_missing_charges( - CN_molecule, tmp_path, vac_settings, + CN_molecule, + tmp_path, + vac_settings, ): # an error should be raised if a nondeterministic charge method would be used at run time vac_settings.partial_charge_settings.partial_charge_method = "am1bcc" @@ -282,7 +284,12 @@ def test_dry_run_missing_charges( csystem = openfe.ChemicalSystem({"ligand": CN_molecule}, name="CN no charges") - with pytest.raises(ProtocolValidationError, match=re.escape("SmallMoleculeComponent(name=) from system CN no charges would have am1bcc charges generated at runtime which is non-deterministic.")): + with pytest.raises( + ProtocolValidationError, + match=re.escape( + "SmallMoleculeComponent(name=) from system CN no charges would have am1bcc charges generated at runtime which is non-deterministic." + ), + ): _ = protocol.create(stateA=csystem, stateB=csystem, mapping=None) diff --git a/src/openfe/tests/protocols/test_openmmutils.py b/src/openfe/tests/protocols/test_openmmutils.py index 62d7c3d59..bb42d5031 100644 --- a/src/openfe/tests/protocols/test_openmmutils.py +++ b/src/openfe/tests/protocols/test_openmmutils.py @@ -17,8 +17,8 @@ from gufe.protocols.errors import ProtocolValidationError from gufe.settings import OpenMMSystemGeneratorFFSettings, ThermoSettings from numpy.testing import assert_allclose, assert_equal -from openff.toolkit import Molecule as OFFMol from openff.toolkit import ForceField +from openff.toolkit import Molecule as OFFMol from openff.toolkit.utils.toolkit_registry import ToolkitRegistry from openff.toolkit.utils.toolkits import RDKitToolkitWrapper from openff.units import unit @@ -1444,51 +1444,61 @@ def benzene_charged_system(self, benzene_modifications): @pytest.fixture def benzene_no_charge_system(self, benzene_modifications_uncharged): - return ChemicalSystem({"ligand": benzene_modifications_uncharged["benzene"]}, name="no charges") + return ChemicalSystem( + {"ligand": benzene_modifications_uncharged["benzene"]}, name="no charges" + ) def test_gaff_with_molecule_charges(self, benzene_charged_system): system_validation.validate_nondeterministic_charges( - benzene_charged_system, - small_molecule_forcefield="gaff-2.11" + benzene_charged_system, small_molecule_forcefield="gaff-2.11" ) def test_gaff_no_charges(self, benzene_no_charge_system): - with pytest.raises(ProtocolValidationError, match=re.escape("SmallMoleculeComponent(name=benzene) from system no charges would have am1bcc charges")): + with pytest.raises( + ProtocolValidationError, + match=re.escape( + "SmallMoleculeComponent(name=benzene) from system no charges would have am1bcc charges" + ), + ): system_validation.validate_nondeterministic_charges( - benzene_no_charge_system, - small_molecule_forcefield="gaff-2.11" + benzene_no_charge_system, small_molecule_forcefield="gaff-2.11" ) def test_espaloma_with_molecule_charges(self, benzene_charged_system): system_validation.validate_nondeterministic_charges( - benzene_charged_system, - small_molecule_forcefield="espaloma-0.3.2" + benzene_charged_system, small_molecule_forcefield="espaloma-0.3.2" ) def test_espaloma_no_charges(self, benzene_no_charge_system): - with pytest.raises(ProtocolValidationError, match=re.escape("SmallMoleculeComponent(name=benzene) from system no charges would have am1bcc charges")): + with pytest.raises( + ProtocolValidationError, + match=re.escape( + "SmallMoleculeComponent(name=benzene) from system no charges would have am1bcc charges" + ), + ): system_validation.validate_nondeterministic_charges( - benzene_no_charge_system, - small_molecule_forcefield="espaloma-0.3.2" + benzene_no_charge_system, small_molecule_forcefield="espaloma-0.3.2" ) def test_openff_with_molecule_charges(self, benzene_charged_system): system_validation.validate_nondeterministic_charges( - benzene_charged_system, - small_molecule_forcefield="openff-2.2.0.offxml" + benzene_charged_system, small_molecule_forcefield="openff-2.2.0.offxml" ) def test_openff_no_charges(self, benzene_no_charge_system): - with pytest.raises(ProtocolValidationError, match=re.escape("SmallMoleculeComponent(name=benzene) from system no charges would have am1bcc charges")): + with pytest.raises( + ProtocolValidationError, + match=re.escape( + "SmallMoleculeComponent(name=benzene) from system no charges would have am1bcc charges" + ), + ): system_validation.validate_nondeterministic_charges( - benzene_no_charge_system, - small_molecule_forcefield="openff-2.2.0.offxml" + benzene_no_charge_system, small_molecule_forcefield="openff-2.2.0.offxml" ) def test_openff_nagl_no_charges(self, benzene_no_charge_system): system_validation.validate_nondeterministic_charges( - benzene_no_charge_system, - small_molecule_forcefield="openff-2.3.0.offxml" + benzene_no_charge_system, small_molecule_forcefield="openff-2.3.0.offxml" ) def test_openff_lib_charges_no_charges(self, benzene_no_charge_system, benzene_modifications): @@ -1503,6 +1513,5 @@ def test_openff_lib_charges_no_charges(self, benzene_no_charge_system, benzene_m lib_charge_handler.add_parameter(parameter=lib_charge) # run the validation system_validation.validate_nondeterministic_charges( - benzene_no_charge_system, - small_molecule_forcefield=ff.to_string() + benzene_no_charge_system, small_molecule_forcefield=ff.to_string() ) From 6c0d7d4b0a879ccb1c68e0a406ceeaa4758a9d2d Mon Sep 17 00:00:00 2001 From: Josh Horton Date: Mon, 17 Aug 2026 14:06:11 +0100 Subject: [PATCH 07/21] fix protocol charge generation tests --- src/openfe/tests/conftest.py | 24 ++++- .../openmm_ahfe/test_ahfe_protocol.py | 74 +++------------ .../openmm_md/test_plain_md_protocol.py | 1 - .../openmm_rfe/test_hybrid_top_protocol.py | 91 ++++--------------- 4 files changed, 55 insertions(+), 135 deletions(-) diff --git a/src/openfe/tests/conftest.py b/src/openfe/tests/conftest.py index 0edcc533a..f6654edc0 100644 --- a/src/openfe/tests/conftest.py +++ b/src/openfe/tests/conftest.py @@ -379,7 +379,7 @@ def fepplus_network(): @pytest.fixture() -def CN_molecule() -> list[SmallMoleculeComponent]: +def CN_molecule(): """ A basic CH3NH2 molecule for quick testing. """ @@ -387,6 +387,28 @@ def CN_molecule() -> list[SmallMoleculeComponent]: yield SmallMoleculeComponent.from_sdf_file(d / "CN.sdf") +@pytest.fixture(scope="function") +def am1bcc_ref_charges(): + ref_chgs = { + "ambertools":[ + 0.146957, -0.918943, 0.025557, 0.025557, + 0.025557, 0.347657, 0.347657 + ] * offunit.elementary_charge, + "openeye": [ + 0.14713, -0.92016, 0.02595, 0.02595, + 0.02595, 0.34759, 0.34759 + ] * offunit.elementary_charge, + "nagl": [ + 0.170413, -0.930417, 0.021593, 0.021593, + 0.021593, 0.347612, 0.347612 + ] * offunit.elementary_charge, + "espaloma": [ + 0.017702, -0.966793, 0.063076, 0.063076, + 0.063076, 0.379931, 0.379931 + ] * offunit.elementary_charge, + } # fmt: skip + return ref_chgs + try: urllib.request.urlopen("https://www.google.com") except urllib.error.URLError: # -no-cov- diff --git a/src/openfe/tests/protocols/openmm_ahfe/test_ahfe_protocol.py b/src/openfe/tests/protocols/openmm_ahfe/test_ahfe_protocol.py index 38d83d393..2cac9bf67 100644 --- a/src/openfe/tests/protocols/openmm_ahfe/test_ahfe_protocol.py +++ b/src/openfe/tests/protocols/openmm_ahfe/test_ahfe_protocol.py @@ -1,5 +1,6 @@ # This code is part of OpenFE and is licensed under the MIT license. # For details, see https://github.com/OpenFreeEnergy/openfe +import re import sys from math import sqrt from unittest import mock @@ -8,6 +9,7 @@ import mdtraj as mdt import numpy as np import pytest +from gufe.protocols import ProtocolValidationError from numpy.testing import assert_allclose from openff.units import unit as offunit from openff.units.openmm import ensure_quantity, from_openmm @@ -619,76 +621,30 @@ def assign_fictitious_charges(offmol): assert pytest.approx(c) == prop_chgs[i] -@pytest.mark.parametrize( - "method, backend, ref_key", - [ - ("am1bcc", "ambertools", "ambertools"), - pytest.param( - "am1bcc", - "openeye", - "openeye", - marks=pytest.mark.skipif(not HAS_OPENEYE, reason="needs oechem"), - ), - pytest.param( - "nagl", - "rdkit", - "nagl", - marks=pytest.mark.skipif( - not HAS_NAGL or HAS_OPENEYE or sys.platform.startswith("darwin"), - reason="needs NAGL (without oechem) and/or on macos", - ), - ), - pytest.param( - "espaloma", - "rdkit", - "espaloma", - marks=pytest.mark.skipif(not HAS_ESPALOMA_CHARGE, reason="needs espaloma charge"), - ), - ], -) -def test_dry_run_charge_backends( - CN_molecule, tmp_path, method, backend, ref_key, protocol_dry_settings, am1bcc_ref_charges +def test_dry_run_missing_charges( + CN_molecule, protocol_dry_settings ): """ - Check that partial charge generation with different backends - works as expected. + Make sure an error is raised if a nondeterministic charge method would be used at run time """ - protocol_dry_settings.partial_charge_settings.partial_charge_method = method - protocol_dry_settings.partial_charge_settings.off_toolkit_backend = backend + protocol_dry_settings.partial_charge_settings.partial_charge_method = "am1bcc" + protocol_dry_settings.partial_charge_settings.off_toolkit_backend = "ambertools" protocol_dry_settings.partial_charge_settings.nagl_model = "openff-gnn-am1bcc-0.1.0-rc.1.pt" protocol = openmm_afe.AbsoluteSolvationProtocol(settings=protocol_dry_settings) # Create ChemicalSystems - stateA = ChemicalSystem({"benzene": CN_molecule, "solvent": SolventComponent()}) + stateA = ChemicalSystem({"benzene": CN_molecule, "solvent": SolventComponent()}, name="CN no charges") stateB = ChemicalSystem({"solvent": SolventComponent()}) - # Create DAG from protocol, get the vacuum and solvent units - # and eventually dry run the first solvent unit - dag = protocol.create(stateA=stateA, stateB=stateB, mapping=None) - prot_units = list(dag.protocol_units) - - vac_setup_units = _get_units(prot_units, UNIT_TYPES["vacuum"]["setup"]) - - # check vac_unit charges - results = vac_setup_units[0].run(dry=True, scratch_basepath=tmp_path, shared_basepath=tmp_path) - system = results["alchem_system"] - nonbond = [f for f in system.getForces() if isinstance(f, CustomNonbondedForce)] - assert len(nonbond) == 4 - - custom_elec = [n for n in nonbond if n.getGlobalParameterName(0) == "lambda_electrostatics"][0] - - charges = [] - for i in range(system.getNumParticles()): - c, s = custom_elec.getParticleParameters(i) - charges.append(c) - - assert_allclose( - am1bcc_ref_charges[ref_key], - charges * offunit.elementary_charge, - rtol=1e-4, - ) + with pytest.raises( + ProtocolValidationError, + match=re.escape( + "SmallMoleculeComponent(name=) from system CN no charges would have am1bcc charges generated at runtime which is non-deterministic." + ) + ): + _ = protocol.create(stateA=stateA, stateB=stateB, mapping=None) @pytest.fixture diff --git a/src/openfe/tests/protocols/openmm_md/test_plain_md_protocol.py b/src/openfe/tests/protocols/openmm_md/test_plain_md_protocol.py index 98e67fa2e..3a33fe222 100644 --- a/src/openfe/tests/protocols/openmm_md/test_plain_md_protocol.py +++ b/src/openfe/tests/protocols/openmm_md/test_plain_md_protocol.py @@ -272,7 +272,6 @@ def test_dry_run_espaloma_vacuum_user_charges( def test_dry_run_missing_charges( CN_molecule, - tmp_path, vac_settings, ): # an error should be raised if a nondeterministic charge method would be used at run time diff --git a/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py b/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py index 7823e1fcf..b012f032e 100644 --- a/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py +++ b/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py @@ -3,6 +3,7 @@ import copy import json import logging +import re import sys import xml.etree.ElementTree as ET from importlib import resources @@ -16,6 +17,8 @@ import numpy as np import openmm import pytest +from gufe import LigandAtomMapping +from gufe.protocols import ProtocolValidationError from kartograf import KartografAtomMapper from kartograf.atom_aligner import align_mol_shape from numpy.testing import assert_allclose @@ -742,38 +745,12 @@ def test_setup_ligand_system_cutoff( assert f_cutoff == cutoff -@pytest.mark.parametrize( - "method, backend, ref_key", - [ - ("am1bcc", "ambertools", "ambertools"), - pytest.param( - "am1bcc", - "openeye", - "openeye", - marks=pytest.mark.skipif(not HAS_OPENEYE, reason="needs oechem"), - ), - pytest.param( - "nagl", - "rdkit", - "nagl", - marks=pytest.mark.skipif( - not HAS_NAGL or HAS_OPENEYE or sys.platform.startswith("darwin"), - reason="needs NAGL (without oechem) and/or on macos", - ), - ), - pytest.param( - "espaloma", - "rdkit", - "espaloma", - marks=pytest.mark.skipif(not HAS_ESPALOMA_CHARGE, reason="needs espaloma charge"), - ), - ], -) def test_setup_charge_backends( - CN_molecule, tmp_path, method, backend, ref_key, vac_settings, am1bcc_ref_charges + CN_molecule, vac_settings, ): - vac_settings.partial_charge_settings.partial_charge_method = method - vac_settings.partial_charge_settings.off_toolkit_backend = backend + # make sure an error is raised if a nondeterministic charge method would be used at run time + vac_settings.partial_charge_settings.partial_charge_method = "am1bcc" + vac_settings.partial_charge_settings.off_toolkit_backend = "ambertools" vac_settings.partial_charge_settings.nagl_model = "openff-gnn-am1bcc-0.1.0-rc.1.pt" protocol = openmm_rfe.RelativeHybridTopologyProtocol( @@ -784,51 +761,17 @@ def test_setup_charge_backends( offmolB = Molecule.from_smiles("CCN") offmolB.generate_conformers() molB = openfe.SmallMoleculeComponent.from_openff(offmolB) - a_molB = align_mol_shape(molB, ref_mol=CN_molecule) - mapper = KartografAtomMapper(atom_map_hydrogens=True) - mapping = next(mapper.suggest_mappings(CN_molecule, a_molB)) - - systemA = openfe.ChemicalSystem({"l": CN_molecule}) - systemB = openfe.ChemicalSystem({"l": a_molB}) - - dag = protocol.create(stateA=systemA, stateB=systemB, mapping=mapping) - - dag_setup_unit = [pu for pu in dag.protocol_units if isinstance(pu, HybridTopologySetupUnit)][0] - - results = dag_setup_unit.run(dry=True, scratch_basepath=tmp_path, shared_basepath=tmp_path) - htf = results["hybrid_factory"] - hybrid_system = results["hybrid_system"] - - # get the standard nonbonded force - nonbond = [f for f in hybrid_system.getForces() if isinstance(f, NonbondedForce)] - assert len(nonbond) == 1 - - # get the particle parameter offsets - c_offsets = {} - for i in range(nonbond[0].getNumParticleParameterOffsets()): - offset = nonbond[0].getParticleParameterOffset(i) - c_offsets[offset[1]] = ensure_quantity(offset[2], "openff") + mapping = LigandAtomMapping(componentA=CN_molecule, componentB=molB, componentA_to_componentB={0: 1}) + systemA = openfe.ChemicalSystem({"l": CN_molecule}, name="CN no charges") + systemB = openfe.ChemicalSystem({"l": molB}) - # See the user charges test below for an idea of what we're doing here - # In this particular case we are solely checking that the old atoms - # match the reference charges in am1bcc_ref_charges - for i in range(hybrid_system.getNumParticles()): - c, s, e = nonbond[0].getParticleParameters(i) - # get the particle charge (c) - c = ensure_quantity(c, "openff") - # particle charge (c) is equal to molA particle charge - # offset (c_offsets) is equal to -(molA particle charge) - if i in htf._atom_classes["unique_old_atoms"]: - idx = htf._hybrid_to_old_map[i] - ref = am1bcc_ref_charges[ref_key][idx] - np.testing.assert_allclose(c, ref, rtol=1e-4) - np.testing.assert_allclose(c_offsets[i], -ref, rtol=1e-4) - # particle charge (c) is equal to molA particle charge - # offset (c_offsets) is equal to difference between molB and molA - elif i in htf._atom_classes["core_atoms"]: - old_i = htf._hybrid_to_old_map[i] - ref = am1bcc_ref_charges[ref_key][i] - np.testing.assert_allclose(c, ref, rtol=1e-4) + with pytest.raises( + ProtocolValidationError, + match=re.escape( + "SmallMoleculeComponent(name=) from system CN no charges would have am1bcc charges generated at runtime which is non-deterministic." + ) + ): + _ = protocol.create(stateA=systemA, stateB=systemB, mapping=mapping) def test_setup_same_mol_different_charges(benzene_modifications_uncharged, vac_settings, tmp_path): From 960b874ca4b47d36619d948d9a45fafc2ef40ca2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 17 Aug 2026 13:17:36 +0000 Subject: [PATCH 08/21] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/openfe/tests/conftest.py | 1 + .../tests/protocols/openmm_ahfe/test_ahfe_protocol.py | 10 +++++----- .../protocols/openmm_rfe/test_hybrid_top_protocol.py | 9 ++++++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/openfe/tests/conftest.py b/src/openfe/tests/conftest.py index f6654edc0..9847b0e7b 100644 --- a/src/openfe/tests/conftest.py +++ b/src/openfe/tests/conftest.py @@ -409,6 +409,7 @@ def am1bcc_ref_charges(): } # fmt: skip return ref_chgs + try: urllib.request.urlopen("https://www.google.com") except urllib.error.URLError: # -no-cov- diff --git a/src/openfe/tests/protocols/openmm_ahfe/test_ahfe_protocol.py b/src/openfe/tests/protocols/openmm_ahfe/test_ahfe_protocol.py index 2cac9bf67..7320261d1 100644 --- a/src/openfe/tests/protocols/openmm_ahfe/test_ahfe_protocol.py +++ b/src/openfe/tests/protocols/openmm_ahfe/test_ahfe_protocol.py @@ -621,9 +621,7 @@ def assign_fictitious_charges(offmol): assert pytest.approx(c) == prop_chgs[i] -def test_dry_run_missing_charges( - CN_molecule, protocol_dry_settings -): +def test_dry_run_missing_charges(CN_molecule, protocol_dry_settings): """ Make sure an error is raised if a nondeterministic charge method would be used at run time """ @@ -634,7 +632,9 @@ def test_dry_run_missing_charges( protocol = openmm_afe.AbsoluteSolvationProtocol(settings=protocol_dry_settings) # Create ChemicalSystems - stateA = ChemicalSystem({"benzene": CN_molecule, "solvent": SolventComponent()}, name="CN no charges") + stateA = ChemicalSystem( + {"benzene": CN_molecule, "solvent": SolventComponent()}, name="CN no charges" + ) stateB = ChemicalSystem({"solvent": SolventComponent()}) @@ -642,7 +642,7 @@ def test_dry_run_missing_charges( ProtocolValidationError, match=re.escape( "SmallMoleculeComponent(name=) from system CN no charges would have am1bcc charges generated at runtime which is non-deterministic." - ) + ), ): _ = protocol.create(stateA=stateA, stateB=stateB, mapping=None) diff --git a/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py b/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py index b012f032e..fd1134c87 100644 --- a/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py +++ b/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py @@ -746,7 +746,8 @@ def test_setup_ligand_system_cutoff( def test_setup_charge_backends( - CN_molecule, vac_settings, + CN_molecule, + vac_settings, ): # make sure an error is raised if a nondeterministic charge method would be used at run time vac_settings.partial_charge_settings.partial_charge_method = "am1bcc" @@ -761,7 +762,9 @@ def test_setup_charge_backends( offmolB = Molecule.from_smiles("CCN") offmolB.generate_conformers() molB = openfe.SmallMoleculeComponent.from_openff(offmolB) - mapping = LigandAtomMapping(componentA=CN_molecule, componentB=molB, componentA_to_componentB={0: 1}) + mapping = LigandAtomMapping( + componentA=CN_molecule, componentB=molB, componentA_to_componentB={0: 1} + ) systemA = openfe.ChemicalSystem({"l": CN_molecule}, name="CN no charges") systemB = openfe.ChemicalSystem({"l": molB}) @@ -769,7 +772,7 @@ def test_setup_charge_backends( ProtocolValidationError, match=re.escape( "SmallMoleculeComponent(name=) from system CN no charges would have am1bcc charges generated at runtime which is non-deterministic." - ) + ), ): _ = protocol.create(stateA=systemA, stateB=systemB, mapping=mapping) From 96b5d24a253e87c4042a3ba081a499970451ab29 Mon Sep 17 00:00:00 2001 From: Josh Horton Date: Mon, 17 Aug 2026 16:02:24 +0100 Subject: [PATCH 09/21] fix tests with missing charges --- src/openfe/tests/conftest.py | 7 +- .../data/openmm_rfe/charged_benzenes.sdf | 109 ++++++++++-------- .../openmm_rfe/test_hybrid_top_protocol.py | 3 + 3 files changed, 71 insertions(+), 48 deletions(-) diff --git a/src/openfe/tests/conftest.py b/src/openfe/tests/conftest.py index 9847b0e7b..7449486a4 100644 --- a/src/openfe/tests/conftest.py +++ b/src/openfe/tests/conftest.py @@ -27,6 +27,8 @@ from openfe.protocols.openmm_utils.serialization import deserialize from openfe.tests.protocols.openmm_rfe.helpers import make_htf +from openff.toolkit import Molecule + class SlowTests: """Plugin for handling fixtures that skips slow tests @@ -189,8 +191,9 @@ def atom_mapping_basic_test_files(): ]: with resources.as_file(resources.files("openfe.tests.data.lomap_basic")) as d: fn = str(d / (f + ".mol2")) - mol = Chem.MolFromMol2File(fn, removeHs=False) - files[f] = SmallMoleculeComponent(mol, name=f) + # go via openff to make sure the partial charges are pulled + mol = Molecule.from_file(fn) + files[f] = SmallMoleculeComponent.from_openff(mol, name=f) return files diff --git a/src/openfe/tests/data/openmm_rfe/charged_benzenes.sdf b/src/openfe/tests/data/openmm_rfe/charged_benzenes.sdf index ea2735835..80853c45a 100644 --- a/src/openfe/tests/data/openmm_rfe/charged_benzenes.sdf +++ b/src/openfe/tests/data/openmm_rfe/charged_benzenes.sdf @@ -1,5 +1,5 @@ benzene - PyMOL2.5 3D 0 + RDKit 3D 12 12 0 0 0 0 0 0 0 0999 V2000 1.4045 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 @@ -14,22 +14,27 @@ benzene -2.5079 -0.0000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 -1.2540 -2.1719 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 1.2540 -2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - 1 2 2 0 0 0 0 - 1 6 1 0 0 0 0 - 1 7 1 0 0 0 0 - 2 3 1 0 0 0 0 - 2 8 1 0 0 0 0 - 3 4 2 0 0 0 0 - 3 9 1 0 0 0 0 - 4 5 1 0 0 0 0 - 4 10 1 0 0 0 0 - 5 6 2 0 0 0 0 - 5 11 1 0 0 0 0 - 6 12 1 0 0 0 0 + 1 2 2 0 + 1 6 1 0 + 1 7 1 0 + 2 3 1 0 + 2 8 1 0 + 3 4 2 0 + 3 9 1 0 + 4 5 1 0 + 4 10 1 0 + 5 6 2 0 + 5 11 1 0 + 6 12 1 0 M END + +> +-0.13019000614682832 -0.1301299991707007 -0.1301299991707007 -0.1301299991707007 -0.1301299991707007 -0.1301299991707007 0.13014000033338866 0.13014000033338866 0.13014000033338866 +0.13014000033338866 0.13014000033338866 0.13014000033338866 + $$$$ aniline - PyMOL2.5 3D 0 + RDKit 3D 15 15 0 0 0 0 0 0 0 0999 V2000 1.4045 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 @@ -41,36 +46,42 @@ aniline 3.2106 0.8251 0.4756 H 0 0 0 0 0 0 0 0 0 0 0 0 3.2117 -0.8238 0.4772 H 0 0 0 0 0 0 0 0 0 0 0 0 3.2117 -0.0000 -0.9520 H 0 0 0 0 0 0 0 0 0 0 0 0 - 2.8745 -0.0000 0.0000 N 0 3 0 0 0 0 0 0 0 0 0 0 + 2.8745 -0.0000 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 1.2540 2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 -1.2540 2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 -2.5079 -0.0000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 -1.2540 -2.1719 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 1.2540 -2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - 1 2 2 0 0 0 0 - 1 6 1 0 0 0 0 - 1 10 1 0 0 0 0 - 2 3 1 0 0 0 0 - 2 11 1 0 0 0 0 - 3 4 2 0 0 0 0 - 3 12 1 0 0 0 0 - 4 5 1 0 0 0 0 - 4 13 1 0 0 0 0 - 5 6 2 0 0 0 0 - 5 14 1 0 0 0 0 - 6 15 1 0 0 0 0 - 7 10 1 0 0 0 0 - 8 10 1 0 0 0 0 - 9 10 1 0 0 0 0 + 1 2 2 0 + 1 6 1 0 + 1 10 1 0 + 2 3 1 0 + 2 11 1 0 + 3 4 2 0 + 3 12 1 0 + 4 5 1 0 + 4 13 1 0 + 5 6 2 0 + 5 14 1 0 + 6 15 1 0 + 7 10 1 0 + 8 10 1 0 + 9 10 1 0 +M CHG 1 10 1 M END + +> +-0.082359999914964038 -0.09780000199874242 -0.098719999690850574 -0.061870001753171283 -0.098719999690850574 -0.09780000199874242 0.46404001067082085 0.46404001067082085 +0.46404001067082085 -0.69748002340396242 0.15680000136295955 0.17632999966541926 0.17636999438206355 0.17632999966541926 0.15680000136295955 + $$$$ benzoic_acid - PyMOL2.5 3D 0 + RDKit 3D 14 14 0 0 0 0 0 0 0 0999 V2000 1.4045 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.7445 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - 3.2607 -1.0833 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 + 3.2607 -1.0833 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 0.7022 1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.7023 1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.4265 1.0159 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 @@ -82,19 +93,25 @@ benzoic_acid -2.5079 -0.0000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 -1.2540 -2.1719 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 1.2540 -2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - 1 2 1 0 0 0 0 - 1 4 2 0 0 0 0 - 1 9 1 0 0 0 0 - 2 3 1 0 0 0 0 - 2 6 2 0 0 0 0 - 4 5 1 0 0 0 0 - 4 10 1 0 0 0 0 - 5 7 2 0 0 0 0 - 5 11 1 0 0 0 0 - 7 8 1 0 0 0 0 - 7 12 1 0 0 0 0 - 8 9 2 0 0 0 0 - 8 13 1 0 0 0 0 - 9 14 1 0 0 0 0 + 1 2 1 0 + 1 4 2 0 + 1 9 1 0 + 2 3 1 0 + 2 6 2 0 + 4 5 1 0 + 4 10 1 0 + 5 7 2 0 + 5 11 1 0 + 7 8 1 0 + 7 12 1 0 + 8 9 2 0 + 8 13 1 0 + 9 14 1 0 +M CHG 1 3 -1 M END + +> +-0.12568999826908112 0.90591001510620117 -0.83410000801086426 -0.10845000296831131 -0.16561999917030334 -0.83410000801086426 -0.16110999882221222 -0.16561999917030334 -0.10845000296831131 +0.14900000393390656 0.10098999738693237 0.097249999642372131 0.10098999738693237 0.14900000393390656 + $$$$ diff --git a/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py b/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py index fd1134c87..39dbdd130 100644 --- a/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py +++ b/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py @@ -2604,6 +2604,9 @@ def test_high_heavy_atom_mapping_ratio_warning(atom_mapping_basic_test_files, va componentA_to_componentB={0: 0, 1: 1, 2: 10, 3: 9, 8: 8}, ) + # make sure the mapping ratio is over 2 which should trigger the warning + assert mapping.get_heavy_atom_mapping_ratio() >= 2.0 + with pytest.warns( UserWarning, match="The ratio of alchemical to mapped heavy atoms: 2.8 is large, please review the mapping if the simulation is unstable.", From b8f7ecb16ac837fef66c95d039fe0596e7e32681 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 18 Aug 2026 13:18:07 +0000 Subject: [PATCH 10/21] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/openfe/tests/conftest.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/openfe/tests/conftest.py b/src/openfe/tests/conftest.py index 7449486a4..b552cfdc6 100644 --- a/src/openfe/tests/conftest.py +++ b/src/openfe/tests/conftest.py @@ -14,7 +14,7 @@ import pandas as pd import pytest from gufe import AtomMapper, LigandAtomMapping, ProteinComponent, SmallMoleculeComponent -from openff.toolkit import ForceField +from openff.toolkit import ForceField, Molecule from openff.units import unit as offunit from openmm import unit as ommunit from rdkit import Chem @@ -27,8 +27,6 @@ from openfe.protocols.openmm_utils.serialization import deserialize from openfe.tests.protocols.openmm_rfe.helpers import make_htf -from openff.toolkit import Molecule - class SlowTests: """Plugin for handling fixtures that skips slow tests From 98152db2091e7853528707664334df21f0d8c1f3 Mon Sep 17 00:00:00 2001 From: Josh Horton Date: Tue, 18 Aug 2026 14:36:48 +0100 Subject: [PATCH 11/21] speedup smoke test --- src/openfe/tests/analysis/test_plotting.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/openfe/tests/analysis/test_plotting.py b/src/openfe/tests/analysis/test_plotting.py index 926b16b06..f42c78f64 100644 --- a/src/openfe/tests/analysis/test_plotting.py +++ b/src/openfe/tests/analysis/test_plotting.py @@ -159,15 +159,21 @@ def test_mbar_overlap_plot(): assert isinstance(ax, matplotlib.axes.Axes) -@pytest.mark.parametrize("num", [i for i in range(1, 30)]) -def test_plot_2D_rmsd(num): +def test_plot_2D_rmsd(): """ Smoke test: Loop through and test plotting fictitious 2D data """ + num = 29 points = num * (num - 1) // 2 - data = [[0.5 for x in range(points)] for i in range(num)] + data = [[0.5 for _ in range(points)] for _ in range(num)] + fig = plot_2D_rmsd(data) + # check the number of axes and their titles + assert len(fig.axes) == 32 + axis_names = [ax.get_title() for ax in fig.axes if ax.get_title()] + assert axis_names == [f"State {i}" for i in range(num)] + assert fig._suptitle.get_text() == "Protein 2D RMSD" plt.close(fig) From df24ca15dc75813b55faa0661e1c5fd02d429c4c Mon Sep 17 00:00:00 2001 From: Josh Horton Date: Tue, 18 Aug 2026 14:45:06 +0100 Subject: [PATCH 12/21] add mixed charge/uncharged system testing --- .../tests/protocols/test_openmmutils.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/openfe/tests/protocols/test_openmmutils.py b/src/openfe/tests/protocols/test_openmmutils.py index bb42d5031..8a37036e7 100644 --- a/src/openfe/tests/protocols/test_openmmutils.py +++ b/src/openfe/tests/protocols/test_openmmutils.py @@ -1448,6 +1448,16 @@ def benzene_no_charge_system(self, benzene_modifications_uncharged): {"ligand": benzene_modifications_uncharged["benzene"]}, name="no charges" ) + @pytest.fixture + def mixed_charge_system(self, benzene_modifications, benzene_modifications_uncharged): + return ChemicalSystem( + { + "benzene": benzene_modifications["benzene"], + "toluene": benzene_modifications_uncharged["toluene"], + }, + name="mixed charges", + ) + def test_gaff_with_molecule_charges(self, benzene_charged_system): system_validation.validate_nondeterministic_charges( benzene_charged_system, small_molecule_forcefield="gaff-2.11" @@ -1515,3 +1525,16 @@ def test_openff_lib_charges_no_charges(self, benzene_no_charge_system, benzene_m system_validation.validate_nondeterministic_charges( benzene_no_charge_system, small_molecule_forcefield=ff.to_string() ) + + def test_openff_nagl_mixed_charges(self, mixed_charge_system): + # make sure an error is raised if not all smcs would have deterministic charges + with pytest.raises( + ProtocolValidationError, + match=re.escape( + "SmallMoleculeComponent(name=toluene) from system mixed charges would have am1bcc charges" + ), + ): + system_validation.validate_nondeterministic_charges( + # pick a force field with an am1bcc handler + mixed_charge_system, small_molecule_forcefield="openff-2.2.0.offxml" + ) \ No newline at end of file From 7dfdb3fd59cbc7c4085df1495f7ea9ac6ad255f3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 18 Aug 2026 13:46:19 +0000 Subject: [PATCH 13/21] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/openfe/tests/protocols/test_openmmutils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/openfe/tests/protocols/test_openmmutils.py b/src/openfe/tests/protocols/test_openmmutils.py index 8a37036e7..7a654bcc1 100644 --- a/src/openfe/tests/protocols/test_openmmutils.py +++ b/src/openfe/tests/protocols/test_openmmutils.py @@ -1536,5 +1536,6 @@ def test_openff_nagl_mixed_charges(self, mixed_charge_system): ): system_validation.validate_nondeterministic_charges( # pick a force field with an am1bcc handler - mixed_charge_system, small_molecule_forcefield="openff-2.2.0.offxml" - ) \ No newline at end of file + mixed_charge_system, + small_molecule_forcefield="openff-2.2.0.offxml", + ) From 0c8e7dd05522b71353e89c54645cb8366d9d0308 Mon Sep 17 00:00:00 2001 From: Josh Horton Date: Wed, 19 Aug 2026 11:07:44 +0100 Subject: [PATCH 14/21] switch to using sdfs --- charged_benzenes_with_charges.sdf | 126 ++++++++++++++++++ src/openfe/tests/conftest.py | 7 +- .../1,3,7-trimethylnaphthalene.mol2 | 63 --------- .../1,3,7-trimethylnaphthalene.sdf | 64 +++++++++ .../lomap_basic/1-butyl-4-methylbenzene.mol2 | 62 --------- .../lomap_basic/1-butyl-4-methylbenzene.sdf | 63 +++++++++ .../lomap_basic/2,6-dimethylnaphthalene.mol2 | 57 -------- .../lomap_basic/2,6-dimethylnaphthalene.sdf | 58 ++++++++ .../2-methyl-6-propylnaphthalene.mol2 | 69 ---------- .../2-methyl-6-propylnaphthalene.sdf | 70 ++++++++++ .../data/lomap_basic/2-methylnaphthalene.mol2 | 51 ------- .../data/lomap_basic/2-methylnaphthalene.sdf | 52 ++++++++ .../tests/data/lomap_basic/2-naftanol.mol2 | 47 ------- .../tests/data/lomap_basic/2-naftanol.sdf | 48 +++++++ .../data/lomap_basic/methylcyclohexane.mol2 | 50 ------- .../data/lomap_basic/methylcyclohexane.sdf | 51 +++++++ .../tests/data/lomap_basic/toluene.mol2 | 38 ------ src/openfe/tests/data/lomap_basic/toluene.sdf | 39 ++++++ .../setup/atom_mapping/test_lomap_scorers.py | 2 +- 19 files changed, 575 insertions(+), 442 deletions(-) create mode 100644 charged_benzenes_with_charges.sdf delete mode 100644 src/openfe/tests/data/lomap_basic/1,3,7-trimethylnaphthalene.mol2 create mode 100644 src/openfe/tests/data/lomap_basic/1,3,7-trimethylnaphthalene.sdf delete mode 100644 src/openfe/tests/data/lomap_basic/1-butyl-4-methylbenzene.mol2 create mode 100644 src/openfe/tests/data/lomap_basic/1-butyl-4-methylbenzene.sdf delete mode 100644 src/openfe/tests/data/lomap_basic/2,6-dimethylnaphthalene.mol2 create mode 100644 src/openfe/tests/data/lomap_basic/2,6-dimethylnaphthalene.sdf delete mode 100644 src/openfe/tests/data/lomap_basic/2-methyl-6-propylnaphthalene.mol2 create mode 100644 src/openfe/tests/data/lomap_basic/2-methyl-6-propylnaphthalene.sdf delete mode 100644 src/openfe/tests/data/lomap_basic/2-methylnaphthalene.mol2 create mode 100644 src/openfe/tests/data/lomap_basic/2-methylnaphthalene.sdf delete mode 100644 src/openfe/tests/data/lomap_basic/2-naftanol.mol2 create mode 100644 src/openfe/tests/data/lomap_basic/2-naftanol.sdf delete mode 100644 src/openfe/tests/data/lomap_basic/methylcyclohexane.mol2 create mode 100644 src/openfe/tests/data/lomap_basic/methylcyclohexane.sdf delete mode 100644 src/openfe/tests/data/lomap_basic/toluene.mol2 create mode 100644 src/openfe/tests/data/lomap_basic/toluene.sdf diff --git a/charged_benzenes_with_charges.sdf b/charged_benzenes_with_charges.sdf new file mode 100644 index 000000000..b0018a774 --- /dev/null +++ b/charged_benzenes_with_charges.sdf @@ -0,0 +1,126 @@ +benzene + RDKit 3D + + 12 12 0 0 0 0 0 0 0 0999 V2000 + 1.4045 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7022 1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.7023 1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.4045 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.7023 -1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7023 -1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5079 -0.0000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.2540 2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.2540 2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + -2.5079 -0.0000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.2540 -2.1719 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.2540 -2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 2 0 + 1 6 1 0 + 1 7 1 0 + 2 3 1 0 + 2 8 1 0 + 3 4 2 0 + 3 9 1 0 + 4 5 1 0 + 4 10 1 0 + 5 6 2 0 + 5 11 1 0 + 6 12 1 0 +M END + +> +benzene + +> +-0.13019000614682832 -0.1301299991707007 -0.1301299991707007 -0.1301299991707007 -0.1301299991707007 -0.1301299991707007 0.13014000033338866 0.13014000033338866 0.13014000033338866 +0.13014000033338866 0.13014000033338866 0.13014000033338866 + +$$$$ +aniline + RDKit 3D + + 15 15 0 0 0 0 0 0 0 0999 V2000 + 1.4045 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7022 1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.7023 1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.4045 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.7023 -1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7023 -1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.2106 0.8251 0.4756 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.2117 -0.8238 0.4772 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.2117 -0.0000 -0.9520 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8745 -0.0000 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 1.2540 2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.2540 2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + -2.5079 -0.0000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.2540 -2.1719 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.2540 -2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 2 0 + 1 6 1 0 + 1 10 1 0 + 2 3 1 0 + 2 11 1 0 + 3 4 2 0 + 3 12 1 0 + 4 5 1 0 + 4 13 1 0 + 5 6 2 0 + 5 14 1 0 + 6 15 1 0 + 7 10 1 0 + 8 10 1 0 + 9 10 1 0 +M CHG 1 10 1 +M END + +> +aniline + +> +-0.082359999914964038 -0.09780000199874242 -0.098719999690850574 -0.061870001753171283 -0.098719999690850574 -0.09780000199874242 0.46404001067082085 0.46404001067082085 +0.46404001067082085 -0.69748002340396242 0.15680000136295955 0.17632999966541926 0.17636999438206355 0.17632999966541926 0.15680000136295955 + +$$$$ +benzoic_acid + RDKit 3D + + 14 14 0 0 0 0 0 0 0 0999 V2000 + 1.4045 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.7445 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.2607 -1.0833 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7022 1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.7023 1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.4265 1.0159 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + -1.4045 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.7023 -1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7023 -1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.2540 2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.2540 2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + -2.5079 -0.0000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.2540 -2.1719 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.2540 -2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 + 1 4 2 0 + 1 9 1 0 + 2 3 1 0 + 2 6 2 0 + 4 5 1 0 + 4 10 1 0 + 5 7 2 0 + 5 11 1 0 + 7 8 1 0 + 7 12 1 0 + 8 9 2 0 + 8 13 1 0 + 9 14 1 0 +M CHG 1 3 -1 +M END + +> +benzoic_acid + +> +-0.12568999826908112 0.90591001510620117 -0.83410000801086426 -0.10845000296831131 -0.16561999917030334 -0.83410000801086426 -0.16110999882221222 -0.16561999917030334 -0.10845000296831131 +0.14900000393390656 0.10098999738693237 0.097249999642372131 0.10098999738693237 0.14900000393390656 + +$$$$ diff --git a/src/openfe/tests/conftest.py b/src/openfe/tests/conftest.py index b552cfdc6..0d1f2b2e4 100644 --- a/src/openfe/tests/conftest.py +++ b/src/openfe/tests/conftest.py @@ -162,7 +162,7 @@ def lomap_basic_test_files_dir(tmp_path_factory): lomap_basic = "openfe.tests.data.lomap_basic" for f in resources.contents(lomap_basic): - if not f.endswith("mol2"): + if not f.endswith("sdf"): continue stuff = resources.read_binary(lomap_basic, f) @@ -188,10 +188,9 @@ def atom_mapping_basic_test_files(): "toluene", ]: with resources.as_file(resources.files("openfe.tests.data.lomap_basic")) as d: - fn = str(d / (f + ".mol2")) + fn = str(d / (f + ".sdf")) # go via openff to make sure the partial charges are pulled - mol = Molecule.from_file(fn) - files[f] = SmallMoleculeComponent.from_openff(mol, name=f) + files[f] = SmallMoleculeComponent.from_sdf_file(fn) return files diff --git a/src/openfe/tests/data/lomap_basic/1,3,7-trimethylnaphthalene.mol2 b/src/openfe/tests/data/lomap_basic/1,3,7-trimethylnaphthalene.mol2 deleted file mode 100644 index 4075dbcd6..000000000 --- a/src/openfe/tests/data/lomap_basic/1,3,7-trimethylnaphthalene.mol2 +++ /dev/null @@ -1,63 +0,0 @@ -@MOLECULE -***** - 27 28 0 0 0 -SMALL -GASTEIGER - -@ATOM - 1 C 0.9251 0.1494 -0.0025 C.3 1 LIG1 -0.0397 - 2 C 2.4242 0.1552 0.0465 C.ar 1 LIG1 -0.0498 - 3 C 3.1297 -1.0574 0.0810 C.ar 1 LIG1 -0.0509 - 4 C 4.5422 -1.1042 0.1427 C.ar 1 LIG1 -0.0147 - 5 C 5.2861 -2.3145 0.1727 C.ar 1 LIG1 -0.0425 - 6 C 6.6903 -2.2735 0.2331 C.ar 1 LIG1 -0.0553 - 7 C 7.3760 -1.0578 0.2620 C.ar 1 LIG1 -0.0496 - 8 C 6.6436 0.1328 0.2358 C.ar 1 LIG1 -0.0511 - 9 C 5.2413 0.1261 0.1765 C.ar 1 LIG1 -0.0174 - 10 C 4.5298 1.3362 0.1522 C.ar 1 LIG1 -0.0538 - 11 C 3.1361 1.3519 0.0914 C.ar 1 LIG1 -0.0582 - 12 C 8.8737 -1.0147 0.3281 C.3 1 LIG1 -0.0397 - 13 C 4.6094 -3.6603 0.1443 C.3 1 LIG1 -0.0391 - 14 H 0.5282 1.1476 -0.2149 H 1 LIG1 0.0278 - 15 H 0.5194 -0.1845 0.9575 H 1 LIG1 0.0278 - 16 H 0.5728 -0.5198 -0.7944 H 1 LIG1 0.0278 - 17 H 2.5577 -1.9826 0.0575 H 1 LIG1 0.0626 - 18 H 7.2540 -3.2043 0.2569 H 1 LIG1 0.0623 - 19 H 7.1781 1.0810 0.2619 H 1 LIG1 0.0626 - 20 H 5.0603 2.2854 0.1820 H 1 LIG1 0.0624 - 21 H 2.6157 2.3065 0.0777 H 1 LIG1 0.0620 - 22 H 9.3069 -2.0200 0.3015 H 1 LIG1 0.0278 - 23 H 9.1960 -0.5332 1.2567 H 1 LIG1 0.0278 - 24 H 9.2731 -0.4567 -0.5249 H 1 LIG1 0.0278 - 25 H 5.3371 -4.4789 0.1591 H 1 LIG1 0.0278 - 26 H 4.0157 -3.7691 -0.7690 H 1 LIG1 0.0278 - 27 H 3.9645 -3.7798 1.0209 H 1 LIG1 0.0278 -@BOND - 1 6 7 ar - 2 5 6 ar - 3 7 8 ar - 4 8 9 ar - 5 9 10 ar - 6 4 9 ar - 7 10 11 ar - 8 2 11 ar - 9 2 3 ar - 10 3 4 ar - 11 4 5 ar - 12 1 2 1 - 13 5 13 1 - 14 7 12 1 - 15 1 14 1 - 16 1 15 1 - 17 1 16 1 - 18 3 17 1 - 19 6 18 1 - 20 8 19 1 - 21 10 20 1 - 22 11 21 1 - 23 12 22 1 - 24 12 23 1 - 25 12 24 1 - 26 13 25 1 - 27 13 26 1 - 28 13 27 1 diff --git a/src/openfe/tests/data/lomap_basic/1,3,7-trimethylnaphthalene.sdf b/src/openfe/tests/data/lomap_basic/1,3,7-trimethylnaphthalene.sdf new file mode 100644 index 000000000..5d9849013 --- /dev/null +++ b/src/openfe/tests/data/lomap_basic/1,3,7-trimethylnaphthalene.sdf @@ -0,0 +1,64 @@ +1,3,7-trimethylnaphthalene + -OEChem-08192610263D + + 27 28 0 0 0 0 0 0 0999 V2000 + 0.9251 0.1494 -0.0025 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4242 0.1552 0.0465 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1297 -1.0574 0.0810 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5422 -1.1042 0.1427 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2861 -2.3145 0.1727 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6903 -2.2735 0.2331 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3760 -1.0578 0.2620 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6436 0.1328 0.2358 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2413 0.1261 0.1765 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5298 1.3362 0.1522 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1361 1.3519 0.0914 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.8737 -1.0147 0.3281 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6094 -3.6603 0.1443 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.5282 1.1476 -0.2149 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.5194 -0.1845 0.9575 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.5728 -0.5198 -0.7944 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5577 -1.9826 0.0575 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2540 -3.2043 0.2569 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1781 1.0810 0.2619 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0603 2.2854 0.1820 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6157 2.3065 0.0777 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.3069 -2.0200 0.3015 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1960 -0.5332 1.2567 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2731 -0.4567 -0.5249 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.3371 -4.4789 0.1591 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0157 -3.7691 -0.7690 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9645 -3.7798 1.0209 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6 7 2 0 0 0 0 + 5 6 1 0 0 0 0 + 7 8 1 0 0 0 0 + 8 9 2 0 0 0 0 + 9 10 1 0 0 0 0 + 4 9 1 0 0 0 0 + 10 11 2 0 0 0 0 + 2 11 1 0 0 0 0 + 2 3 2 0 0 0 0 + 3 4 1 0 0 0 0 + 4 5 2 0 0 0 0 + 1 2 1 0 0 0 0 + 5 13 1 0 0 0 0 + 7 12 1 0 0 0 0 + 1 14 1 0 0 0 0 + 1 15 1 0 0 0 0 + 1 16 1 0 0 0 0 + 3 17 1 0 0 0 0 + 6 18 1 0 0 0 0 + 8 19 1 0 0 0 0 + 10 20 1 0 0 0 0 + 11 21 1 0 0 0 0 + 12 22 1 0 0 0 0 + 12 23 1 0 0 0 0 + 12 24 1 0 0 0 0 + 13 25 1 0 0 0 0 + 13 26 1 0 0 0 0 + 13 27 1 0 0 0 0 +M END +> +-0.039700 -0.049800 -0.050900 -0.014700 -0.042500 -0.055300 -0.049600 -0.051100 -0.017400 -0.053800 -0.058200 -0.039700 -0.039100 0.027800 0.027800 0.027800 0.062600 0.062300 0.062600 0.062400 0.062000 0.027800 0.027800 0.027800 0.027800 0.027800 0.027800 + +$$$$ diff --git a/src/openfe/tests/data/lomap_basic/1-butyl-4-methylbenzene.mol2 b/src/openfe/tests/data/lomap_basic/1-butyl-4-methylbenzene.mol2 deleted file mode 100644 index 3c4d3de90..000000000 --- a/src/openfe/tests/data/lomap_basic/1-butyl-4-methylbenzene.mol2 +++ /dev/null @@ -1,62 +0,0 @@ -@MOLECULE -***** - 27 27 0 0 0 -SMALL -GASTEIGER - -@ATOM - 1 C 1.9899 -1.5496 1.4095 C.3 1 LIG1 -0.0653 - 2 C 3.0612 -0.6401 0.8303 C.3 1 LIG1 -0.0556 - 3 C 3.5100 0.4139 1.8419 C.3 1 LIG1 -0.0493 - 4 C 4.5859 1.3256 1.2470 C.3 1 LIG1 -0.0277 - 5 C 5.0777 2.3433 2.2464 C.ar 1 LIG1 -0.0473 - 6 C 4.4793 3.6056 2.3316 C.ar 1 LIG1 -0.0583 - 7 C 4.9350 4.5439 3.2608 C.ar 1 LIG1 -0.0586 - 8 C 5.9858 4.2317 4.1299 C.ar 1 LIG1 -0.0504 - 9 C 6.5860 2.9711 4.0431 C.ar 1 LIG1 -0.0586 - 10 C 6.1309 2.0320 3.1146 C.ar 1 LIG1 -0.0583 - 11 C 6.4975 5.2565 5.0979 C.3 1 LIG1 -0.0397 - 12 H 1.6931 -2.3034 0.6726 H 1 LIG1 0.0230 - 13 H 1.0980 -0.9785 1.6885 H 1 LIG1 0.0230 - 14 H 2.3560 -2.0720 2.3000 H 1 LIG1 0.0230 - 15 H 2.6700 -0.1517 -0.0688 H 1 LIG1 0.0263 - 16 H 3.9190 -1.2484 0.5229 H 1 LIG1 0.0263 - 17 H 3.8957 -0.0824 2.7418 H 1 LIG1 0.0268 - 18 H 2.6473 1.0144 2.1583 H 1 LIG1 0.0268 - 19 H 4.1939 1.8426 0.3620 H 1 LIG1 0.0313 - 20 H 5.4358 0.7279 0.8940 H 1 LIG1 0.0313 - 21 H 3.6566 3.8691 1.6710 H 1 LIG1 0.0620 - 22 H 4.4634 5.5233 3.3010 H 1 LIG1 0.0620 - 23 H 7.4138 2.7097 4.6988 H 1 LIG1 0.0620 - 24 H 6.6080 1.0557 3.0700 H 1 LIG1 0.0620 - 25 H 7.0175 4.7827 5.9374 H 1 LIG1 0.0278 - 26 H 5.6710 5.8412 5.5155 H 1 LIG1 0.0278 - 27 H 7.1937 5.9336 4.5926 H 1 LIG1 0.0278 -@BOND - 1 5 6 ar - 2 6 7 ar - 3 5 10 ar - 4 9 10 ar - 5 8 9 ar - 6 7 8 ar - 7 1 2 1 - 8 2 3 1 - 9 3 4 1 - 10 4 5 1 - 11 8 11 1 - 12 1 12 1 - 13 1 13 1 - 14 1 14 1 - 15 2 15 1 - 16 2 16 1 - 17 3 17 1 - 18 3 18 1 - 19 4 19 1 - 20 4 20 1 - 21 6 21 1 - 22 7 22 1 - 23 9 23 1 - 24 10 24 1 - 25 11 25 1 - 26 11 26 1 - 27 11 27 1 diff --git a/src/openfe/tests/data/lomap_basic/1-butyl-4-methylbenzene.sdf b/src/openfe/tests/data/lomap_basic/1-butyl-4-methylbenzene.sdf new file mode 100644 index 000000000..855a71538 --- /dev/null +++ b/src/openfe/tests/data/lomap_basic/1-butyl-4-methylbenzene.sdf @@ -0,0 +1,63 @@ +1-butyl-4-methylbenzene + -OEChem-08192610283D + + 27 27 0 0 0 0 0 0 0999 V2000 + 1.9899 -1.5496 1.4095 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.0612 -0.6401 0.8303 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5100 0.4139 1.8419 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5859 1.3256 1.2470 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0777 2.3433 2.2464 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.4793 3.6056 2.3316 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9350 4.5439 3.2608 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9858 4.2317 4.1299 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5860 2.9711 4.0431 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1309 2.0320 3.1146 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4975 5.2565 5.0979 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.6931 -2.3034 0.6726 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.0980 -0.9785 1.6885 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3560 -2.0720 2.3000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6700 -0.1517 -0.0688 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9190 -1.2484 0.5229 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.8957 -0.0824 2.7418 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6473 1.0144 2.1583 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.1939 1.8426 0.3620 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4358 0.7279 0.8940 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.6566 3.8691 1.6710 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.4634 5.5233 3.3010 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4138 2.7097 4.6988 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6080 1.0557 3.0700 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0175 4.7827 5.9374 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6710 5.8412 5.5155 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1937 5.9336 4.5926 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5 6 2 0 0 0 0 + 6 7 1 0 0 0 0 + 5 10 1 0 0 0 0 + 9 10 2 0 0 0 0 + 8 9 1 0 0 0 0 + 7 8 2 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 1 0 0 0 0 + 4 5 1 0 0 0 0 + 8 11 1 0 0 0 0 + 1 12 1 0 0 0 0 + 1 13 1 0 0 0 0 + 1 14 1 0 0 0 0 + 2 15 1 0 0 0 0 + 2 16 1 0 0 0 0 + 3 17 1 0 0 0 0 + 3 18 1 0 0 0 0 + 4 19 1 0 0 0 0 + 4 20 1 0 0 0 0 + 6 21 1 0 0 0 0 + 7 22 1 0 0 0 0 + 9 23 1 0 0 0 0 + 10 24 1 0 0 0 0 + 11 25 1 0 0 0 0 + 11 26 1 0 0 0 0 + 11 27 1 0 0 0 0 +M END +> +-0.065300 -0.055600 -0.049300 -0.027700 -0.047300 -0.058300 -0.058600 -0.050400 -0.058600 -0.058300 -0.039700 0.023000 0.023000 0.023000 0.026300 0.026300 0.026800 0.026800 0.031300 0.031300 0.062000 0.062000 0.062000 0.062000 0.027800 0.027800 0.027800 + +$$$$ diff --git a/src/openfe/tests/data/lomap_basic/2,6-dimethylnaphthalene.mol2 b/src/openfe/tests/data/lomap_basic/2,6-dimethylnaphthalene.mol2 deleted file mode 100644 index 1ead00447..000000000 --- a/src/openfe/tests/data/lomap_basic/2,6-dimethylnaphthalene.mol2 +++ /dev/null @@ -1,57 +0,0 @@ -@MOLECULE -***** - 24 25 0 0 0 -SMALL -GASTEIGER - -@ATOM - 1 C 0.8817 -0.0759 0.0737 C.3 1 LIG1 -0.0397 - 2 C 2.3811 -0.0540 0.0354 C.ar 1 LIG1 -0.0499 - 3 C 3.0834 1.1532 0.0347 C.ar 1 LIG1 -0.0582 - 4 C 4.4799 1.1582 -0.0011 C.ar 1 LIG1 -0.0538 - 5 C 5.2032 -0.0442 -0.0356 C.ar 1 LIG1 -0.0176 - 6 C 6.6087 -0.0550 -0.0772 C.ar 1 LIG1 -0.0511 - 7 C 7.3242 -1.2594 -0.1176 C.ar 1 LIG1 -0.0499 - 8 C 8.8235 -1.2393 -0.1486 C.3 1 LIG1 -0.0397 - 9 C 6.6224 -2.4667 -0.0984 C.ar 1 LIG1 -0.0582 - 10 C 5.2258 -2.4717 -0.0573 C.ar 1 LIG1 -0.0538 - 11 C 4.5024 -1.2692 -0.0283 C.ar 1 LIG1 -0.0176 - 12 C 3.0971 -1.2586 0.0081 C.ar 1 LIG1 -0.0511 - 13 H 0.4638 0.9355 0.0362 H 1 LIG1 0.0278 - 14 H 0.5354 -0.5508 0.9973 H 1 LIG1 0.0278 - 15 H 0.4903 -0.6304 -0.7856 H 1 LIG1 0.0278 - 16 H 2.5515 2.1015 0.0601 H 1 LIG1 0.0620 - 17 H 5.0016 2.1125 -0.0033 H 1 LIG1 0.0624 - 18 H 7.1539 0.8869 -0.0807 H 1 LIG1 0.0626 - 19 H 9.2330 -2.2332 -0.3578 H 1 LIG1 0.0278 - 20 H 9.2150 -0.9034 0.8167 H 1 LIG1 0.0278 - 21 H 9.1785 -0.5651 -0.9354 H 1 LIG1 0.0278 - 22 H 7.1546 -3.4149 -0.1177 H 1 LIG1 0.0620 - 23 H 4.7045 -3.4261 -0.0479 H 1 LIG1 0.0624 - 24 H 2.5515 -2.2003 0.0134 H 1 LIG1 0.0626 -@BOND - 1 2 3 ar - 2 3 4 ar - 3 2 12 ar - 4 11 12 ar - 5 10 11 ar - 6 5 11 ar - 7 9 10 ar - 8 7 9 ar - 9 6 7 ar - 10 5 6 ar - 11 4 5 ar - 12 1 2 1 - 13 7 8 1 - 14 1 13 1 - 15 1 14 1 - 16 1 15 1 - 17 3 16 1 - 18 4 17 1 - 19 6 18 1 - 20 8 19 1 - 21 8 20 1 - 22 8 21 1 - 23 9 22 1 - 24 10 23 1 - 25 12 24 1 diff --git a/src/openfe/tests/data/lomap_basic/2,6-dimethylnaphthalene.sdf b/src/openfe/tests/data/lomap_basic/2,6-dimethylnaphthalene.sdf new file mode 100644 index 000000000..839dba911 --- /dev/null +++ b/src/openfe/tests/data/lomap_basic/2,6-dimethylnaphthalene.sdf @@ -0,0 +1,58 @@ +2,6-dimethylnaphthalene + -OEChem-08192610283D + + 24 25 0 0 0 0 0 0 0999 V2000 + 0.8817 -0.0759 0.0737 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3811 -0.0540 0.0354 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.0834 1.1532 0.0347 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.4799 1.1582 -0.0011 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2032 -0.0442 -0.0356 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6087 -0.0550 -0.0772 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3242 -1.2594 -0.1176 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.8235 -1.2393 -0.1486 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6224 -2.4667 -0.0984 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2258 -2.4717 -0.0573 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5024 -1.2692 -0.0283 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.0971 -1.2586 0.0081 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.4638 0.9355 0.0362 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.5354 -0.5508 0.9973 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.4903 -0.6304 -0.7856 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5515 2.1015 0.0601 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0016 2.1125 -0.0033 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1539 0.8869 -0.0807 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2330 -2.2332 -0.3578 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2150 -0.9034 0.8167 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1785 -0.5651 -0.9354 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1546 -3.4149 -0.1177 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.7045 -3.4261 -0.0479 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5515 -2.2003 0.0134 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2 3 2 0 0 0 0 + 3 4 1 0 0 0 0 + 2 12 1 0 0 0 0 + 11 12 2 0 0 0 0 + 10 11 1 0 0 0 0 + 5 11 1 0 0 0 0 + 9 10 2 0 0 0 0 + 7 9 1 0 0 0 0 + 6 7 2 0 0 0 0 + 5 6 1 0 0 0 0 + 4 5 2 0 0 0 0 + 1 2 1 0 0 0 0 + 7 8 1 0 0 0 0 + 1 13 1 0 0 0 0 + 1 14 1 0 0 0 0 + 1 15 1 0 0 0 0 + 3 16 1 0 0 0 0 + 4 17 1 0 0 0 0 + 6 18 1 0 0 0 0 + 8 19 1 0 0 0 0 + 8 20 1 0 0 0 0 + 8 21 1 0 0 0 0 + 9 22 1 0 0 0 0 + 10 23 1 0 0 0 0 + 12 24 1 0 0 0 0 +M END +> +-0.039700 -0.049900 -0.058200 -0.053800 -0.017600 -0.051100 -0.049900 -0.039700 -0.058200 -0.053800 -0.017600 -0.051100 0.027800 0.027800 0.027800 0.062000 0.062400 0.062600 0.027800 0.027800 0.027800 0.062000 0.062400 0.062600 + +$$$$ diff --git a/src/openfe/tests/data/lomap_basic/2-methyl-6-propylnaphthalene.mol2 b/src/openfe/tests/data/lomap_basic/2-methyl-6-propylnaphthalene.mol2 deleted file mode 100644 index e5595c491..000000000 --- a/src/openfe/tests/data/lomap_basic/2-methyl-6-propylnaphthalene.mol2 +++ /dev/null @@ -1,69 +0,0 @@ -@MOLECULE -***** - 30 31 0 0 0 -SMALL -GASTEIGER - -@ATOM - 1 C 0.7969 1.3147 -1.5722 C.3 1 LIG1 -0.0650 - 2 C 2.3162 1.3551 -1.5843 C.3 1 LIG1 -0.0519 - 3 C 2.8874 1.3784 -0.1657 C.3 1 LIG1 -0.0279 - 4 C 4.3949 1.4239 -0.1763 C.ar 1 LIG1 -0.0468 - 5 C 5.1419 0.2376 -0.2084 C.ar 1 LIG1 -0.0508 - 6 C 6.5466 0.2630 -0.2482 C.ar 1 LIG1 -0.0176 - 7 C 7.2164 1.5056 -0.2598 C.ar 1 LIG1 -0.0176 - 8 C 6.4634 2.6893 -0.2285 C.ar 1 LIG1 -0.0538 - 9 C 5.0678 2.6494 -0.1901 C.ar 1 LIG1 -0.0580 - 10 C 8.6208 1.5306 -0.3057 C.ar 1 LIG1 -0.0511 - 11 C 9.3673 0.3447 -0.3470 C.ar 1 LIG1 -0.0499 - 12 C 8.6960 -0.8804 -0.3232 C.ar 1 LIG1 -0.0582 - 13 C 7.3000 -0.9207 -0.2780 C.ar 1 LIG1 -0.0538 - 14 C 10.8656 0.4019 -0.3796 C.3 1 LIG1 -0.0397 - 15 H 0.4106 1.2994 -2.5961 H 1 LIG1 0.0230 - 16 H 0.3854 2.1942 -1.0670 H 1 LIG1 0.0230 - 17 H 0.4325 0.4199 -1.0579 H 1 LIG1 0.0230 - 18 H 2.6477 2.2415 -2.1391 H 1 LIG1 0.0266 - 19 H 2.6945 0.4813 -2.1292 H 1 LIG1 0.0266 - 20 H 2.5534 0.4943 0.3922 H 1 LIG1 0.0313 - 21 H 2.5001 2.2454 0.3843 H 1 LIG1 0.0313 - 22 H 4.6215 -0.7184 -0.2065 H 1 LIG1 0.0626 - 23 H 6.9606 3.6568 -0.2384 H 1 LIG1 0.0624 - 24 H 4.5102 3.5829 -0.1747 H 1 LIG1 0.0620 - 25 H 9.1424 2.4858 -0.3125 H 1 LIG1 0.0626 - 26 H 9.2523 -1.8147 -0.3422 H 1 LIG1 0.0620 - 27 H 6.8033 -1.8881 -0.2657 H 1 LIG1 0.0624 - 28 H 11.2977 -0.5759 -0.6162 H 1 LIG1 0.0278 - 29 H 11.2498 0.7216 0.5938 H 1 LIG1 0.0278 - 30 H 11.2028 1.1048 -1.1483 H 1 LIG1 0.0278 -@BOND - 1 4 9 ar - 2 8 9 ar - 3 4 5 ar - 4 5 6 ar - 5 6 13 ar - 6 6 7 ar - 7 12 13 ar - 8 11 12 ar - 9 10 11 ar - 10 7 10 ar - 11 7 8 ar - 12 1 2 1 - 13 2 3 1 - 14 3 4 1 - 15 11 14 1 - 16 1 15 1 - 17 1 16 1 - 18 1 17 1 - 19 2 18 1 - 20 2 19 1 - 21 3 20 1 - 22 3 21 1 - 23 5 22 1 - 24 8 23 1 - 25 9 24 1 - 26 10 25 1 - 27 12 26 1 - 28 13 27 1 - 29 14 28 1 - 30 14 29 1 - 31 14 30 1 diff --git a/src/openfe/tests/data/lomap_basic/2-methyl-6-propylnaphthalene.sdf b/src/openfe/tests/data/lomap_basic/2-methyl-6-propylnaphthalene.sdf new file mode 100644 index 000000000..175aa303b --- /dev/null +++ b/src/openfe/tests/data/lomap_basic/2-methyl-6-propylnaphthalene.sdf @@ -0,0 +1,70 @@ +2-methyl-6-propylnaphthalene + -OEChem-08192610293D + + 30 31 0 0 0 0 0 0 0999 V2000 + 0.7969 1.3147 -1.5722 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3162 1.3551 -1.5843 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8874 1.3784 -0.1657 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.3949 1.4239 -0.1763 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1419 0.2376 -0.2084 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5466 0.2630 -0.2482 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2164 1.5056 -0.2598 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4634 2.6893 -0.2285 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0678 2.6494 -0.1901 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.6208 1.5306 -0.3057 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.3673 0.3447 -0.3470 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.6960 -0.8804 -0.3232 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3000 -0.9207 -0.2780 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.8656 0.4019 -0.3796 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.4106 1.2994 -2.5961 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.3854 2.1942 -1.0670 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.4325 0.4199 -1.0579 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6477 2.2415 -2.1391 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6945 0.4813 -2.1292 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5534 0.4943 0.3922 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5001 2.2454 0.3843 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6215 -0.7184 -0.2065 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9606 3.6568 -0.2384 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5102 3.5829 -0.1747 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1424 2.4858 -0.3125 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2523 -1.8147 -0.3422 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8033 -1.8881 -0.2657 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.2977 -0.5759 -0.6162 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.2498 0.7216 0.5938 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.2028 1.1048 -1.1483 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4 9 2 0 0 0 0 + 8 9 1 0 0 0 0 + 4 5 1 0 0 0 0 + 5 6 2 0 0 0 0 + 6 13 1 0 0 0 0 + 6 7 1 0 0 0 0 + 12 13 2 0 0 0 0 + 11 12 1 0 0 0 0 + 10 11 2 0 0 0 0 + 7 10 1 0 0 0 0 + 7 8 2 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 1 0 0 0 0 + 11 14 1 0 0 0 0 + 1 15 1 0 0 0 0 + 1 16 1 0 0 0 0 + 1 17 1 0 0 0 0 + 2 18 1 0 0 0 0 + 2 19 1 0 0 0 0 + 3 20 1 0 0 0 0 + 3 21 1 0 0 0 0 + 5 22 1 0 0 0 0 + 8 23 1 0 0 0 0 + 9 24 1 0 0 0 0 + 10 25 1 0 0 0 0 + 12 26 1 0 0 0 0 + 13 27 1 0 0 0 0 + 14 28 1 0 0 0 0 + 14 29 1 0 0 0 0 + 14 30 1 0 0 0 0 +M END +> +-0.065000 -0.051900 -0.027900 -0.046800 -0.050800 -0.017600 -0.017600 -0.053800 -0.058000 -0.051100 -0.049900 -0.058200 -0.053800 -0.039700 0.023000 0.023000 0.023000 0.026600 0.026600 0.031300 0.031300 0.062600 0.062400 0.062000 0.062600 0.062000 0.062400 0.027800 0.027800 0.027800 + +$$$$ diff --git a/src/openfe/tests/data/lomap_basic/2-methylnaphthalene.mol2 b/src/openfe/tests/data/lomap_basic/2-methylnaphthalene.mol2 deleted file mode 100644 index 4f4a6ea55..000000000 --- a/src/openfe/tests/data/lomap_basic/2-methylnaphthalene.mol2 +++ /dev/null @@ -1,51 +0,0 @@ -@MOLECULE -***** - 21 22 0 0 0 -SMALL -GASTEIGER - -@ATOM - 1 C 0.9750 -0.0348 0.0833 C.3 1 LIG1 -0.0397 - 2 C 2.4749 -0.0532 0.0754 C.ar 1 LIG1 -0.0499 - 3 C 3.2098 1.1400 0.0597 C.ar 1 LIG1 -0.0511 - 4 C 4.6141 1.1264 -0.0127 C.ar 1 LIG1 -0.0176 - 5 C 5.3554 2.3190 -0.0614 C.ar 1 LIG1 -0.0540 - 6 C 6.7465 2.2894 -0.1768 C.ar 1 LIG1 -0.0612 - 7 C 7.4142 1.0702 -0.2321 C.ar 1 LIG1 -0.0612 - 8 C 6.6939 -0.1236 -0.1628 C.ar 1 LIG1 -0.0540 - 9 C 5.2934 -0.1107 -0.0560 C.ar 1 LIG1 -0.0179 - 10 C 4.5528 -1.3018 -0.0057 C.ar 1 LIG1 -0.0538 - 11 C 3.1574 -1.2723 0.0551 C.ar 1 LIG1 -0.0582 - 12 H 0.5850 0.9882 0.1017 H 1 LIG1 0.0278 - 13 H 0.5915 -0.5586 0.9643 H 1 LIG1 0.0278 - 14 H 0.5924 -0.5209 -0.8204 H 1 LIG1 0.0278 - 15 H 2.6835 2.0917 0.0844 H 1 LIG1 0.0626 - 16 H 4.8512 3.2813 -0.0218 H 1 LIG1 0.0624 - 17 H 7.3059 3.2198 -0.2301 H 1 LIG1 0.0618 - 18 H 8.4962 1.0459 -0.3322 H 1 LIG1 0.0618 - 19 H 7.2331 -1.0669 -0.2063 H 1 LIG1 0.0624 - 20 H 5.0575 -2.2644 -0.0289 H 1 LIG1 0.0624 - 21 H 2.6062 -2.2099 0.0730 H 1 LIG1 0.0620 -@BOND - 1 1 2 1 - 2 2 3 ar - 3 3 4 ar - 4 4 5 ar - 5 5 6 ar - 6 6 7 ar - 7 7 8 ar - 8 8 9 ar - 9 4 9 ar - 10 9 10 ar - 11 10 11 ar - 12 2 11 ar - 13 1 12 1 - 14 1 13 1 - 15 1 14 1 - 16 3 15 1 - 17 5 16 1 - 18 6 17 1 - 19 7 18 1 - 20 8 19 1 - 21 10 20 1 - 22 11 21 1 diff --git a/src/openfe/tests/data/lomap_basic/2-methylnaphthalene.sdf b/src/openfe/tests/data/lomap_basic/2-methylnaphthalene.sdf new file mode 100644 index 000000000..8d754318e --- /dev/null +++ b/src/openfe/tests/data/lomap_basic/2-methylnaphthalene.sdf @@ -0,0 +1,52 @@ +2-methylnaphthalene + -OEChem-08192610323D + + 21 22 0 0 0 0 0 0 0999 V2000 + 0.9750 -0.0348 0.0833 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4749 -0.0532 0.0754 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.2098 1.1400 0.0597 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6141 1.1264 -0.0127 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.3554 2.3190 -0.0614 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7465 2.2894 -0.1768 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4142 1.0702 -0.2321 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6939 -0.1236 -0.1628 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2934 -0.1107 -0.0560 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5528 -1.3018 -0.0057 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1574 -1.2723 0.0551 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.5850 0.9882 0.1017 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.5915 -0.5586 0.9643 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.5924 -0.5209 -0.8204 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6835 2.0917 0.0844 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8512 3.2813 -0.0218 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3059 3.2198 -0.2301 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4962 1.0459 -0.3322 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2331 -1.0669 -0.2063 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0575 -2.2644 -0.0289 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6062 -2.2099 0.0730 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 2 0 0 0 0 + 3 4 1 0 0 0 0 + 4 5 2 0 0 0 0 + 5 6 1 0 0 0 0 + 6 7 2 0 0 0 0 + 7 8 1 0 0 0 0 + 8 9 2 0 0 0 0 + 4 9 1 0 0 0 0 + 9 10 1 0 0 0 0 + 10 11 2 0 0 0 0 + 2 11 1 0 0 0 0 + 1 12 1 0 0 0 0 + 1 13 1 0 0 0 0 + 1 14 1 0 0 0 0 + 3 15 1 0 0 0 0 + 5 16 1 0 0 0 0 + 6 17 1 0 0 0 0 + 7 18 1 0 0 0 0 + 8 19 1 0 0 0 0 + 10 20 1 0 0 0 0 + 11 21 1 0 0 0 0 +M END +> +-0.039700 -0.049900 -0.051100 -0.017600 -0.054000 -0.061200 -0.061200 -0.054000 -0.017900 -0.053800 -0.058200 0.027800 0.027800 0.027800 0.062600 0.062400 0.061800 0.061800 0.062400 0.062400 0.062000 + +$$$$ diff --git a/src/openfe/tests/data/lomap_basic/2-naftanol.mol2 b/src/openfe/tests/data/lomap_basic/2-naftanol.mol2 deleted file mode 100644 index 79bfc04e2..000000000 --- a/src/openfe/tests/data/lomap_basic/2-naftanol.mol2 +++ /dev/null @@ -1,47 +0,0 @@ -@MOLECULE -***** - 19 20 0 0 0 -SMALL -GASTEIGER - -@ATOM - 1 O 9.9477 -4.1875 1.4569 O.3 1 LIG1 -0.5067 - 2 C 9.0676 -3.1850 1.1716 C.ar 1 LIG1 0.1175 - 3 C 7.7820 -3.1808 1.6974 C.ar 1 LIG1 -0.0196 - 4 C 6.9153 -2.1329 1.3798 C.ar 1 LIG1 -0.0506 - 5 C 7.3294 -1.0867 0.5375 C.ar 1 LIG1 -0.0177 - 6 C 6.4703 -0.0252 0.2106 C.ar 1 LIG1 -0.0540 - 7 C 6.8977 1.0066 -0.6271 C.ar 1 LIG1 -0.0612 - 8 C 8.1878 0.9906 -1.1489 C.ar 1 LIG1 -0.0612 - 9 C 9.0548 -0.0574 -0.8344 C.ar 1 LIG1 -0.0539 - 10 C 8.6411 -1.1023 0.0071 C.ar 1 LIG1 -0.0144 - 11 C 9.5008 -2.1625 0.3346 C.ar 1 LIG1 -0.0125 - 12 H 9.5144 -4.8197 2.0534 H 1 LIG1 0.2921 - 13 H 7.4363 -3.9757 2.3505 H 1 LIG1 0.0654 - 14 H 5.9102 -2.1360 1.7951 H 1 LIG1 0.0625 - 15 H 5.4583 0.0059 0.6080 H 1 LIG1 0.0624 - 16 H 6.2223 1.8225 -0.8713 H 1 LIG1 0.0618 - 17 H 8.5216 1.7931 -1.8011 H 1 LIG1 0.0618 - 18 H 10.0593 -0.0532 -1.2512 H 1 LIG1 0.0624 - 19 H 10.5134 -2.1967 -0.0603 H 1 LIG1 0.0660 -@BOND - 1 2 3 ar - 2 2 11 ar - 3 3 4 ar - 4 4 5 ar - 5 5 6 ar - 6 5 10 ar - 7 6 7 ar - 8 7 8 ar - 9 8 9 ar - 10 9 10 ar - 11 10 11 ar - 12 1 2 1 - 13 1 12 1 - 14 3 13 1 - 15 4 14 1 - 16 6 15 1 - 17 7 16 1 - 18 8 17 1 - 19 9 18 1 - 20 11 19 1 diff --git a/src/openfe/tests/data/lomap_basic/2-naftanol.sdf b/src/openfe/tests/data/lomap_basic/2-naftanol.sdf new file mode 100644 index 000000000..e51db2e63 --- /dev/null +++ b/src/openfe/tests/data/lomap_basic/2-naftanol.sdf @@ -0,0 +1,48 @@ +2-naftanol + -OEChem-08192610333D + + 19 20 0 0 0 0 0 0 0999 V2000 + 9.9477 -4.1875 1.4569 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0676 -3.1850 1.1716 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7820 -3.1808 1.6974 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9153 -2.1329 1.3798 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3294 -1.0867 0.5375 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4703 -0.0252 0.2106 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8977 1.0066 -0.6271 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1878 0.9906 -1.1489 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0548 -0.0574 -0.8344 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.6411 -1.1023 0.0071 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.5008 -2.1625 0.3346 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.5144 -4.8197 2.0534 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4363 -3.9757 2.3505 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9102 -2.1360 1.7951 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4583 0.0059 0.6080 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2223 1.8225 -0.8713 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5216 1.7931 -1.8011 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0593 -0.0532 -1.2512 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.5134 -2.1967 -0.0603 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2 3 2 0 0 0 0 + 2 11 1 0 0 0 0 + 3 4 1 0 0 0 0 + 4 5 2 0 0 0 0 + 5 6 1 0 0 0 0 + 5 10 1 0 0 0 0 + 6 7 2 0 0 0 0 + 7 8 1 0 0 0 0 + 8 9 2 0 0 0 0 + 9 10 1 0 0 0 0 + 10 11 2 0 0 0 0 + 1 2 1 0 0 0 0 + 1 12 1 0 0 0 0 + 3 13 1 0 0 0 0 + 4 14 1 0 0 0 0 + 6 15 1 0 0 0 0 + 7 16 1 0 0 0 0 + 8 17 1 0 0 0 0 + 9 18 1 0 0 0 0 + 11 19 1 0 0 0 0 +M END +> +-0.506700 0.117500 -0.019600 -0.050600 -0.017700 -0.054000 -0.061200 -0.061200 -0.053900 -0.014400 -0.012500 0.292100 0.065400 0.062500 0.062400 0.061800 0.061800 0.062400 0.066000 + +$$$$ diff --git a/src/openfe/tests/data/lomap_basic/methylcyclohexane.mol2 b/src/openfe/tests/data/lomap_basic/methylcyclohexane.mol2 deleted file mode 100644 index 926963c28..000000000 --- a/src/openfe/tests/data/lomap_basic/methylcyclohexane.mol2 +++ /dev/null @@ -1,50 +0,0 @@ -@MOLECULE -***** - 21 21 0 0 0 -SMALL -GASTEIGER - -@ATOM - 1 C 0.9730 0.1554 0.1412 C.3 1 LIG1 -0.0624 - 2 C 2.4934 0.1043 -0.0303 C.3 1 LIG1 -0.0439 - 3 C 2.9737 -1.3048 -0.4072 C.3 1 LIG1 -0.0505 - 4 C 2.6388 -1.6853 -1.8487 C.3 1 LIG1 -0.0528 - 5 C 3.1458 -0.6424 -2.8397 C.3 1 LIG1 -0.0530 - 6 C 2.6503 0.7581 -2.4959 C.3 1 LIG1 -0.0528 - 7 C 2.9839 1.1382 -1.0536 C.3 1 LIG1 -0.0505 - 8 H 0.6557 1.1542 0.4607 H 1 LIG1 0.0232 - 9 H 0.6478 -0.5562 0.9081 H 1 LIG1 0.0232 - 10 H 0.4392 -0.0840 -0.7835 H 1 LIG1 0.0232 - 11 H 2.9316 0.3596 0.9431 H 1 LIG1 0.0298 - 12 H 2.5484 -2.0480 0.2778 H 1 LIG1 0.0268 - 13 H 4.0632 -1.3509 -0.2778 H 1 LIG1 0.0268 - 14 H 3.0861 -2.6588 -2.0822 H 1 LIG1 0.0265 - 15 H 1.5550 -1.8040 -1.9616 H 1 LIG1 0.0265 - 16 H 4.2430 -0.6482 -2.8423 H 1 LIG1 0.0265 - 17 H 2.8244 -0.9091 -3.8522 H 1 LIG1 0.0265 - 18 H 3.1052 1.4851 -3.1790 H 1 LIG1 0.0265 - 19 H 1.5667 0.8148 -2.6555 H 1 LIG1 0.0265 - 20 H 4.0736 1.2348 -0.9613 H 1 LIG1 0.0268 - 21 H 2.5648 2.1260 -0.8271 H 1 LIG1 0.0268 -@BOND - 1 2 7 1 - 2 2 3 1 - 3 6 7 1 - 4 5 6 1 - 5 4 5 1 - 6 3 4 1 - 7 1 2 1 - 8 1 8 1 - 9 1 9 1 - 10 1 10 1 - 11 2 11 1 - 12 3 12 1 - 13 3 13 1 - 14 4 14 1 - 15 4 15 1 - 16 5 16 1 - 17 5 17 1 - 18 6 18 1 - 19 6 19 1 - 20 7 20 1 - 21 7 21 1 diff --git a/src/openfe/tests/data/lomap_basic/methylcyclohexane.sdf b/src/openfe/tests/data/lomap_basic/methylcyclohexane.sdf new file mode 100644 index 000000000..b7eb9621c --- /dev/null +++ b/src/openfe/tests/data/lomap_basic/methylcyclohexane.sdf @@ -0,0 +1,51 @@ +methylcyclohexane + -OEChem-08192610333D + + 21 21 0 0 0 0 0 0 0999 V2000 + 0.9730 0.1554 0.1412 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4934 0.1043 -0.0303 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9737 -1.3048 -0.4072 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6388 -1.6853 -1.8487 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1458 -0.6424 -2.8397 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6503 0.7581 -2.4959 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9839 1.1382 -1.0536 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.6557 1.1542 0.4607 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.6478 -0.5562 0.9081 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.4392 -0.0840 -0.7835 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9316 0.3596 0.9431 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5484 -2.0480 0.2778 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0632 -1.3509 -0.2778 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.0861 -2.6588 -2.0822 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.5550 -1.8040 -1.9616 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2430 -0.6482 -2.8423 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8244 -0.9091 -3.8522 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1052 1.4851 -3.1790 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.5667 0.8148 -2.6555 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0736 1.2348 -0.9613 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5648 2.1260 -0.8271 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2 7 1 0 0 0 0 + 2 3 1 0 0 0 0 + 6 7 1 0 0 0 0 + 5 6 1 0 0 0 0 + 4 5 1 0 0 0 0 + 3 4 1 0 0 0 0 + 1 2 1 0 0 0 0 + 1 8 1 0 0 0 0 + 1 9 1 0 0 0 0 + 1 10 1 0 0 0 0 + 2 11 1 0 0 0 0 + 3 12 1 0 0 0 0 + 3 13 1 0 0 0 0 + 4 14 1 0 0 0 0 + 4 15 1 0 0 0 0 + 5 16 1 0 0 0 0 + 5 17 1 0 0 0 0 + 6 18 1 0 0 0 0 + 6 19 1 0 0 0 0 + 7 20 1 0 0 0 0 + 7 21 1 0 0 0 0 +M END +> +-0.062400 -0.043900 -0.050500 -0.052800 -0.053000 -0.052800 -0.050500 0.023200 0.023200 0.023200 0.029800 0.026800 0.026800 0.026500 0.026500 0.026500 0.026500 0.026500 0.026500 0.026800 0.026800 + +$$$$ diff --git a/src/openfe/tests/data/lomap_basic/toluene.mol2 b/src/openfe/tests/data/lomap_basic/toluene.mol2 deleted file mode 100644 index 472586c68..000000000 --- a/src/openfe/tests/data/lomap_basic/toluene.mol2 +++ /dev/null @@ -1,38 +0,0 @@ -@MOLECULE -***** - 15 15 0 0 0 -SMALL -GASTEIGER - -@ATOM - 1 C 0.9562 0.0874 0.1225 C.3 1 LIG1 -0.0397 - 2 C 2.4532 0.1169 0.0327 C.ar 1 LIG1 -0.0504 - 3 C 3.1480 1.3313 -0.0192 C.ar 1 LIG1 -0.0588 - 4 C 4.5410 1.3447 -0.1106 C.ar 1 LIG1 -0.0615 - 5 C 5.2502 0.1457 -0.1538 C.ar 1 LIG1 -0.0617 - 6 C 4.5672 -1.0681 -0.1065 C.ar 1 LIG1 -0.0615 - 7 C 3.1745 -1.0830 -0.0148 C.ar 1 LIG1 -0.0588 - 8 H 0.5405 1.0944 0.2295 H 1 LIG1 0.0278 - 9 H 0.6412 -0.4972 0.9928 H 1 LIG1 0.0278 - 10 H 0.5343 -0.3590 -0.7834 H 1 LIG1 0.0278 - 11 H 2.6097 2.2757 0.0116 H 1 LIG1 0.0620 - 12 H 5.0726 2.2915 -0.1488 H 1 LIG1 0.0618 - 13 H 6.3343 0.1573 -0.2252 H 1 LIG1 0.0618 - 14 H 5.1186 -2.0039 -0.1418 H 1 LIG1 0.0618 - 15 H 2.6530 -2.0368 0.0195 H 1 LIG1 0.0620 -@BOND - 1 2 3 ar - 2 3 4 ar - 3 2 7 ar - 4 6 7 ar - 5 5 6 ar - 6 4 5 ar - 7 1 2 1 - 8 1 8 1 - 9 1 9 1 - 10 1 10 1 - 11 3 11 1 - 12 4 12 1 - 13 5 13 1 - 14 6 14 1 - 15 7 15 1 diff --git a/src/openfe/tests/data/lomap_basic/toluene.sdf b/src/openfe/tests/data/lomap_basic/toluene.sdf new file mode 100644 index 000000000..c10f889bb --- /dev/null +++ b/src/openfe/tests/data/lomap_basic/toluene.sdf @@ -0,0 +1,39 @@ +toluene + -OEChem-08192610343D + + 15 15 0 0 0 0 0 0 0999 V2000 + 0.9562 0.0874 0.1225 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4532 0.1169 0.0327 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1480 1.3313 -0.0192 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5410 1.3447 -0.1106 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2502 0.1457 -0.1538 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5672 -1.0681 -0.1065 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1745 -1.0830 -0.0148 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.5405 1.0944 0.2295 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.6412 -0.4972 0.9928 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.5343 -0.3590 -0.7834 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6097 2.2757 0.0116 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0726 2.2915 -0.1488 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3343 0.1573 -0.2252 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1186 -2.0039 -0.1418 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6530 -2.0368 0.0195 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2 3 2 0 0 0 0 + 3 4 1 0 0 0 0 + 2 7 1 0 0 0 0 + 6 7 2 0 0 0 0 + 5 6 1 0 0 0 0 + 4 5 2 0 0 0 0 + 1 2 1 0 0 0 0 + 1 8 1 0 0 0 0 + 1 9 1 0 0 0 0 + 1 10 1 0 0 0 0 + 3 11 1 0 0 0 0 + 4 12 1 0 0 0 0 + 5 13 1 0 0 0 0 + 6 14 1 0 0 0 0 + 7 15 1 0 0 0 0 +M END +> +-0.039700 -0.050400 -0.058800 -0.061500 -0.061700 -0.061500 -0.058800 0.027800 0.027800 0.027800 0.062000 0.061800 0.061800 0.061800 0.062000 + +$$$$ diff --git a/src/openfe/tests/setup/atom_mapping/test_lomap_scorers.py b/src/openfe/tests/setup/atom_mapping/test_lomap_scorers.py index faad4ba47..f05792be2 100644 --- a/src/openfe/tests/setup/atom_mapping/test_lomap_scorers.py +++ b/src/openfe/tests/setup/atom_mapping/test_lomap_scorers.py @@ -287,7 +287,7 @@ def test_lomap_regression( smallmols = [] for i in range(matrix.shape[0]): nm = dbmols[i].getName() - smallmols.append(atom_mapping_basic_test_files[nm[:-5]]) # - ".mol2" + smallmols.append(atom_mapping_basic_test_files[nm[:-4]]) # - ".sdf" mapper = openfe.setup.atom_mapping.LomapAtomMapper( time=20, From 540aefa35cf7db4078e695556438c6f36fd24bce Mon Sep 17 00:00:00 2001 From: Josh Horton Date: Wed, 19 Aug 2026 11:08:12 +0100 Subject: [PATCH 15/21] remove extra file --- charged_benzenes_with_charges.sdf | 126 ------------------------------ 1 file changed, 126 deletions(-) delete mode 100644 charged_benzenes_with_charges.sdf diff --git a/charged_benzenes_with_charges.sdf b/charged_benzenes_with_charges.sdf deleted file mode 100644 index b0018a774..000000000 --- a/charged_benzenes_with_charges.sdf +++ /dev/null @@ -1,126 +0,0 @@ -benzene - RDKit 3D - - 12 12 0 0 0 0 0 0 0 0999 V2000 - 1.4045 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - 0.7022 1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - -0.7023 1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - -1.4045 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - -0.7023 -1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - 0.7023 -1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - 2.5079 -0.0000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - 1.2540 2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - -1.2540 2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - -2.5079 -0.0000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - -1.2540 -2.1719 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - 1.2540 -2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - 1 2 2 0 - 1 6 1 0 - 1 7 1 0 - 2 3 1 0 - 2 8 1 0 - 3 4 2 0 - 3 9 1 0 - 4 5 1 0 - 4 10 1 0 - 5 6 2 0 - 5 11 1 0 - 6 12 1 0 -M END - -> -benzene - -> --0.13019000614682832 -0.1301299991707007 -0.1301299991707007 -0.1301299991707007 -0.1301299991707007 -0.1301299991707007 0.13014000033338866 0.13014000033338866 0.13014000033338866 -0.13014000033338866 0.13014000033338866 0.13014000033338866 - -$$$$ -aniline - RDKit 3D - - 15 15 0 0 0 0 0 0 0 0999 V2000 - 1.4045 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - 0.7022 1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - -0.7023 1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - -1.4045 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - -0.7023 -1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - 0.7023 -1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - 3.2106 0.8251 0.4756 H 0 0 0 0 0 0 0 0 0 0 0 0 - 3.2117 -0.8238 0.4772 H 0 0 0 0 0 0 0 0 0 0 0 0 - 3.2117 -0.0000 -0.9520 H 0 0 0 0 0 0 0 0 0 0 0 0 - 2.8745 -0.0000 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 - 1.2540 2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - -1.2540 2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - -2.5079 -0.0000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - -1.2540 -2.1719 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - 1.2540 -2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - 1 2 2 0 - 1 6 1 0 - 1 10 1 0 - 2 3 1 0 - 2 11 1 0 - 3 4 2 0 - 3 12 1 0 - 4 5 1 0 - 4 13 1 0 - 5 6 2 0 - 5 14 1 0 - 6 15 1 0 - 7 10 1 0 - 8 10 1 0 - 9 10 1 0 -M CHG 1 10 1 -M END - -> -aniline - -> --0.082359999914964038 -0.09780000199874242 -0.098719999690850574 -0.061870001753171283 -0.098719999690850574 -0.09780000199874242 0.46404001067082085 0.46404001067082085 -0.46404001067082085 -0.69748002340396242 0.15680000136295955 0.17632999966541926 0.17636999438206355 0.17632999966541926 0.15680000136295955 - -$$$$ -benzoic_acid - RDKit 3D - - 14 14 0 0 0 0 0 0 0 0999 V2000 - 1.4045 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - 2.7445 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - 3.2607 -1.0833 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 - 0.7022 1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - -0.7023 1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - 3.4265 1.0159 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 - -1.4045 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - -0.7023 -1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - 0.7023 -1.2164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 - 1.2540 2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - -1.2540 2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - -2.5079 -0.0000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - -1.2540 -2.1719 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - 1.2540 -2.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 - 1 2 1 0 - 1 4 2 0 - 1 9 1 0 - 2 3 1 0 - 2 6 2 0 - 4 5 1 0 - 4 10 1 0 - 5 7 2 0 - 5 11 1 0 - 7 8 1 0 - 7 12 1 0 - 8 9 2 0 - 8 13 1 0 - 9 14 1 0 -M CHG 1 3 -1 -M END - -> -benzoic_acid - -> --0.12568999826908112 0.90591001510620117 -0.83410000801086426 -0.10845000296831131 -0.16561999917030334 -0.83410000801086426 -0.16110999882221222 -0.16561999917030334 -0.10845000296831131 -0.14900000393390656 0.10098999738693237 0.097249999642372131 0.10098999738693237 0.14900000393390656 - -$$$$ From bacb1c34cd02c395961fc86ed297ac5c27ebcb55 Mon Sep 17 00:00:00 2001 From: Josh Horton Date: Wed, 19 Aug 2026 13:18:02 +0100 Subject: [PATCH 16/21] fix cli tests --- src/openfecli/tests/data/toluene.mol2 | 38 +++++++++++++++++++ src/openfecli/tests/parameters/test_mol.py | 2 +- .../tests/parameters/test_molecules.py | 16 +------- 3 files changed, 40 insertions(+), 16 deletions(-) create mode 100644 src/openfecli/tests/data/toluene.mol2 diff --git a/src/openfecli/tests/data/toluene.mol2 b/src/openfecli/tests/data/toluene.mol2 new file mode 100644 index 000000000..472586c68 --- /dev/null +++ b/src/openfecli/tests/data/toluene.mol2 @@ -0,0 +1,38 @@ +@MOLECULE +***** + 15 15 0 0 0 +SMALL +GASTEIGER + +@ATOM + 1 C 0.9562 0.0874 0.1225 C.3 1 LIG1 -0.0397 + 2 C 2.4532 0.1169 0.0327 C.ar 1 LIG1 -0.0504 + 3 C 3.1480 1.3313 -0.0192 C.ar 1 LIG1 -0.0588 + 4 C 4.5410 1.3447 -0.1106 C.ar 1 LIG1 -0.0615 + 5 C 5.2502 0.1457 -0.1538 C.ar 1 LIG1 -0.0617 + 6 C 4.5672 -1.0681 -0.1065 C.ar 1 LIG1 -0.0615 + 7 C 3.1745 -1.0830 -0.0148 C.ar 1 LIG1 -0.0588 + 8 H 0.5405 1.0944 0.2295 H 1 LIG1 0.0278 + 9 H 0.6412 -0.4972 0.9928 H 1 LIG1 0.0278 + 10 H 0.5343 -0.3590 -0.7834 H 1 LIG1 0.0278 + 11 H 2.6097 2.2757 0.0116 H 1 LIG1 0.0620 + 12 H 5.0726 2.2915 -0.1488 H 1 LIG1 0.0618 + 13 H 6.3343 0.1573 -0.2252 H 1 LIG1 0.0618 + 14 H 5.1186 -2.0039 -0.1418 H 1 LIG1 0.0618 + 15 H 2.6530 -2.0368 0.0195 H 1 LIG1 0.0620 +@BOND + 1 2 3 ar + 2 3 4 ar + 3 2 7 ar + 4 6 7 ar + 5 5 6 ar + 6 4 5 ar + 7 1 2 1 + 8 1 8 1 + 9 1 9 1 + 10 1 10 1 + 11 3 11 1 + 12 4 12 1 + 13 5 13 1 + 14 6 14 1 + 15 7 15 1 diff --git a/src/openfecli/tests/parameters/test_mol.py b/src/openfecli/tests/parameters/test_mol.py index 680ea0673..8f0e6b55a 100644 --- a/src/openfecli/tests/parameters/test_mol.py +++ b/src/openfecli/tests/parameters/test_mol.py @@ -27,7 +27,7 @@ def test_get_molecule_sdf(): def test_get_molecule_mol2(): - with resources.as_file(resources.files("openfe.tests.data.lomap_basic")) as d: + with resources.as_file(resources.files("openfecli.tests.data")) as d: f = d / "toluene.mol2" mol = get_molecule(str(f)) diff --git a/src/openfecli/tests/parameters/test_molecules.py b/src/openfecli/tests/parameters/test_molecules.py index dd2fa0309..445a7f07d 100644 --- a/src/openfecli/tests/parameters/test_molecules.py +++ b/src/openfecli/tests/parameters/test_molecules.py @@ -8,19 +8,6 @@ from openfe import SmallMoleculeComponent from openfecli.parameters.molecules import load_molecules - -def test_get_dir_molecules_sdf(): - with resources.as_file(resources.files("openfe.tests.data.serialization")) as dir_path: - # Note: the template doesn't include a valid version, but it loads - # anyway. In the future, we may need to create a temporary file with - # template substitutions done, but that seemed like overkill now. - mols = load_molecules(dir_path) - - assert len(mols) == 1 - assert mols[0].smiles == "CC" - assert mols[0].name == "ethane" - - def test_load_molecules_sdf_file(): files = resources.files("openfe.tests.data") ref = files / "benzene_modifications.sdf" @@ -30,7 +17,7 @@ def test_load_molecules_sdf_file(): assert len(mols) == 7 -def test_get_dir_molecules_mol2(): +def test_get_dir_molecules_sdf(): with resources.as_file(resources.files("openfe.tests.data.lomap_basic")) as dir_path: # Note: the template doesn't include a valid version, but it loads # anyway. In the future, we may need to create a temporary file with @@ -41,7 +28,6 @@ def test_get_dir_molecules_mol2(): all_smiles = {mol.smiles for mol in mols} all_names = {mol.name for mol in mols} assert "Cc1cc(C)c2cc(C)ccc2c1" in all_smiles - assert "*****" in all_names def test_get_molecule_error(): From 9cb4322e757959fc0294210cff09f03d03259e05 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 19 Aug 2026 12:19:02 +0000 Subject: [PATCH 17/21] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/openfecli/tests/parameters/test_molecules.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/openfecli/tests/parameters/test_molecules.py b/src/openfecli/tests/parameters/test_molecules.py index 445a7f07d..b2d25142b 100644 --- a/src/openfecli/tests/parameters/test_molecules.py +++ b/src/openfecli/tests/parameters/test_molecules.py @@ -8,6 +8,7 @@ from openfe import SmallMoleculeComponent from openfecli.parameters.molecules import load_molecules + def test_load_molecules_sdf_file(): files = resources.files("openfe.tests.data") ref = files / "benzene_modifications.sdf" From 7b7e4ee417124137828a16e840ee6b6f38df281b Mon Sep 17 00:00:00 2001 From: Josh Horton Date: Thu, 20 Aug 2026 10:51:14 +0100 Subject: [PATCH 18/21] add charges to inputs, remove slow mark --- src/openfe/tests/data/openmm_rfe/ligand_23.sdf | 6 ++++++ src/openfe/tests/data/openmm_rfe/ligand_55.sdf | 6 ++++++ .../tests/protocols/openmm_rfe/test_hybrid_top_protocol.py | 1 - 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/openfe/tests/data/openmm_rfe/ligand_23.sdf b/src/openfe/tests/data/openmm_rfe/ligand_23.sdf index 3a503794e..ee1052b3e 100644 --- a/src/openfe/tests/data/openmm_rfe/ligand_23.sdf +++ b/src/openfe/tests/data/openmm_rfe/ligand_23.sdf @@ -77,4 +77,10 @@ ligand_23 6 29 1 0 1 27 1 0 M END +> (1) +-0.096620000795357763 -0.12906000007771784 0.052329998566872545 -0.1420399992623263 0.052329998566872545 -0.12906000007771784 -0.058970000491374068 0.68882000456667614 -0.54965001334332753 +-0.46614998568677241 0.11851000051117605 -0.28589999665402704 0.43860000382281011 -0.72825998057507801 0.54181998978472423 -0.29723998893880182 -0.55049002160214711 0.70070999871111583 +-0.57516002644681263 -0.24832999695920283 -0.12739999581956202 0.15659999857760137 -0.18966999639653498 -0.058970000491374068 0.097960002823836267 0.093230001731879175 0.14708000431872076 +0.15737000118113226 0.15737000118113226 0.331019997700221 0.17687000344610876 0.026149999453789659 0.17281000326491064 0.32982999096728033 0.096780002220637262 0.096780002220637262 + $$$$ diff --git a/src/openfe/tests/data/openmm_rfe/ligand_55.sdf b/src/openfe/tests/data/openmm_rfe/ligand_55.sdf index 073d61e2b..0c183511d 100644 --- a/src/openfe/tests/data/openmm_rfe/ligand_55.sdf +++ b/src/openfe/tests/data/openmm_rfe/ligand_55.sdf @@ -70,4 +70,10 @@ ligand_55 6 23 1 0 1 21 1 0 M END +> (1) +-0.096989995263742676 -0.12775999160878587 0.05143000332243515 -0.14379000517003465 0.05143000332243515 -0.12775999160878587 -0.060149999730514755 0.68993997720606404 -0.54237001987569256 +-0.47453999372594285 0.12862999884016585 -0.28791001292340684 0.44437000302202773 -0.68067002149693889 0.56722998765833454 -0.34415998908154893 -0.53615999075047893 0.75111001876719075 +-0.52239000650517864 -0.060149999730514755 0.14786000696547102 0.15815000382788252 0.15815000382788252 0.3266800060642488 0.17450000492460799 0.033190002269817123 0.14128999558813643 +0.34145999101526808 0.13978999999888014 0.053180003143621216 0.053180003143621216 -0.45994999858014513 0.053180003143621216 + $$$$ diff --git a/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py b/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py index 39dbdd130..8125ebf68 100644 --- a/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py +++ b/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py @@ -1671,7 +1671,6 @@ def tyk2_reference_xml(): return ET.fromstring(xmldata) -@pytest.mark.slow class TestTyk2XmlRegression: """Generates Hybrid system XML and performs regression test""" From c43dbf087e9a08e48c7d9a81bd3cd945eb07425c Mon Sep 17 00:00:00 2001 From: Josh Horton Date: Thu, 20 Aug 2026 10:56:13 +0100 Subject: [PATCH 19/21] Update src/openfe/tests/protocols/test_openmmutils.py --- src/openfe/tests/protocols/test_openmmutils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openfe/tests/protocols/test_openmmutils.py b/src/openfe/tests/protocols/test_openmmutils.py index 7a654bcc1..2d59cf15a 100644 --- a/src/openfe/tests/protocols/test_openmmutils.py +++ b/src/openfe/tests/protocols/test_openmmutils.py @@ -1526,7 +1526,7 @@ def test_openff_lib_charges_no_charges(self, benzene_no_charge_system, benzene_m benzene_no_charge_system, small_molecule_forcefield=ff.to_string() ) - def test_openff_nagl_mixed_charges(self, mixed_charge_system): + def test_openff_mixed_charges(self, mixed_charge_system): # make sure an error is raised if not all smcs would have deterministic charges with pytest.raises( ProtocolValidationError, From 6c32c99ae12392770d35dcf95eaac843caf7e544 Mon Sep 17 00:00:00 2001 From: Josh Horton Date: Thu, 20 Aug 2026 11:31:20 +0100 Subject: [PATCH 20/21] add news, update error to list all molecules with missing charges. --- news/charge_validation.rst | 23 +++++++++++++ .../openmm_utils/system_validation.py | 15 ++++++--- .../openmm_rfe/test_hybrid_top_protocol.py | 6 ++-- .../tests/protocols/test_openmmutils.py | 33 ++++++++++++++++--- 4 files changed, 65 insertions(+), 12 deletions(-) create mode 100644 news/charge_validation.rst diff --git a/news/charge_validation.rst b/news/charge_validation.rst new file mode 100644 index 000000000..ce783ec93 --- /dev/null +++ b/news/charge_validation.rst @@ -0,0 +1,23 @@ +**Added:** + +* + +**Changed:** + +* All protocols will raise a ``ProtocolValidationError`` error if a non-deterministic partial charge generation method is used at run time, please provide molecules with pre-calculated charges to use ``am1bcc`` or use a deterministic method such as library charges or AshGC. + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/src/openfe/protocols/openmm_utils/system_validation.py b/src/openfe/protocols/openmm_utils/system_validation.py index 96b0bee82..10aa3a4c3 100644 --- a/src/openfe/protocols/openmm_utils/system_validation.py +++ b/src/openfe/protocols/openmm_utils/system_validation.py @@ -408,6 +408,7 @@ def validate_nondeterministic_charges(system: ChemicalSystem, small_molecule_for errmsg = f"Could not load force field {small_molecule_forcefield} or {small_molecule_forcefield}.offxml: {e}" raise ProtocolValidationError(errmsg) + errors = [] for smc in smcs: offmol = smc.to_openff() if offmol.partial_charges is not None and np.any(offmol.partial_charges): @@ -423,8 +424,12 @@ def validate_nondeterministic_charges(system: ChemicalSystem, small_molecule_for # We count library and nagl charges as deterministic # While users could use a deterministic charge method with increments we don't currently support this if not labels["LibraryCharges"] and "NAGLCharges" not in labels: - errmsg = ( - f"{smc} from system {system.name} would have am1bcc charges generated at runtime which is non-deterministic. " - f"Please provide a molecule with pre-computed charges or use library charges instead." - ) - raise ProtocolValidationError(errmsg) + errors.append(smc) + + if errors: + errmsg = ( + f"System: '{system.name}' contains SmallMoleculeComponents which would have am1bcc charges generated at runtime which is non-deterministic. " + f"Please provide a molecule with pre-computed charges or use library charges instead. " + f"The following Components are affected: {', '.join([str(smc) for smc in errors])}" + ) + raise ProtocolValidationError(errmsg) diff --git a/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py b/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py index 8125ebf68..1c0524ec6 100644 --- a/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py +++ b/src/openfe/tests/protocols/openmm_rfe/test_hybrid_top_protocol.py @@ -752,7 +752,6 @@ def test_setup_charge_backends( # make sure an error is raised if a nondeterministic charge method would be used at run time vac_settings.partial_charge_settings.partial_charge_method = "am1bcc" vac_settings.partial_charge_settings.off_toolkit_backend = "ambertools" - vac_settings.partial_charge_settings.nagl_model = "openff-gnn-am1bcc-0.1.0-rc.1.pt" protocol = openmm_rfe.RelativeHybridTopologyProtocol( settings=vac_settings, @@ -761,7 +760,7 @@ def test_setup_charge_backends( # make stateB molecule offmolB = Molecule.from_smiles("CCN") offmolB.generate_conformers() - molB = openfe.SmallMoleculeComponent.from_openff(offmolB) + molB = openfe.SmallMoleculeComponent.from_openff(offmolB, name="CCN") mapping = LigandAtomMapping( componentA=CN_molecule, componentB=molB, componentA_to_componentB={0: 1} ) @@ -771,7 +770,8 @@ def test_setup_charge_backends( with pytest.raises( ProtocolValidationError, match=re.escape( - "SmallMoleculeComponent(name=) from system CN no charges would have am1bcc charges generated at runtime which is non-deterministic." + # this is the CN failing as it has no name + "The following Components are affected: SmallMoleculeComponent(name=)" ), ): _ = protocol.create(stateA=systemA, stateB=systemB, mapping=mapping) diff --git a/src/openfe/tests/protocols/test_openmmutils.py b/src/openfe/tests/protocols/test_openmmutils.py index 2d59cf15a..49839a207 100644 --- a/src/openfe/tests/protocols/test_openmmutils.py +++ b/src/openfe/tests/protocols/test_openmmutils.py @@ -1458,6 +1458,17 @@ def mixed_charge_system(self, benzene_modifications, benzene_modifications_uncha name="mixed charges", ) + @pytest.fixture + def many_no_charge_system(self, benzene_modifications_uncharged): + return ChemicalSystem( + { + "benzene": benzene_modifications_uncharged["benzene"], + "toluene": benzene_modifications_uncharged["toluene"], + "phenol": benzene_modifications_uncharged["phenol"], + }, + name="many no charges", + ) + def test_gaff_with_molecule_charges(self, benzene_charged_system): system_validation.validate_nondeterministic_charges( benzene_charged_system, small_molecule_forcefield="gaff-2.11" @@ -1467,7 +1478,7 @@ def test_gaff_no_charges(self, benzene_no_charge_system): with pytest.raises( ProtocolValidationError, match=re.escape( - "SmallMoleculeComponent(name=benzene) from system no charges would have am1bcc charges" + "The following Components are affected: SmallMoleculeComponent(name=benzene)" ), ): system_validation.validate_nondeterministic_charges( @@ -1483,7 +1494,7 @@ def test_espaloma_no_charges(self, benzene_no_charge_system): with pytest.raises( ProtocolValidationError, match=re.escape( - "SmallMoleculeComponent(name=benzene) from system no charges would have am1bcc charges" + "The following Components are affected: SmallMoleculeComponent(name=benzene)" ), ): system_validation.validate_nondeterministic_charges( @@ -1499,7 +1510,7 @@ def test_openff_no_charges(self, benzene_no_charge_system): with pytest.raises( ProtocolValidationError, match=re.escape( - "SmallMoleculeComponent(name=benzene) from system no charges would have am1bcc charges" + "The following Components are affected: SmallMoleculeComponent(name=benzene)" ), ): system_validation.validate_nondeterministic_charges( @@ -1531,7 +1542,7 @@ def test_openff_mixed_charges(self, mixed_charge_system): with pytest.raises( ProtocolValidationError, match=re.escape( - "SmallMoleculeComponent(name=toluene) from system mixed charges would have am1bcc charges" + "The following Components are affected: SmallMoleculeComponent(name=toluene)" ), ): system_validation.validate_nondeterministic_charges( @@ -1539,3 +1550,17 @@ def test_openff_mixed_charges(self, mixed_charge_system): mixed_charge_system, small_molecule_forcefield="openff-2.2.0.offxml", ) + + def test_openff_many_missing_charges(self, many_no_charge_system): + # make sure an error is raised if not all smcs would have deterministic charges and all smcs are listed in the error message + with pytest.raises( + ProtocolValidationError, + match=re.escape( + "The following Components are affected: SmallMoleculeComponent(name=benzene), SmallMoleculeComponent(name=toluene), SmallMoleculeComponent(name=phenol)" + ), + ): + system_validation.validate_nondeterministic_charges( + # pick a force field with an am1bcc handler + many_no_charge_system, + small_molecule_forcefield="openff-2.2.0.offxml", + ) From 4cf822dec3df0e39b7ff8ba3c498dd34a4c18bca Mon Sep 17 00:00:00 2001 From: Josh Horton Date: Fri, 21 Aug 2026 10:43:58 +0100 Subject: [PATCH 21/21] catch missing librarycharges handler in ff --- src/openfe/protocols/openmm_utils/system_validation.py | 2 +- src/openfe/tests/protocols/test_openmmutils.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/openfe/protocols/openmm_utils/system_validation.py b/src/openfe/protocols/openmm_utils/system_validation.py index 10aa3a4c3..0f56bd6d9 100644 --- a/src/openfe/protocols/openmm_utils/system_validation.py +++ b/src/openfe/protocols/openmm_utils/system_validation.py @@ -423,7 +423,7 @@ def validate_nondeterministic_charges(system: ChemicalSystem, small_molecule_for # We count library and nagl charges as deterministic # While users could use a deterministic charge method with increments we don't currently support this - if not labels["LibraryCharges"] and "NAGLCharges" not in labels: + if not labels.get("LibraryCharges", {}) and "NAGLCharges" not in labels: errors.append(smc) if errors: diff --git a/src/openfe/tests/protocols/test_openmmutils.py b/src/openfe/tests/protocols/test_openmmutils.py index 49839a207..2e5e3c2c5 100644 --- a/src/openfe/tests/protocols/test_openmmutils.py +++ b/src/openfe/tests/protocols/test_openmmutils.py @@ -1506,7 +1506,9 @@ def test_openff_with_molecule_charges(self, benzene_charged_system): benzene_charged_system, small_molecule_forcefield="openff-2.2.0.offxml" ) - def test_openff_no_charges(self, benzene_no_charge_system): + @pytest.mark.parametrize("forcefield", ["openff-1.0.0.offxml", "openff-2.2.0.offxml", "openff-2.2.0"]) + def test_openff_no_charges(self, benzene_no_charge_system, forcefield): + # Test ffs with/out LibraryCharges handler, and with/out .offxml extension with pytest.raises( ProtocolValidationError, match=re.escape( @@ -1514,7 +1516,7 @@ def test_openff_no_charges(self, benzene_no_charge_system): ), ): system_validation.validate_nondeterministic_charges( - benzene_no_charge_system, small_molecule_forcefield="openff-2.2.0.offxml" + benzene_no_charge_system, small_molecule_forcefield=forcefield ) def test_openff_nagl_no_charges(self, benzene_no_charge_system):