From 20aadeffe250d95ef594aee8793648463cb28381 Mon Sep 17 00:00:00 2001 From: pbuslaev Date: Tue, 9 Apr 2024 17:29:48 +0100 Subject: [PATCH 01/29] Add atom type merging for Interchange.to_gromacs() --- .../interop/gromacs/export/_export.py | 54 +++++++++++++++---- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/openff/interchange/interop/gromacs/export/_export.py b/openff/interchange/interop/gromacs/export/_export.py index 4ec010981..35d74863e 100644 --- a/openff/interchange/interop/gromacs/export/_export.py +++ b/openff/interchange/interop/gromacs/export/_export.py @@ -32,9 +32,9 @@ def to_top(self): with open(self.top_file, "w") as top: self._write_defaults(top) - self._write_atomtypes(top) + to_reduced_atom_types = self._write_atomtypes(top) - self._write_moleculetypes(top) + self._write_moleculetypes(top, to_reduced_atom_types) self._write_system(top) self._write_molecules(top) @@ -60,20 +60,56 @@ def _write_defaults(self, top): f"{self.system.coul_14:8.6f}\n\n", ) - def _write_atomtypes(self, top): + def _write_atomtypes(self, top) -> dict: top.write("[ atomtypes ]\n") top.write( ";type, bondingtype, atomic_number, mass, charge, ptype, sigma, epsilon\n", ) + reduced_atom_types = [] + to_reduced_atom_types = {} + + def _is_atom_type_in_list(atom_type, atom_type_list, diff: float = 1e-5) -> bool | str: + """ + Checks if the atom type is already in list. + """ + for _at_name, _atom_type in atom_type_list: + if ( + atom_type.atomic_number == _atom_type.atomic_number and + numpy.abs(atom_type.mass.m - _atom_type.mass.m) < diff and + numpy.abs(atom_type.sigma.m - _atom_type.sigma.m) < diff and + numpy.abs(atom_type.epsilon.m - _atom_type.epsilon.m) < diff + ): + return _at_name + return False + + def _get_new_entry_name(atom_type_list) -> str: + """ + Entry name for atom type to be added. + """ + if len(reduced_atom_types) > 0: + _previous_name = reduced_atom_types[-1][0] + _previous_idx = int(_previous_name.split("_")[1]) + return f"AT_{_previous_idx+1}" + else: + return "AT_0" + for atom_type in self.system.atom_types.values(): if not isinstance(atom_type, LennardJonesAtomType): raise NotImplementedError( "Only Lennard-Jones atom types are currently supported.", ) + + if _is_atom_type_in_list(atom_type, reduced_atom_types): + to_reduced_atom_types[atom_type.name] = _is_atom_type_in_list(atom_type, reduced_atom_types) + else: + _at_name = _get_new_entry_name(reduced_atom_types) + reduced_atom_types.append((_at_name, atom_type)) + to_reduced_atom_types[atom_type.name] = _at_name + for atom_type_name, atom_type in reduced_atom_types: top.write( - f"{atom_type.name :<11s}\t" + f"{atom_type_name :<11s}\t" f"{atom_type.atomic_number :6d}\t" f"{atom_type.mass.m :.16g}\t" f"{atom_type.charge.m :.16f}\t" @@ -81,10 +117,10 @@ def _write_atomtypes(self, top): f"{atom_type.sigma.m :.16g}\t" f"{atom_type.epsilon.m :.16g}\n", ) - top.write("\n") + return to_reduced_atom_types - def _write_moleculetypes(self, top): + def _write_moleculetypes(self, top, to_reduced_atom_types): for molecule_name, molecule_type in self.system.molecule_types.items(): top.write("[ moleculetype ]\n") @@ -93,7 +129,7 @@ def _write_moleculetypes(self, top): f"{molecule_type.nrexcl:10d}\n\n", ) - self._write_atoms(top, molecule_type) + self._write_atoms(top, molecule_type, to_reduced_atom_types) self._write_pairs(top, molecule_type) self._write_bonds(top, molecule_type) self._write_angles(top, molecule_type) @@ -104,14 +140,14 @@ def _write_moleculetypes(self, top): top.write("\n") - def _write_atoms(self, top, molecule_type): + def _write_atoms(self, top, molecule_type, to_reduced_atom_types): top.write("[ atoms ]\n") top.write(";index, atom type, resnum, resname, name, cgnr, charge, mass\n") for atom in molecule_type.atoms: top.write( f"{atom.index :6d} " - f"{atom.atom_type :6s}" + f"{to_reduced_atom_types[atom.atom_type] :6s}" f"{atom.residue_index :8d} " f"{atom.residue_name :8s} " f"{atom.name :6s}" From 36fcd52b15f9bdfc97bed416399cb86fa352326e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 9 Apr 2024 16:32:41 +0000 Subject: [PATCH 02/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../interop/gromacs/export/_export.py | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/openff/interchange/interop/gromacs/export/_export.py b/openff/interchange/interop/gromacs/export/_export.py index 35d74863e..ed4d4bac5 100644 --- a/openff/interchange/interop/gromacs/export/_export.py +++ b/openff/interchange/interop/gromacs/export/_export.py @@ -69,16 +69,18 @@ def _write_atomtypes(self, top) -> dict: reduced_atom_types = [] to_reduced_atom_types = {} - def _is_atom_type_in_list(atom_type, atom_type_list, diff: float = 1e-5) -> bool | str: + def _is_atom_type_in_list( + atom_type, atom_type_list, diff: float = 1e-5 + ) -> bool | str: """ Checks if the atom type is already in list. """ for _at_name, _atom_type in atom_type_list: if ( - atom_type.atomic_number == _atom_type.atomic_number and - numpy.abs(atom_type.mass.m - _atom_type.mass.m) < diff and - numpy.abs(atom_type.sigma.m - _atom_type.sigma.m) < diff and - numpy.abs(atom_type.epsilon.m - _atom_type.epsilon.m) < diff + atom_type.atomic_number == _atom_type.atomic_number + and numpy.abs(atom_type.mass.m - _atom_type.mass.m) < diff + and numpy.abs(atom_type.sigma.m - _atom_type.sigma.m) < diff + and numpy.abs(atom_type.epsilon.m - _atom_type.epsilon.m) < diff ): return _at_name return False @@ -93,15 +95,17 @@ def _get_new_entry_name(atom_type_list) -> str: return f"AT_{_previous_idx+1}" else: return "AT_0" - + for atom_type in self.system.atom_types.values(): if not isinstance(atom_type, LennardJonesAtomType): raise NotImplementedError( "Only Lennard-Jones atom types are currently supported.", ) - + if _is_atom_type_in_list(atom_type, reduced_atom_types): - to_reduced_atom_types[atom_type.name] = _is_atom_type_in_list(atom_type, reduced_atom_types) + to_reduced_atom_types[atom_type.name] = _is_atom_type_in_list( + atom_type, reduced_atom_types + ) else: _at_name = _get_new_entry_name(reduced_atom_types) reduced_atom_types.append((_at_name, atom_type)) From c98e14ebf93106054460c88a45e02efab562426c Mon Sep 17 00:00:00 2001 From: pbuslaev Date: Mon, 15 Apr 2024 14:12:39 +0100 Subject: [PATCH 03/29] Update openff/interchange/interop/gromacs/export/_export.py Co-authored-by: Matt Thompson --- openff/interchange/interop/gromacs/export/_export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openff/interchange/interop/gromacs/export/_export.py b/openff/interchange/interop/gromacs/export/_export.py index ed4d4bac5..926963e31 100644 --- a/openff/interchange/interop/gromacs/export/_export.py +++ b/openff/interchange/interop/gromacs/export/_export.py @@ -32,7 +32,7 @@ def to_top(self): with open(self.top_file, "w") as top: self._write_defaults(top) - to_reduced_atom_types = self._write_atomtypes(top) + reduced_atom_types = self._write_atomtypes(top) self._write_moleculetypes(top, to_reduced_atom_types) From 6954a9a589efde72d1802da9e6cdefe1f40331d2 Mon Sep 17 00:00:00 2001 From: pbuslaev Date: Mon, 15 Apr 2024 14:13:16 +0100 Subject: [PATCH 04/29] Update openff/interchange/interop/gromacs/export/_export.py Co-authored-by: Matt Thompson --- openff/interchange/interop/gromacs/export/_export.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openff/interchange/interop/gromacs/export/_export.py b/openff/interchange/interop/gromacs/export/_export.py index 926963e31..ffee6f35d 100644 --- a/openff/interchange/interop/gromacs/export/_export.py +++ b/openff/interchange/interop/gromacs/export/_export.py @@ -78,9 +78,9 @@ def _is_atom_type_in_list( for _at_name, _atom_type in atom_type_list: if ( atom_type.atomic_number == _atom_type.atomic_number - and numpy.abs(atom_type.mass.m - _atom_type.mass.m) < diff - and numpy.abs(atom_type.sigma.m - _atom_type.sigma.m) < diff - and numpy.abs(atom_type.epsilon.m - _atom_type.epsilon.m) < diff + and abs(atom_type.mass - _atom_type.mass).m < mass_tolerance + and abs(atom_type.sigma - _atom_type.sigma).m < sigma_tolerance + and abs(atom_type.epsilon - _atom_type.epsilon).m < epsilon_tolerance ): return _at_name return False From 4ddc1e084c3a9693f28874455a0913d74f57a3bf Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 13:13:16 +0000 Subject: [PATCH 05/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- openff/interchange/interop/gromacs/export/_export.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/openff/interchange/interop/gromacs/export/_export.py b/openff/interchange/interop/gromacs/export/_export.py index ffee6f35d..aecfb4858 100644 --- a/openff/interchange/interop/gromacs/export/_export.py +++ b/openff/interchange/interop/gromacs/export/_export.py @@ -70,7 +70,9 @@ def _write_atomtypes(self, top) -> dict: to_reduced_atom_types = {} def _is_atom_type_in_list( - atom_type, atom_type_list, diff: float = 1e-5 + atom_type, + atom_type_list, + diff: float = 1e-5, ) -> bool | str: """ Checks if the atom type is already in list. @@ -104,7 +106,8 @@ def _get_new_entry_name(atom_type_list) -> str: if _is_atom_type_in_list(atom_type, reduced_atom_types): to_reduced_atom_types[atom_type.name] = _is_atom_type_in_list( - atom_type, reduced_atom_types + atom_type, + reduced_atom_types, ) else: _at_name = _get_new_entry_name(reduced_atom_types) From e38de61fffa7679243324d2eb196251960b8ee9d Mon Sep 17 00:00:00 2001 From: pbuslaev Date: Mon, 15 Apr 2024 14:13:36 +0100 Subject: [PATCH 06/29] Update openff/interchange/interop/gromacs/export/_export.py Co-authored-by: Matt Thompson --- openff/interchange/interop/gromacs/export/_export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openff/interchange/interop/gromacs/export/_export.py b/openff/interchange/interop/gromacs/export/_export.py index aecfb4858..8acfd5873 100644 --- a/openff/interchange/interop/gromacs/export/_export.py +++ b/openff/interchange/interop/gromacs/export/_export.py @@ -60,7 +60,7 @@ def _write_defaults(self, top): f"{self.system.coul_14:8.6f}\n\n", ) - def _write_atomtypes(self, top) -> dict: + def _write_atomtypes(self, top) -> dict[str, str]: top.write("[ atomtypes ]\n") top.write( ";type, bondingtype, atomic_number, mass, charge, ptype, sigma, epsilon\n", From f8907ae6fec39b60bcfcb9f45690ceb64d83b188 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 13:14:16 +0000 Subject: [PATCH 07/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- openff/interchange/interop/gromacs/export/_export.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openff/interchange/interop/gromacs/export/_export.py b/openff/interchange/interop/gromacs/export/_export.py index 8acfd5873..ab735d5f3 100644 --- a/openff/interchange/interop/gromacs/export/_export.py +++ b/openff/interchange/interop/gromacs/export/_export.py @@ -82,7 +82,8 @@ def _is_atom_type_in_list( atom_type.atomic_number == _atom_type.atomic_number and abs(atom_type.mass - _atom_type.mass).m < mass_tolerance and abs(atom_type.sigma - _atom_type.sigma).m < sigma_tolerance - and abs(atom_type.epsilon - _atom_type.epsilon).m < epsilon_tolerance + and abs(atom_type.epsilon - _atom_type.epsilon).m + < epsilon_tolerance ): return _at_name return False From b85edc796c83e255ed3fbed6ce842841ff059c83 Mon Sep 17 00:00:00 2001 From: pbuslaev Date: Mon, 15 Apr 2024 14:50:04 +0100 Subject: [PATCH 08/29] Update _export.py Added `merge_atom_types` flag to `to_top` method. Changed variables names --- .../interop/gromacs/export/_export.py | 85 +++++++++++++------ 1 file changed, 57 insertions(+), 28 deletions(-) diff --git a/openff/interchange/interop/gromacs/export/_export.py b/openff/interchange/interop/gromacs/export/_export.py index ab735d5f3..a94496322 100644 --- a/openff/interchange/interop/gromacs/export/_export.py +++ b/openff/interchange/interop/gromacs/export/_export.py @@ -25,16 +25,16 @@ class GROMACSWriter(DefaultModel): top_file: pathlib.Path | str | None = None gro_file: pathlib.Path | str | None = None - def to_top(self): + def to_top(self, merge_atom_types: bool = False): """Write a GROMACS topology file.""" if self.top_file is None: raise ValueError("No TOP file specified.") with open(self.top_file, "w") as top: self._write_defaults(top) - reduced_atom_types = self._write_atomtypes(top) + mapping_to_reduced_atom_types = self._write_atomtypes(top, merge_atom_types: bool = False) - self._write_moleculetypes(top, to_reduced_atom_types) + self._write_moleculetypes(top, mapping_to_reduced_atom_types, merge_atom_types) self._write_system(top) self._write_molecules(top) @@ -60,23 +60,25 @@ def _write_defaults(self, top): f"{self.system.coul_14:8.6f}\n\n", ) - def _write_atomtypes(self, top) -> dict[str, str]: + def _write_atomtypes(self, top, merge_atom_types: bool) -> dict[str, str]: top.write("[ atomtypes ]\n") top.write( ";type, bondingtype, atomic_number, mass, charge, ptype, sigma, epsilon\n", ) reduced_atom_types = [] - to_reduced_atom_types = {} + mapping_to_reduced_atom_types = {} def _is_atom_type_in_list( atom_type, atom_type_list, - diff: float = 1e-5, ) -> bool | str: """ Checks if the atom type is already in list. """ + mass_tolerance = Quantity("1e-5 amu") + sigma_tolerance = Quantity("1e-5 nanometer") + epsilon_tolerance = Quantity("1e-5 kilojoule_per_mol") for _at_name, _atom_type in atom_type_list: if ( atom_type.atomic_number == _atom_type.atomic_number @@ -105,16 +107,31 @@ def _get_new_entry_name(atom_type_list) -> str: "Only Lennard-Jones atom types are currently supported.", ) - if _is_atom_type_in_list(atom_type, reduced_atom_types): - to_reduced_atom_types[atom_type.name] = _is_atom_type_in_list( - atom_type, - reduced_atom_types, - ) + if merge_atom_types: + if _is_atom_type_in_list(atom_type, reduced_atom_types): + mapping_to_reduced_atom_types[atom_type.name] = _is_atom_type_in_list( + atom_type, + reduced_atom_types, + ) + else: + _at_name = _get_new_entry_name(reduced_atom_types) + reduced_atom_types.append((_at_name, atom_type)) + mapping_to_reduced_atom_types[atom_type.name] = _at_name else: - _at_name = _get_new_entry_name(reduced_atom_types) - reduced_atom_types.append((_at_name, atom_type)) - to_reduced_atom_types[atom_type.name] = _at_name + top.write( + f"{atom_type.name :<11s}\t" + f"{atom_type.atomic_number :6d}\t" + f"{atom_type.mass.m :.16g}\t" + f"{atom_type.charge.m :.16f}\t" + f"{atom_type.particle_type :5s}\t" + f"{atom_type.sigma.m :.16g}\t" + f"{atom_type.epsilon.m :.16g}\n", + ) + if not merge_atom_types: + top.write("\n") + return mapping_to_reduced_atom_types + for atom_type_name, atom_type in reduced_atom_types: top.write( f"{atom_type_name :<11s}\t" @@ -126,9 +143,9 @@ def _get_new_entry_name(atom_type_list) -> str: f"{atom_type.epsilon.m :.16g}\n", ) top.write("\n") - return to_reduced_atom_types + return mapping_to_reduced_atom_types - def _write_moleculetypes(self, top, to_reduced_atom_types): + def _write_moleculetypes(self, top, mapping_to_reduced_atom_types, merge_atom_types: bool): for molecule_name, molecule_type in self.system.molecule_types.items(): top.write("[ moleculetype ]\n") @@ -137,7 +154,7 @@ def _write_moleculetypes(self, top, to_reduced_atom_types): f"{molecule_type.nrexcl:10d}\n\n", ) - self._write_atoms(top, molecule_type, to_reduced_atom_types) + self._write_atoms(top, molecule_type, mapping_to_reduced_atom_types, merge_atom_types: bool) self._write_pairs(top, molecule_type) self._write_bonds(top, molecule_type) self._write_angles(top, molecule_type) @@ -148,21 +165,33 @@ def _write_moleculetypes(self, top, to_reduced_atom_types): top.write("\n") - def _write_atoms(self, top, molecule_type, to_reduced_atom_types): + def _write_atoms(self, top, molecule_type, mapping_to_reduced_atom_types, merge_atom_types: bool): top.write("[ atoms ]\n") top.write(";index, atom type, resnum, resname, name, cgnr, charge, mass\n") for atom in molecule_type.atoms: - top.write( - f"{atom.index :6d} " - f"{to_reduced_atom_types[atom.atom_type] :6s}" - f"{atom.residue_index :8d} " - f"{atom.residue_name :8s} " - f"{atom.name :6s}" - f"{atom.charge_group_number :6d}" - f"{atom.charge.m :20.12f}" - f"{atom.mass.m :20.12f}\n", - ) + if merge_atom_types: + top.write( + f"{atom.index :6d} " + f"{mapping_to_reduced_atom_types[atom.atom_type] :6s}" + f"{atom.residue_index :8d} " + f"{atom.residue_name :8s} " + f"{atom.name :6s}" + f"{atom.charge_group_number :6d}" + f"{atom.charge.m :20.12f}" + f"{atom.mass.m :20.12f}\n", + ) + else: + top.write( + f"{atom.index :6d} " + f"{atom.atom_type :6s}" + f"{atom.residue_index :8d} " + f"{atom.residue_name :8s} " + f"{atom.name :6s}" + f"{atom.charge_group_number :6d}" + f"{atom.charge.m :20.12f}" + f"{atom.mass.m :20.12f}\n", + ) top.write("\n") From 817141c5959b60fdae0500c9d3a9101bc3e6d0a1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 13:51:14 +0000 Subject: [PATCH 09/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- openff/interchange/interop/gromacs/export/_export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openff/interchange/interop/gromacs/export/_export.py b/openff/interchange/interop/gromacs/export/_export.py index a94496322..6898148b1 100644 --- a/openff/interchange/interop/gromacs/export/_export.py +++ b/openff/interchange/interop/gromacs/export/_export.py @@ -131,7 +131,7 @@ def _get_new_entry_name(atom_type_list) -> str: if not merge_atom_types: top.write("\n") return mapping_to_reduced_atom_types - + for atom_type_name, atom_type in reduced_atom_types: top.write( f"{atom_type_name :<11s}\t" From 2070603f879e9afb44b9c8d0ba0bb63b8d1e2f9c Mon Sep 17 00:00:00 2001 From: pbuslaev Date: Mon, 15 Apr 2024 15:20:07 +0100 Subject: [PATCH 10/29] Added tests to merging atomtypes. Added flag for merging atomtypes to `Interchange.to_gromacs()` --- .../interop/gromacs/export/test_export.py | 72 +++++++++++++++++++ openff/interchange/components/interchange.py | 3 +- openff/interchange/drivers/gromacs.py | 3 +- 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py b/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py index 713107eec..0f727d352 100644 --- a/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py +++ b/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py @@ -232,6 +232,78 @@ def test_simple_roundtrip(self, sage, smiles, reader): }, ) + @pytest.mark.slow + @pytest.mark.skip("from_top is not yet refactored for new Topology API") + @pytest.mark.parametrize("reader", ["intermol", "internal"]) + @pytest.mark.parametrize( + "smiles", + [ + "CC", # Two identical carbons + "C1CCCCC1", # Identical carbons in the ring + "C1C(N)CC(N)CC1", # Identical carbons and nitrogens in the ring + ], + ) + def test_energies_with_merging_atom_types(self, sage, smiles, reader): + """ + Tests #962 + """ + molecule = MoleculeWithConformer.from_smiles(smiles) + molecule.name = molecule.to_hill_formula() + topology = molecule.to_topology() + + out = Interchange.from_smirnoff(force_field=sage, topology=topology) + out.box = [4, 4, 4] + out.positions = molecule.conformers[0] + + get_gromacs_energies(out).compare( + get_gromacs_energies(out, merge_atom_types=True), + tolerances={ + "Bond": 0.002 * molecule.n_bonds * unit.kilojoule / unit.mol, + "Electrostatics": 0.05 * unit.kilojoule / unit.mol, + }, + ) + + @pytest.mark.slow + @pytest.mark.skip("from_top is not yet refactored for new Topology API") + @pytest.mark.parametrize("reader", ["intermol", "internal"]) + @pytest.mark.parametrize( + "smiles", + [ + "CC", # Two identical carbons + "C1CCCCC1", # Identical carbons in the ring + "C1C(N)CC(N)CC1", # Identical carbons and nitrogens in the ring + ], + ) + def test_simple_roundtrip_with_merging_atom_types(self, sage, smiles, reader): + """ + Tests #962 + """ + molecule = MoleculeWithConformer.from_smiles(smiles) + molecule.name = molecule.to_hill_formula() + topology = molecule.to_topology() + + out = Interchange.from_smirnoff(force_field=sage, topology=topology) + out.box = [4, 4, 4] + out.positions = molecule.conformers[0] + + out.to_top("out.top") + out.to_top("out_merged.top", merge_atom_types=True) + out.to_gro("out.gro") + + converted = Interchange.from_gromacs("out.top", "out.gro", reader=reader) + converted_merged = Interchange.from_gromacs("out_merged.top", "out.gro", reader=reader) + + assert numpy.allclose(converted.positions, converted_merged.positions) + assert numpy.allclose(converted.box, converted_merged.box) + + get_gromacs_energies(converted_merged).compare( + get_gromacs_energies(converted), + tolerances={ + "Bond": 0.002 * molecule.n_bonds * unit.kilojoule / unit.mol, + "Electrostatics": 0.05 * unit.kilojoule / unit.mol, + }, + ) + @skip_if_missing("parmed") def test_num_impropers(self, sage): out = Interchange.from_smirnoff( diff --git a/openff/interchange/components/interchange.py b/openff/interchange/components/interchange.py index 02eaa9874..2a72590b1 100644 --- a/openff/interchange/components/interchange.py +++ b/openff/interchange/components/interchange.py @@ -421,6 +421,7 @@ def to_gromacs( prefix: str, decimal: int = 3, hydrogen_mass: float = 1.007947, + merge_atom_types: bool = False, ): """ Export this Interchange object to GROMACS files. @@ -447,7 +448,7 @@ def to_gromacs( gro_file=prefix + ".gro", ) - writer.to_top() + writer.to_top(merge_atom_types=merge_atom_types) writer.to_gro(decimal=decimal) def to_top( diff --git a/openff/interchange/drivers/gromacs.py b/openff/interchange/drivers/gromacs.py index 9b212aeac..0704917a0 100644 --- a/openff/interchange/drivers/gromacs.py +++ b/openff/interchange/drivers/gromacs.py @@ -88,11 +88,12 @@ def _get_gromacs_energies( interchange: Interchange, mdp: str = "auto", round_positions: int = 8, + merge_atom_types: bool = False, ) -> dict[str, unit.Quantity]: with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): prefix = "_tmp" - interchange.to_gromacs(prefix=prefix, decimal=round_positions) + interchange.to_gromacs(prefix=prefix, decimal=round_positions, merge_atom_types=merge_atom_types) if mdp == "auto": mdconfig = MDConfig.from_interchange(interchange) From 185d88d2c2c9bf75233963a16955a1f1ad408a4a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 14:20:34 +0000 Subject: [PATCH 11/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../_tests/unit_tests/interop/gromacs/export/test_export.py | 4 +++- openff/interchange/drivers/gromacs.py | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py b/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py index 0f727d352..de77b15b2 100644 --- a/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py +++ b/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py @@ -291,7 +291,9 @@ def test_simple_roundtrip_with_merging_atom_types(self, sage, smiles, reader): out.to_gro("out.gro") converted = Interchange.from_gromacs("out.top", "out.gro", reader=reader) - converted_merged = Interchange.from_gromacs("out_merged.top", "out.gro", reader=reader) + converted_merged = Interchange.from_gromacs( + "out_merged.top", "out.gro", reader=reader + ) assert numpy.allclose(converted.positions, converted_merged.positions) assert numpy.allclose(converted.box, converted_merged.box) diff --git a/openff/interchange/drivers/gromacs.py b/openff/interchange/drivers/gromacs.py index 0704917a0..0a09778de 100644 --- a/openff/interchange/drivers/gromacs.py +++ b/openff/interchange/drivers/gromacs.py @@ -93,7 +93,11 @@ def _get_gromacs_energies( with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): prefix = "_tmp" - interchange.to_gromacs(prefix=prefix, decimal=round_positions, merge_atom_types=merge_atom_types) + interchange.to_gromacs( + prefix=prefix, + decimal=round_positions, + merge_atom_types=merge_atom_types, + ) if mdp == "auto": mdconfig = MDConfig.from_interchange(interchange) From 85bff3198c0ab3f2da17860a2c769cfc962f3ff7 Mon Sep 17 00:00:00 2001 From: pbuslaev Date: Mon, 15 Apr 2024 16:11:56 +0100 Subject: [PATCH 12/29] Fixed typo --- openff/interchange/interop/gromacs/export/_export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openff/interchange/interop/gromacs/export/_export.py b/openff/interchange/interop/gromacs/export/_export.py index 6898148b1..ffd62e4a5 100644 --- a/openff/interchange/interop/gromacs/export/_export.py +++ b/openff/interchange/interop/gromacs/export/_export.py @@ -32,7 +32,7 @@ def to_top(self, merge_atom_types: bool = False): with open(self.top_file, "w") as top: self._write_defaults(top) - mapping_to_reduced_atom_types = self._write_atomtypes(top, merge_atom_types: bool = False) + mapping_to_reduced_atom_types = self._write_atomtypes(top, merge_atom_types) self._write_moleculetypes(top, mapping_to_reduced_atom_types, merge_atom_types) From 3d071f4f50dcf9c0b906c3e933f965ac9ee68523 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 15:12:55 +0000 Subject: [PATCH 13/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../_tests/unit_tests/interop/gromacs/export/test_export.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py b/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py index de77b15b2..00e3f8db1 100644 --- a/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py +++ b/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py @@ -292,7 +292,9 @@ def test_simple_roundtrip_with_merging_atom_types(self, sage, smiles, reader): converted = Interchange.from_gromacs("out.top", "out.gro", reader=reader) converted_merged = Interchange.from_gromacs( - "out_merged.top", "out.gro", reader=reader + "out_merged.top", + "out.gro", + reader=reader, ) assert numpy.allclose(converted.positions, converted_merged.positions) From 2b8fec943694de52cd1e5eabe721f3109e439b75 Mon Sep 17 00:00:00 2001 From: pbuslaev Date: Mon, 15 Apr 2024 17:36:17 +0100 Subject: [PATCH 14/29] Fixed typo --- openff/interchange/interop/gromacs/export/_export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openff/interchange/interop/gromacs/export/_export.py b/openff/interchange/interop/gromacs/export/_export.py index ffd62e4a5..4761b1f52 100644 --- a/openff/interchange/interop/gromacs/export/_export.py +++ b/openff/interchange/interop/gromacs/export/_export.py @@ -154,7 +154,7 @@ def _write_moleculetypes(self, top, mapping_to_reduced_atom_types, merge_atom_ty f"{molecule_type.nrexcl:10d}\n\n", ) - self._write_atoms(top, molecule_type, mapping_to_reduced_atom_types, merge_atom_types: bool) + self._write_atoms(top, molecule_type, mapping_to_reduced_atom_types, merge_atom_types) self._write_pairs(top, molecule_type) self._write_bonds(top, molecule_type) self._write_angles(top, molecule_type) From 09c44bd201c6f8d5c7a7a7c930c40802c445dafd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 16:39:59 +0000 Subject: [PATCH 15/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../interop/gromacs/export/_export.py | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/openff/interchange/interop/gromacs/export/_export.py b/openff/interchange/interop/gromacs/export/_export.py index 4761b1f52..a3aca5ee3 100644 --- a/openff/interchange/interop/gromacs/export/_export.py +++ b/openff/interchange/interop/gromacs/export/_export.py @@ -34,7 +34,9 @@ def to_top(self, merge_atom_types: bool = False): self._write_defaults(top) mapping_to_reduced_atom_types = self._write_atomtypes(top, merge_atom_types) - self._write_moleculetypes(top, mapping_to_reduced_atom_types, merge_atom_types) + self._write_moleculetypes( + top, mapping_to_reduced_atom_types, merge_atom_types + ) self._write_system(top) self._write_molecules(top) @@ -109,9 +111,11 @@ def _get_new_entry_name(atom_type_list) -> str: if merge_atom_types: if _is_atom_type_in_list(atom_type, reduced_atom_types): - mapping_to_reduced_atom_types[atom_type.name] = _is_atom_type_in_list( - atom_type, - reduced_atom_types, + mapping_to_reduced_atom_types[atom_type.name] = ( + _is_atom_type_in_list( + atom_type, + reduced_atom_types, + ) ) else: _at_name = _get_new_entry_name(reduced_atom_types) @@ -145,7 +149,9 @@ def _get_new_entry_name(atom_type_list) -> str: top.write("\n") return mapping_to_reduced_atom_types - def _write_moleculetypes(self, top, mapping_to_reduced_atom_types, merge_atom_types: bool): + def _write_moleculetypes( + self, top, mapping_to_reduced_atom_types, merge_atom_types: bool + ): for molecule_name, molecule_type in self.system.molecule_types.items(): top.write("[ moleculetype ]\n") @@ -154,7 +160,9 @@ def _write_moleculetypes(self, top, mapping_to_reduced_atom_types, merge_atom_ty f"{molecule_type.nrexcl:10d}\n\n", ) - self._write_atoms(top, molecule_type, mapping_to_reduced_atom_types, merge_atom_types) + self._write_atoms( + top, molecule_type, mapping_to_reduced_atom_types, merge_atom_types + ) self._write_pairs(top, molecule_type) self._write_bonds(top, molecule_type) self._write_angles(top, molecule_type) @@ -165,7 +173,9 @@ def _write_moleculetypes(self, top, mapping_to_reduced_atom_types, merge_atom_ty top.write("\n") - def _write_atoms(self, top, molecule_type, mapping_to_reduced_atom_types, merge_atom_types: bool): + def _write_atoms( + self, top, molecule_type, mapping_to_reduced_atom_types, merge_atom_types: bool + ): top.write("[ atoms ]\n") top.write(";index, atom type, resnum, resname, name, cgnr, charge, mass\n") From ff52df3fd5f7b52da8f1bbcd513ee070d9f0139f Mon Sep 17 00:00:00 2001 From: pbuslaev Date: Tue, 16 Apr 2024 19:42:18 +0100 Subject: [PATCH 16/29] Update openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py Co-authored-by: Matt Thompson --- .../_tests/unit_tests/interop/gromacs/export/test_export.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py b/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py index 00e3f8db1..478b28ba5 100644 --- a/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py +++ b/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py @@ -233,7 +233,6 @@ def test_simple_roundtrip(self, sage, smiles, reader): ) @pytest.mark.slow - @pytest.mark.skip("from_top is not yet refactored for new Topology API") @pytest.mark.parametrize("reader", ["intermol", "internal"]) @pytest.mark.parametrize( "smiles", From 66acca85022906aab08e575aae861dad00a3d72b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 18:42:41 +0000 Subject: [PATCH 17/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../interop/gromacs/export/_export.py | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/openff/interchange/interop/gromacs/export/_export.py b/openff/interchange/interop/gromacs/export/_export.py index a3aca5ee3..a24315db4 100644 --- a/openff/interchange/interop/gromacs/export/_export.py +++ b/openff/interchange/interop/gromacs/export/_export.py @@ -35,7 +35,9 @@ def to_top(self, merge_atom_types: bool = False): mapping_to_reduced_atom_types = self._write_atomtypes(top, merge_atom_types) self._write_moleculetypes( - top, mapping_to_reduced_atom_types, merge_atom_types + top, + mapping_to_reduced_atom_types, + merge_atom_types, ) self._write_system(top) @@ -150,7 +152,10 @@ def _get_new_entry_name(atom_type_list) -> str: return mapping_to_reduced_atom_types def _write_moleculetypes( - self, top, mapping_to_reduced_atom_types, merge_atom_types: bool + self, + top, + mapping_to_reduced_atom_types, + merge_atom_types: bool, ): for molecule_name, molecule_type in self.system.molecule_types.items(): top.write("[ moleculetype ]\n") @@ -161,7 +166,10 @@ def _write_moleculetypes( ) self._write_atoms( - top, molecule_type, mapping_to_reduced_atom_types, merge_atom_types + top, + molecule_type, + mapping_to_reduced_atom_types, + merge_atom_types, ) self._write_pairs(top, molecule_type) self._write_bonds(top, molecule_type) @@ -174,7 +182,11 @@ def _write_moleculetypes( top.write("\n") def _write_atoms( - self, top, molecule_type, mapping_to_reduced_atom_types, merge_atom_types: bool + self, + top, + molecule_type, + mapping_to_reduced_atom_types, + merge_atom_types: bool, ): top.write("[ atoms ]\n") top.write(";index, atom type, resnum, resname, name, cgnr, charge, mass\n") From 9092e3fef5009f120c1b736923411718377e71f7 Mon Sep 17 00:00:00 2001 From: pbuslaev Date: Tue, 16 Apr 2024 19:46:37 +0100 Subject: [PATCH 18/29] Fixed imports --- openff/interchange/interop/gromacs/export/_export.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openff/interchange/interop/gromacs/export/_export.py b/openff/interchange/interop/gromacs/export/_export.py index a24315db4..78776d1db 100644 --- a/openff/interchange/interop/gromacs/export/_export.py +++ b/openff/interchange/interop/gromacs/export/_export.py @@ -80,16 +80,16 @@ def _is_atom_type_in_list( """ Checks if the atom type is already in list. """ + from openff.toolkit import Quantity mass_tolerance = Quantity("1e-5 amu") sigma_tolerance = Quantity("1e-5 nanometer") epsilon_tolerance = Quantity("1e-5 kilojoule_per_mol") for _at_name, _atom_type in atom_type_list: if ( - atom_type.atomic_number == _atom_type.atomic_number - and abs(atom_type.mass - _atom_type.mass).m < mass_tolerance - and abs(atom_type.sigma - _atom_type.sigma).m < sigma_tolerance - and abs(atom_type.epsilon - _atom_type.epsilon).m - < epsilon_tolerance + atom_type.atomic_number == _atom_type.atomic_number and + abs(atom_type.mass - _atom_type.mass).m < mass_tolerance and + abs(atom_type.sigma - _atom_type.sigma).m < sigma_tolerance and + abs(atom_type.epsilon - _atom_type.epsilon).m < epsilon_tolerance ): return _at_name return False From 0f72fe57d6d9e35206f41aa2a75db6b0384e9ca1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 18:47:02 +0000 Subject: [PATCH 19/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- openff/interchange/interop/gromacs/export/_export.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/openff/interchange/interop/gromacs/export/_export.py b/openff/interchange/interop/gromacs/export/_export.py index 78776d1db..005a9d0a7 100644 --- a/openff/interchange/interop/gromacs/export/_export.py +++ b/openff/interchange/interop/gromacs/export/_export.py @@ -81,15 +81,17 @@ def _is_atom_type_in_list( Checks if the atom type is already in list. """ from openff.toolkit import Quantity + mass_tolerance = Quantity("1e-5 amu") sigma_tolerance = Quantity("1e-5 nanometer") epsilon_tolerance = Quantity("1e-5 kilojoule_per_mol") for _at_name, _atom_type in atom_type_list: if ( - atom_type.atomic_number == _atom_type.atomic_number and - abs(atom_type.mass - _atom_type.mass).m < mass_tolerance and - abs(atom_type.sigma - _atom_type.sigma).m < sigma_tolerance and - abs(atom_type.epsilon - _atom_type.epsilon).m < epsilon_tolerance + atom_type.atomic_number == _atom_type.atomic_number + and abs(atom_type.mass - _atom_type.mass).m < mass_tolerance + and abs(atom_type.sigma - _atom_type.sigma).m < sigma_tolerance + and abs(atom_type.epsilon - _atom_type.epsilon).m + < epsilon_tolerance ): return _at_name return False From 28171c8999e66ac73d3c1df2c3cb9bb38fa1d0e8 Mon Sep 17 00:00:00 2001 From: pbuslaev Date: Tue, 16 Apr 2024 21:50:39 +0100 Subject: [PATCH 20/29] Fixed smiles for testing --- .../_tests/unit_tests/interop/gromacs/export/test_export.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py b/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py index 478b28ba5..dc6c550b6 100644 --- a/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py +++ b/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py @@ -239,7 +239,7 @@ def test_simple_roundtrip(self, sage, smiles, reader): [ "CC", # Two identical carbons "C1CCCCC1", # Identical carbons in the ring - "C1C(N)CC(N)CC1", # Identical carbons and nitrogens in the ring + "C1[C@@H](N)C[C@@H](N)CC1", # Identical carbons and nitrogens in the ring ], ) def test_energies_with_merging_atom_types(self, sage, smiles, reader): @@ -270,7 +270,7 @@ def test_energies_with_merging_atom_types(self, sage, smiles, reader): [ "CC", # Two identical carbons "C1CCCCC1", # Identical carbons in the ring - "C1C(N)CC(N)CC1", # Identical carbons and nitrogens in the ring + "C1[C@@H](N)C[C@@H](N)CC1", # Identical carbons and nitrogens in the ring ], ) def test_simple_roundtrip_with_merging_atom_types(self, sage, smiles, reader): From 50b98d83bb489037e2754c6bb46abf7cff3b761f Mon Sep 17 00:00:00 2001 From: pbuslaev Date: Tue, 16 Apr 2024 21:57:26 +0100 Subject: [PATCH 21/29] Fixed to_gromacs documentation --- openff/interchange/components/interchange.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openff/interchange/components/interchange.py b/openff/interchange/components/interchange.py index 2a72590b1..f75543ba0 100644 --- a/openff/interchange/components/interchange.py +++ b/openff/interchange/components/interchange.py @@ -437,7 +437,9 @@ def to_gromacs( The mass to use for hydrogen atoms if not present in the topology. If non-trivially different than the default value, mass will be transferred from neighboring heavy atoms. Note that this is currently not applied to any waters and is unsupported when virtual sites are present. - + merge_atom_types: bool, default = False + The flag to define behaviour of GROMACSWriter. If True, then similar atom types will be merged. + If False, each atom will have its own atom type. """ from openff.interchange.interop.gromacs.export._export import GROMACSWriter from openff.interchange.smirnoff._gromacs import _convert From 712ab1f277ff6d832c2609130cc853b380b18363 Mon Sep 17 00:00:00 2001 From: pbuslaev Date: Tue, 16 Apr 2024 21:59:36 +0100 Subject: [PATCH 22/29] Added blank line to docs --- openff/interchange/components/interchange.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openff/interchange/components/interchange.py b/openff/interchange/components/interchange.py index f75543ba0..66c85a9f7 100644 --- a/openff/interchange/components/interchange.py +++ b/openff/interchange/components/interchange.py @@ -440,6 +440,7 @@ def to_gromacs( merge_atom_types: bool, default = False The flag to define behaviour of GROMACSWriter. If True, then similar atom types will be merged. If False, each atom will have its own atom type. + """ from openff.interchange.interop.gromacs.export._export import GROMACSWriter from openff.interchange.smirnoff._gromacs import _convert From daf225cf020035c7587ae881ad30b952add02dc1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 21:01:07 +0000 Subject: [PATCH 23/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- openff/interchange/components/interchange.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openff/interchange/components/interchange.py b/openff/interchange/components/interchange.py index 66c85a9f7..22cbbf5ef 100644 --- a/openff/interchange/components/interchange.py +++ b/openff/interchange/components/interchange.py @@ -440,7 +440,7 @@ def to_gromacs( merge_atom_types: bool, default = False The flag to define behaviour of GROMACSWriter. If True, then similar atom types will be merged. If False, each atom will have its own atom type. - + """ from openff.interchange.interop.gromacs.export._export import GROMACSWriter from openff.interchange.smirnoff._gromacs import _convert From 363802e004195bde8ab02eb87bcca7bc6fe132c1 Mon Sep 17 00:00:00 2001 From: pbuslaev Date: Tue, 16 Apr 2024 22:38:29 +0100 Subject: [PATCH 24/29] Added merge_atom_types flag to get_gromac_energies --- openff/interchange/drivers/gromacs.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openff/interchange/drivers/gromacs.py b/openff/interchange/drivers/gromacs.py index 0a09778de..21e3f7294 100644 --- a/openff/interchange/drivers/gromacs.py +++ b/openff/interchange/drivers/gromacs.py @@ -51,6 +51,7 @@ def get_gromacs_energies( mdp: str = "auto", round_positions: int = 8, detailed: bool = False, + merge_atom_types: bool = False, ) -> EnergyReport: """ Given an OpenFF Interchange object, return single-point energies as computed by GROMACS. @@ -67,6 +68,8 @@ def get_gromacs_energies( A decimal precision for the positions in the `.gro` file. detailed : bool, default=False If True, return a detailed report containing the energies of each term. + merge_atom_types: bool, default=False + If True, energy should be computed with merging atom types. Returns ------- @@ -79,6 +82,7 @@ def get_gromacs_energies( interchange=interchange, mdp=mdp, round_positions=round_positions, + merge_atom_types=merge_atom_types, ), detailed=detailed, ) From cd650ee12ee843a8d3905049641f071ebdeb43cd Mon Sep 17 00:00:00 2001 From: pbuslaev Date: Tue, 16 Apr 2024 23:16:20 +0100 Subject: [PATCH 25/29] Fixed typo in tolerance --- openff/interchange/interop/gromacs/export/_export.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openff/interchange/interop/gromacs/export/_export.py b/openff/interchange/interop/gromacs/export/_export.py index 005a9d0a7..64db1acfc 100644 --- a/openff/interchange/interop/gromacs/export/_export.py +++ b/openff/interchange/interop/gromacs/export/_export.py @@ -80,11 +80,11 @@ def _is_atom_type_in_list( """ Checks if the atom type is already in list. """ - from openff.toolkit import Quantity + from openff.units import Quantity mass_tolerance = Quantity("1e-5 amu") sigma_tolerance = Quantity("1e-5 nanometer") - epsilon_tolerance = Quantity("1e-5 kilojoule_per_mol") + epsilon_tolerance = Quantity("1e-5 kilojoule_per_mole") for _at_name, _atom_type in atom_type_list: if ( atom_type.atomic_number == _atom_type.atomic_number From 5948f36ad377170066dfc4972c3e2b70bcb98f6a Mon Sep 17 00:00:00 2001 From: pbuslaev Date: Tue, 16 Apr 2024 23:34:11 +0100 Subject: [PATCH 26/29] Fixed quantity comparison --- openff/interchange/interop/gromacs/export/_export.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openff/interchange/interop/gromacs/export/_export.py b/openff/interchange/interop/gromacs/export/_export.py index 64db1acfc..ef827a6f4 100644 --- a/openff/interchange/interop/gromacs/export/_export.py +++ b/openff/interchange/interop/gromacs/export/_export.py @@ -88,9 +88,9 @@ def _is_atom_type_in_list( for _at_name, _atom_type in atom_type_list: if ( atom_type.atomic_number == _atom_type.atomic_number - and abs(atom_type.mass - _atom_type.mass).m < mass_tolerance - and abs(atom_type.sigma - _atom_type.sigma).m < sigma_tolerance - and abs(atom_type.epsilon - _atom_type.epsilon).m + and abs(atom_type.mass - _atom_type.mass) < mass_tolerance + and abs(atom_type.sigma - _atom_type.sigma) < sigma_tolerance + and abs(atom_type.epsilon - _atom_type.epsilon) < epsilon_tolerance ): return _at_name From fe3cd938a09c9f4ff4d6cbd8f8eef0ad106f3a47 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 22:34:34 +0000 Subject: [PATCH 27/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- openff/interchange/interop/gromacs/export/_export.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openff/interchange/interop/gromacs/export/_export.py b/openff/interchange/interop/gromacs/export/_export.py index ef827a6f4..26487dbe2 100644 --- a/openff/interchange/interop/gromacs/export/_export.py +++ b/openff/interchange/interop/gromacs/export/_export.py @@ -90,8 +90,7 @@ def _is_atom_type_in_list( atom_type.atomic_number == _atom_type.atomic_number and abs(atom_type.mass - _atom_type.mass) < mass_tolerance and abs(atom_type.sigma - _atom_type.sigma) < sigma_tolerance - and abs(atom_type.epsilon - _atom_type.epsilon) - < epsilon_tolerance + and abs(atom_type.epsilon - _atom_type.epsilon) < epsilon_tolerance ): return _at_name return False From 51c9465bed9fe82b576a7ed4271384f06110e79f Mon Sep 17 00:00:00 2001 From: pbuslaev Date: Mon, 22 Apr 2024 10:50:10 +0100 Subject: [PATCH 28/29] Added underscore to all public methods with merge_atom_types flag --- .../unit_tests/interop/gromacs/export/test_export.py | 4 ++-- openff/interchange/components/interchange.py | 12 ++++++++---- openff/interchange/drivers/gromacs.py | 8 ++++---- openff/interchange/interop/gromacs/export/_export.py | 6 +++--- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py b/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py index dc6c550b6..4e2d5e411 100644 --- a/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py +++ b/openff/interchange/_tests/unit_tests/interop/gromacs/export/test_export.py @@ -255,7 +255,7 @@ def test_energies_with_merging_atom_types(self, sage, smiles, reader): out.positions = molecule.conformers[0] get_gromacs_energies(out).compare( - get_gromacs_energies(out, merge_atom_types=True), + get_gromacs_energies(out, _merge_atom_types=True), tolerances={ "Bond": 0.002 * molecule.n_bonds * unit.kilojoule / unit.mol, "Electrostatics": 0.05 * unit.kilojoule / unit.mol, @@ -286,7 +286,7 @@ def test_simple_roundtrip_with_merging_atom_types(self, sage, smiles, reader): out.positions = molecule.conformers[0] out.to_top("out.top") - out.to_top("out_merged.top", merge_atom_types=True) + out.to_top("out_merged.top", _merge_atom_types=True) out.to_gro("out.gro") converted = Interchange.from_gromacs("out.top", "out.gro", reader=reader) diff --git a/openff/interchange/components/interchange.py b/openff/interchange/components/interchange.py index 22cbbf5ef..4efbd1a8d 100644 --- a/openff/interchange/components/interchange.py +++ b/openff/interchange/components/interchange.py @@ -421,7 +421,7 @@ def to_gromacs( prefix: str, decimal: int = 3, hydrogen_mass: float = 1.007947, - merge_atom_types: bool = False, + _merge_atom_types: bool = False, ): """ Export this Interchange object to GROMACS files. @@ -437,7 +437,7 @@ def to_gromacs( The mass to use for hydrogen atoms if not present in the topology. If non-trivially different than the default value, mass will be transferred from neighboring heavy atoms. Note that this is currently not applied to any waters and is unsupported when virtual sites are present. - merge_atom_types: bool, default = False + _merge_atom_types: bool, default = False The flag to define behaviour of GROMACSWriter. If True, then similar atom types will be merged. If False, each atom will have its own atom type. @@ -451,13 +451,14 @@ def to_gromacs( gro_file=prefix + ".gro", ) - writer.to_top(merge_atom_types=merge_atom_types) + writer.to_top(_merge_atom_types=_merge_atom_types) writer.to_gro(decimal=decimal) def to_top( self, file_path: Path | str, hydrogen_mass: float = 1.007947, + _merge_atom_types: bool = False, ): """ Export this Interchange to a GROMACS topology file. @@ -470,6 +471,9 @@ def to_top( The mass to use for hydrogen atoms if not present in the topology. If non-trivially different than the default value, mass will be transferred from neighboring heavy atoms. Note that this is currently not applied to any waters and is unsupported when virtual sites are present. + _merge_atom_types: book, default=False + The flag to define behaviour of GROMACSWriter. If True, then similar atom types will be merged. + If False, each atom will have its own atom type. """ from openff.interchange.interop.gromacs.export._export import GROMACSWriter @@ -478,7 +482,7 @@ def to_top( GROMACSWriter( system=_convert(self, hydrogen_mass=hydrogen_mass), top_file=file_path, - ).to_top() + ).to_top(_merge_atom_types=_merge_atom_types) def to_gro(self, file_path: Path | str, decimal: int = 3): """ diff --git a/openff/interchange/drivers/gromacs.py b/openff/interchange/drivers/gromacs.py index 21e3f7294..29915ebf0 100644 --- a/openff/interchange/drivers/gromacs.py +++ b/openff/interchange/drivers/gromacs.py @@ -51,7 +51,7 @@ def get_gromacs_energies( mdp: str = "auto", round_positions: int = 8, detailed: bool = False, - merge_atom_types: bool = False, + _merge_atom_types: bool = False, ) -> EnergyReport: """ Given an OpenFF Interchange object, return single-point energies as computed by GROMACS. @@ -68,7 +68,7 @@ def get_gromacs_energies( A decimal precision for the positions in the `.gro` file. detailed : bool, default=False If True, return a detailed report containing the energies of each term. - merge_atom_types: bool, default=False + _merge_atom_types: bool, default=False If True, energy should be computed with merging atom types. Returns @@ -82,7 +82,7 @@ def get_gromacs_energies( interchange=interchange, mdp=mdp, round_positions=round_positions, - merge_atom_types=merge_atom_types, + merge_atom_types=_merge_atom_types, ), detailed=detailed, ) @@ -100,7 +100,7 @@ def _get_gromacs_energies( interchange.to_gromacs( prefix=prefix, decimal=round_positions, - merge_atom_types=merge_atom_types, + _merge_atom_types=merge_atom_types, ) if mdp == "auto": diff --git a/openff/interchange/interop/gromacs/export/_export.py b/openff/interchange/interop/gromacs/export/_export.py index 26487dbe2..2679319e4 100644 --- a/openff/interchange/interop/gromacs/export/_export.py +++ b/openff/interchange/interop/gromacs/export/_export.py @@ -25,19 +25,19 @@ class GROMACSWriter(DefaultModel): top_file: pathlib.Path | str | None = None gro_file: pathlib.Path | str | None = None - def to_top(self, merge_atom_types: bool = False): + def to_top(self, _merge_atom_types: bool = False): """Write a GROMACS topology file.""" if self.top_file is None: raise ValueError("No TOP file specified.") with open(self.top_file, "w") as top: self._write_defaults(top) - mapping_to_reduced_atom_types = self._write_atomtypes(top, merge_atom_types) + mapping_to_reduced_atom_types = self._write_atomtypes(top, _merge_atom_types) self._write_moleculetypes( top, mapping_to_reduced_atom_types, - merge_atom_types, + _merge_atom_types, ) self._write_system(top) From 4531a7571c3c4f1a87304dc977b530ddb1335b86 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 09:50:35 +0000 Subject: [PATCH 29/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- openff/interchange/interop/gromacs/export/_export.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openff/interchange/interop/gromacs/export/_export.py b/openff/interchange/interop/gromacs/export/_export.py index 2679319e4..fe4af79a9 100644 --- a/openff/interchange/interop/gromacs/export/_export.py +++ b/openff/interchange/interop/gromacs/export/_export.py @@ -32,7 +32,9 @@ def to_top(self, _merge_atom_types: bool = False): with open(self.top_file, "w") as top: self._write_defaults(top) - mapping_to_reduced_atom_types = self._write_atomtypes(top, _merge_atom_types) + mapping_to_reduced_atom_types = self._write_atomtypes( + top, _merge_atom_types + ) self._write_moleculetypes( top,