OpenEye 2D "courtesy conformer" conflicts with downstream omega use
See original GitHub issueDescribe the bug
A user encountered a bug resulting from the interaction of two seemingly innocuous behaviors.
- If a user makes an OFFMol from SMILES, then immediately writes it to SDF using OpenEyeToolkitWrapper, a 2D “courtesy conformer” of the molecule is automatically generated.
- If omega is passed an input molecule with both graph stereochemistry and 3D stereochemistry, it will “trust” the 3D stereochemistry.
To Reproduce
The reproducing case below relies on (sigh) what I think is another bug where we trust the 3D stereochemistry from a loaded molecule, even if it contradicts the graph stereochemistry. But this can be used in this example to show that the conformer generated from a loaded 2D molecule has the wrong stereochemistry.
from openff.toolkit.topology import Molecule
# Make a simple chiral molecule from SMILES
smiles = "[C@H](F)(Cl)(Br)"
offmol = Molecule.from_smiles(smiles)
# Show that the first stereocenter is S and the mol has 0 conformers
print(offmol.atoms[0].stereochemistry, offmol.conformers)
# Save it to file without adding any conformers
offmol.to_file('xxx.sdf', file_format='sdf')
S None
# Load the molecule we just saved
offmol2 = Molecule.from_file('xxx.sdf', file_format='sdf')
# Show that the first stereocenter is still S and that the mol now has 1 conformer (the "courtesy conformer")
print(offmol2.atoms[0].stereochemistry, offmol2.conformers)
S [Quantity(value=array([[ 0., 0., 0.], [ 1., 0., 0.], [-0., -1., 0.], [ 0., 1., 0.], [-1., 0., 0.]]), unit=angstrom)]
# Generate conformers and show that the GRAPH still thinks the stereo is S, and that
# a new conformer has replaced the courtesy conformer.
offmol2.generate_conformers()
print(offmol2.atoms[0].stereochemistry, offmol2.conformers)
offmol2.to_file('yyy.sdf', file_format='sdf')
S [Quantity(value=array([[ 1.20802656e-01, …]]) , unit=angstrom)]
# Load the newly generated conformer from file, triggering a perception of stereo
# from the geometry (possibly another bug, since I think it should should trust
# the graph stereo...)
offmol3 = Molecule.from_file('yyy.sdf')
# Show that the correct geometry was loaded, but that it's actually the R isomer
print(offmol3.atoms[0].stereochemistry, offmol3.conformers)
R [Quantity(value=array([[ 1.20802656e-01, …]]) , unit=angstrom)]
Suggested resolution
If I delete the courtesy conformer before running offmol2.generate_conformers
, everything works fine:
...
offmol2._conformers = None
offmol2.generate_conformers()
offmol2.to_file('yyy.sdf', file_format='sdf')
offmol3 = Molecule.from_file('yyy.sdf')
print(offmol3.atoms[0].stereochemistry)
S
So I’d propose scrubbing all conformers before invoking omega for conformer generation, so it HAS to read the graph stereo.
Computing environment (please complete the following information):
- Mac OSX 12
- OFFTK 0.10.1, OETK 2020.2.2
Additional context
There may be a less brute-force workaround by manipulating OEOmegaOptions.SetIncludeInput
and/or OEMolBuilderOptions.SetFromCT
but the former seems unlikely to help, and the default value for the latter should yield the behavior that we want, so I didn’t bother trying to dig in to switch them.
Issue Analytics
- State:
- Created 2 years ago
- Comments:5 (5 by maintainers)
Yeah, that’s an important subtlety. The difference here is that RDKitToolkitWrapper can load that 2D SDF and do conformer generation in a way that must rely on the graph stereo (since otherwise it would be guessing 3D stereo from 2D and should have the same problem as OpenEyeToolkitWrapper). So courtesy conformers themselves aren’t really an issue, as long as all of our calls trust graph stereo if there’s a conflict.
FTR the “courtesty conformer” is also something that
RDKitToolkitWrapper
also produces:Out of scope here but would at least like to note somewhere this possibly-strange behavior for searchability:
Maybe I should have already known this, but it took me by surprise when digging deep reviewing the linked PR.