diff --git a/docs/releasehistory.md b/docs/releasehistory.md index e41a93848..96addb86e 100644 --- a/docs/releasehistory.md +++ b/docs/releasehistory.md @@ -14,6 +14,8 @@ Releases follow the `major.minor.micro` scheme recommended by [PEP440](https://w ### Bugfixes +- [PR #1778](https://github.com/openforcefield/openff-toolkit/pull/1778): Ensures SD data tags are preserved in `Molecule.from_openeye` if the input is of type `oechem.OEGraphMol`. + ### New features ### Improved documentation and warnings diff --git a/openff/toolkit/_tests/test_toolkits.py b/openff/toolkit/_tests/test_toolkits.py index 332289f9a..d15b85691 100644 --- a/openff/toolkit/_tests/test_toolkits.py +++ b/openff/toolkit/_tests/test_toolkits.py @@ -550,6 +550,17 @@ def test_to_from_openeye_core_props_unset(self): == expected_output_smiles ) + def test_sd_tags_in_graph_mol_preserved(self): + from openeye import oechem + + graph = oechem.OEGraphMol() + + oechem.OEParseSmiles(graph, "CCO") + + oechem.OEAddSDData(graph, "cat", "meow") + + assert Molecule.from_openeye(graph).properties["cat"] == "meow" + def test_to_from_openeye_none_partial_charges(self): """Test to ensure that to_openeye and from_openeye correctly handle None partial charges""" import math diff --git a/openff/toolkit/topology/molecule.py b/openff/toolkit/topology/molecule.py index a59b292ab..8905f9efd 100644 --- a/openff/toolkit/topology/molecule.py +++ b/openff/toolkit/topology/molecule.py @@ -2718,6 +2718,8 @@ def assign_fractional_bond_orders( def _invalidate_cached_properties(self) -> None: """ Indicate that the chemical entity has been altered. + + Note that this does not clear the `.properties` dictionary attribute. """ self._conformers = None self._partial_charges = None diff --git a/openff/toolkit/utils/openeye_wrapper.py b/openff/toolkit/utils/openeye_wrapper.py index dc618c431..414a2041a 100644 --- a/openff/toolkit/utils/openeye_wrapper.py +++ b/openff/toolkit/utils/openeye_wrapper.py @@ -1125,6 +1125,12 @@ def from_openeye( from openeye import oechem + # Save the existing SD tags, if any, since they're lost in the cast to + # OEMol of the input is OEGraphMol. See issue #1711 + existing_sd_tags = { + pair.GetTag(): pair.GetValue() for pair in oechem.OEGetSDDataIter(oemol) + } + oemol = oechem.OEMol(oemol) # Add explicit hydrogens if they're implicit @@ -1207,9 +1213,9 @@ def describe_oeatom(oeatom) -> str: if oemol.GetTitle() != "": molecule.name = oemol.GetTitle() - # Copy any attached SD tag information - for dp in oechem.OEGetSDDataPairs(oemol): - molecule._properties[dp.GetTag()] = dp.GetValue() + # Attach any SD tag information we saved before casting input to new OEMol + for key, value in existing_sd_tags.items(): + molecule._properties[key] = value off_to_oe_idx = dict() # {oemol_idx: molecule_idx} atom_mapping = {}