From 75cb071ea8d0493abf9cc7108d0353232b7d50f8 Mon Sep 17 00:00:00 2001 From: "David W.H. Swenson" Date: Fri, 19 Jan 2024 04:39:41 -0600 Subject: [PATCH 1/2] Support `name` in `SmallMoleculeComponent.copy_with_replacements` (#273) * draft for SMC.copy_with_replacements needs tests: one changing the molecule; one changing the name * tests for SMC.copy_with_replacements * minor cleanup --- gufe/components/smallmoleculecomponent.py | 13 +++++++++ gufe/tests/test_smallmoleculecomponent.py | 33 +++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/gufe/components/smallmoleculecomponent.py b/gufe/components/smallmoleculecomponent.py index 849097b5..79786cfe 100644 --- a/gufe/components/smallmoleculecomponent.py +++ b/gufe/components/smallmoleculecomponent.py @@ -264,3 +264,16 @@ def _from_dict(cls, d: dict): m.UpdatePropertyCache() return cls(rdkit=m) + + def copy_with_replacements(self, **replacements): + # this implementation first makes a copy with the name replaced + # only, then does any other replacements that are necessary + if 'name' in replacements: + name = replacements.pop('name') + dct = self._to_dict() + dct['molprops']['ofe-name'] = name + obj = self._from_dict(dct) + else: + obj = self + + return super(SmallMoleculeComponent, obj).copy_with_replacements(**replacements) diff --git a/gufe/tests/test_smallmoleculecomponent.py b/gufe/tests/test_smallmoleculecomponent.py index 8c93a90b..a15c887f 100644 --- a/gufe/tests/test_smallmoleculecomponent.py +++ b/gufe/tests/test_smallmoleculecomponent.py @@ -185,6 +185,39 @@ def test_serialization_cycle_smiles(self, named_ethane): assert named_ethane is not copy assert named_ethane.smiles == copy.smiles + @pytest.mark.parametrize('replace', ( + ['name'], + ['mol'], + ['name', 'mol'], + )) + def test_copy_with_replacements(self, named_ethane, replace): + replacements = {} + if 'name' in replace: + replacements['name'] = "foo" + + if 'mol' in replace: + # it is a little weird to use copy_with_replacements to replace + # the whole molecule (possibly keeping the same name), but it + # should work if someone does! (could more easily imagine only + # using a new conformer) + rdmol = Chem.AddHs(Chem.MolFromSmiles("CO")) + Chem.AllChem.Compute2DCoords(rdmol) + mol = SmallMoleculeComponent.from_rdkit(rdmol) + dct = mol._to_dict() + for item in ['atoms', 'bonds', 'conformer']: + replacements[item] = dct[item] + + new = named_ethane.copy_with_replacements(**replacements) + if 'name' in replace: + assert new.name == "foo" + else: + assert new.name == "ethane" + + if 'mol' in replace: + assert new.smiles == "CO" + else: + assert new.smiles == "CC" + @pytest.mark.skipif(not HAS_OFFTK, reason="no openff toolkit available") class TestSmallMoleculeComponentConversion: From a426dce524111531b8b6bd5c0b7d48f31c6df587 Mon Sep 17 00:00:00 2001 From: Richard Gowers Date: Fri, 19 Jan 2024 16:02:17 +0000 Subject: [PATCH 2/2] makes AtomMappers subclass from GufeTokenizable (#266) Co-authored-by: Irfan Alibay --- gufe/mapping/atom_mapper.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gufe/mapping/atom_mapper.py b/gufe/mapping/atom_mapper.py index d6506d3f..8c648077 100644 --- a/gufe/mapping/atom_mapper.py +++ b/gufe/mapping/atom_mapper.py @@ -4,10 +4,11 @@ from collections.abc import Iterator import gufe +from ..tokenization import GufeTokenizable from .atom_mapping import AtomMapping -class AtomMapper(abc.ABC): +class AtomMapper(GufeTokenizable): """A class for manufacturing mappings""" @abc.abstractmethod def suggest_mappings(self, A: gufe.Component,