Repository: rdkit/rdkit — pull request #4355 (cpp)
Title: Fixes #4346 : trans bonds are now properly honored in macrocycles

We didn't have code which handled the possibility that there'd be a trans bond in a ring. :-(



Resolved issues:
title: Specified trans stereo being ignored during conformation generation in macrocycles
body: **Describe the bug**
Here's the example:
```
smis = r''smis = r'''C1CC=CCCCCCCCC1
C1C/C=C/CCCCCCCC1
C1C\C=C/CCCCCCCC1'''.split('\n')
mols = [Chem.MolFromSmiles(x) for x in smis]

mol_hs = [Chem.AddHs(m) for m in mols]
for mol in mol_hs:
    ok = Chem.MolToInchiKey(mol)
    rdDistGeom.EmbedMolecule(mol,randomSeed=0xf00d,enforceChirality=True,useBasicKnowledge=True)
    mol = Chem.RemoveHs(mol)
    Chem.AssignStereochemistryFrom3D(mol,replaceExistingTags=True)
    print(Chem.MolToSmiles(mol),ok, Chem.MolToInchiKey(mol))
```
Output:
```
C1=C/CCCCCCCCCC/1 HYPABJGVBDSCIT-UHFFFAOYSA-N HYPABJGVBDSCIT-OWOJBTEDSA-N
C1=C\CCCCCCCCCC/1 HYPABJGVBDSCIT-OWOJBTEDSA-N HYPABJGVBDSCIT-UPHRSURJSA-N
C1=C\CCCCCCCCCC/1 HYPABJGVBDSCIT-UPHRSURJSA-N HYPABJGVBDSCIT-UPHRSURJSA-N
```
If we open up the macrocycle things are fine:
```
smis = r'''CCC=CCCCCCCCC
CC/C=C/CCCCCCCC
CC\C=C/CCCCCCCC'''.split('\n')
mols = [Chem.MolFromSmiles(x) for x in smis]

mol_hs = [Chem.AddHs(m) for m in mols]
for mol in mol_hs:
    ok = Chem.MolToInchiKey(mol)
    rdDistGeom.EmbedMolecule(mol,randomSeed=0xf00d,enforceChirality=True,useBasicKnowledge=True)
    mol = Chem.RemoveHs(mol)
    Chem.AssignStereochemistryFrom3D(mol,replaceExistingTags=True)
    print(Chem.MolToSmiles(mol),ok, Chem.MolToInchiKey(mol))
```
Output:
```
CC/C=C\CCCCCCCC WLTSXAIICPDFKI-UHFFFAOYSA-N WLTSXAIICPDFKI-ALCCZGGFSA-N
CC/C=C/CCCCCCCC WLTSXAIICPDFKI-FNORWQNLSA-N WLTSXAIICPDFKI-FNORWQNLSA-N
CC/C=C\CCCCCCCC WLTSXAIICPDFKI-ALCCZGGFSA-N WLTSXAIICPDFKI-ALCCZGGFSA-N
```

**Configuration (please complete the following information):**
 - RDKit version: master and previous releases
 - OS: all




Hints:

Fix patch:
diff --git a/Code/GraphMol/DistGeomHelpers/BoundsMatrixBuilder.cpp b/Code/GraphMol/DistGeomHelpers/BoundsMatrixBuilder.cpp
index e37eb79acb1..0d581f48cde 100644
--- a/Code/GraphMol/DistGeomHelpers/BoundsMatrixBuilder.cpp
+++ b/Code/GraphMol/DistGeomHelpers/BoundsMatrixBuilder.cpp
@@ -1,5 +1,5 @@
 //
-//  Copyright (C) 2004-2019 Greg Landrum and Rational Discovery LLC
+//  Copyright (C) 2004-2021 Greg Landrum and other RDKit contributors
 //
 //   @@ All Rights Reserved @@
 //  This file is part of the RDKit.
@@ -610,6 +610,7 @@ void _setInRing14Bounds(const ROMol &mol, const Bond *bnd1, const Bond *bnd2,
   path14.bid3 = bid3;
   Bond::BondStereo stype = _getAtomStereo(bnd2, aid1, aid4);
   bool preferCis = false;
+  bool preferTrans = false;
 
   // we add a check for the ring size here because there's no reason to assume
   // cis bonds in bigger rings.
@@ -634,9 +635,12 @@ void _setInRing14Bounds(const ROMol &mol, const Bond *bnd1, const Bond *bnd2,
     }
   } else if (stype == Bond::STEREOZ || stype == Bond::STEREOCIS) {
     preferCis = true;
+  } else if (stype == Bond::STEREOE || stype == Bond::STEREOTRANS) {
+    preferTrans = true;
   }
-  // std::cerr << "  torsion: " << aid1 << " " << aid4 << ": " << preferCis
-  //           << std::endl;
+  // std::cerr << "  torsion: " << aid1 << " " << aid4 << ": " << preferCis << "
+  // "
+  //           << preferTrans << std::endl;
   if (preferCis) {
     dl = RDGeom::compute14DistCis(bl1, bl2, bl3, ba12, ba23) - GEN_DIST_TOL;
     du = dl + 2 * GEN_DIST_TOL;
@@ -645,6 +649,15 @@ void _setInRing14Bounds(const ROMol &mol, const Bond *bnd1, const Bond *bnd2,
                        bid3] = 1;
     accumData.cisPaths[static_cast<unsigned long>(bid3) * nb * nb + bid2 * nb +
                        bid1] = 1;
+  } else if (preferTrans) {
+    dl = RDGeom::compute14DistTrans(bl1, bl2, bl3, ba12, ba23) - GEN_DIST_TOL;
+    du = dl + 2 * GEN_DIST_TOL;
+    path14.type = Path14Configuration::TRANS;
+    accumData.transPaths[static_cast<unsigned long>(bid1) * nb * nb +
+                         bid2 * nb + bid3] = 1;
+    accumData.transPaths[static_cast<unsigned long>(bid3) * nb * nb +
+                         bid2 * nb + bid1] = 1;
+
   } else {
     // basically we will assume 0 to 180 allowed
     dl = RDGeom::compute14DistCis(bl1, bl2, bl3, ba12, ba23);
@@ -812,7 +825,7 @@ bool _checkNhChChNh(const Atom *atm1, const Atom *atm2, const Atom *atm3,
 //      2   5  <- 2 is an oxygen/nitrogen
 bool _checkAmideEster14(const Bond *bnd1, const Bond *bnd3, const Atom *atm1,
                         const Atom *atm2, const Atom *atm3, const Atom *atm4) {
-  unsigned int a1Num = atm1->getAtomicNum();
+  RDUNUSED_PARAM(atm1);
   unsigned int a2Num = atm2->getAtomicNum();
   unsigned int a3Num = atm3->getAtomicNum();
   unsigned int a4Num = atm4->getAtomicNum();
@@ -917,6 +930,7 @@ bool _isCarbonyl(const ROMol &mol, const Atom *at) {
 bool _checkAmideEster15(const ROMol &mol, const Bond *bnd1, const Bond *bnd3,
                         const Atom *atm1, const Atom *atm2, const Atom *atm3,
                         const Atom *atm4) {
+  RDUNUSED_PARAM(atm1);
   RDUNUSED_PARAM(atm4);
   unsigned int a2Num = atm2->getAtomicNum();
   if ((a2Num == 8) || ((a2Num == 7) && (atm2->getTotalNumHs(true) == 1))) {
diff --git a/Code/GraphMol/DistGeomHelpers/Embedder.cpp b/Code/GraphMol/DistGeomHelpers/Embedder.cpp
index d260d869f85..f99af457931 100644
--- a/Code/GraphMol/DistGeomHelpers/Embedder.cpp
+++ b/Code/GraphMol/DistGeomHelpers/Embedder.cpp
@@ -1,5 +1,5 @@
 //
-//  Copyright (C) 2004-2019 Greg Landrum and Rational Discovery LLC
+//  Copyright (C) 2004-2021 Greg Landrum and other RDKit contributors
 //
 //   @@ All Rights Reserved @@
 //  This file is part of the RDKit.


Test patch:
diff --git a/Code/GraphMol/DistGeomHelpers/catch_tests.cpp b/Code/GraphMol/DistGeomHelpers/catch_tests.cpp
index 64c98fe38e6..8f8ecfd1087 100644
--- a/Code/GraphMol/DistGeomHelpers/catch_tests.cpp
+++ b/Code/GraphMol/DistGeomHelpers/catch_tests.cpp
@@ -1,5 +1,5 @@
 //
-//  Copyright (C) 2021 Greg Landrum
+//  Copyright (C) 2021 Greg Landrum and other RDKit contributors
 //   @@ All Rights Reserved @@
 //  This file is part of the RDKit.
 //  The contents are covered by the terms of the BSD license
@@ -172,4 +172,35 @@ TEST_CASE("update parameters from JSON") {
       CHECK(v1.angleTo(v2) == Approx(M_PI / 2).margin(0.15));
     }
   }
+}
+
+TEST_CASE(
+    "github #4346: Specified cis/trans stereo being ignored during "
+    "conformation generation in macrocycles") {
+  SECTION("basics 1") {
+    auto m1 = "C1C/C=C/CCCCCCCC1"_smiles;
+    REQUIRE(m1);
+    CHECK(m1->getBondBetweenAtoms(2, 3)->getStereo() ==
+          Bond::BondStereo::STEREOE);
+    MolOps::addHs(*m1);
+    DGeomHelpers::EmbedParameters params = DGeomHelpers::KDG;
+    params.randomSeed = 0xf00d;
+    CHECK(DGeomHelpers::EmbedMolecule(*m1, params) != -1);
+    MolOps::assignStereochemistryFrom3D(*m1);
+    CHECK(m1->getBondBetweenAtoms(2, 3)->getStereo() ==
+          Bond::BondStereo::STEREOE);
+  }
+  SECTION("basics 2") {
+    auto m1 = "C1C/C=C\\CCCCCCCC1"_smiles;
+    REQUIRE(m1);
+    CHECK(m1->getBondBetweenAtoms(2, 3)->getStereo() ==
+          Bond::BondStereo::STEREOZ);
+    MolOps::addHs(*m1);
+    DGeomHelpers::EmbedParameters params = DGeomHelpers::KDG;
+    params.randomSeed = 0xf00d;
+    CHECK(DGeomHelpers::EmbedMolecule(*m1, params) != -1);
+    MolOps::assignStereochemistryFrom3D(*m1);
+    CHECK(m1->getBondBetweenAtoms(2, 3)->getStereo() ==
+          Bond::BondStereo::STEREOZ);
+  }
 }
\ No newline at end of file