1 //
2 //
3 //  Copyright (C) 2020 Schrödinger, LLC
4 //
5 //   @@ All Rights Reserved @@
6 //  This file is part of the RDKit.
7 //  The contents are covered by the terms of the BSD license
8 //  which is included in the file license.txt, found at the root
9 //  of the RDKit source tree.
10 //
11 #include <GraphMol/Chirality.h>
12 
13 #include "Sp2Bond.h"
14 #include "../Sort.h"
15 #include "../rules/Rules.h"
16 
17 namespace RDKit {
18 namespace CIPLabeler {
19 
Sp2Bond(const CIPMol & mol,Bond * bond,Atom * startAtom,Atom * endAtom,Bond::BondStereo cfg)20 Sp2Bond::Sp2Bond(const CIPMol &mol, Bond *bond, Atom *startAtom, Atom *endAtom,
21                  Bond::BondStereo cfg)
22     : Configuration(mol, {startAtom, endAtom}), dp_bond{bond}, d_cfg{cfg} {
23   CHECK_INVARIANT(startAtom && endAtom, "bad foci")
24   CHECK_INVARIANT(d_cfg == Bond::STEREOTRANS || d_cfg == Bond::STEREOCIS,
25                   "bad config")
26 
27   auto stereo_atoms = Chirality::findStereoAtoms(bond);
28   CHECK_INVARIANT(stereo_atoms.size() == 2, "incorrect number of stereo atoms")
29 
30   std::vector<Atom *> anchors{
31       {mol.getAtom(stereo_atoms[0]), mol.getAtom(stereo_atoms[1])}};
32 
33   setCarriers(std::move(anchors));
34 }
35 
setPrimaryLabel(Descriptor desc)36 void Sp2Bond::setPrimaryLabel(Descriptor desc) {
37   switch (desc) {
38   case Descriptor::seqTrans:
39   case Descriptor::E:
40   case Descriptor::seqCis:
41   case Descriptor::Z: {
42     auto carriers = getCarriers();
43     dp_bond->setStereoAtoms(carriers[0]->getIdx(), carriers[1]->getIdx());
44     dp_bond->setStereo(d_cfg);
45     dp_bond->setProp(common_properties::_CIPCode, to_string(desc));
46     return;
47   }
48   case Descriptor::R:
49   case Descriptor::S:
50   case Descriptor::r:
51   case Descriptor::s:
52   case Descriptor::SP_4:
53   case Descriptor::TBPY_5:
54   case Descriptor::OC_6:
55     throw std::runtime_error(
56         "Received a Descriptor that is not supported for bonds");
57   default:
58     throw std::runtime_error("Received an invalid Bond Descriptor");
59   }
60 }
61 
label(const Rules & comp)62 Descriptor Sp2Bond::label(const Rules &comp) {
63   auto &digraph = getDigraph();
64   auto root1 = digraph.getOriginalRoot();
65   if (digraph.getCurrentRoot() != root1) {
66     digraph.changeRoot(root1);
67   }
68 
69   return label(root1, digraph, comp);
70 }
71 
label(Node * root1,Digraph & digraph,const Rules & comp)72 Descriptor Sp2Bond::label(Node *root1, Digraph &digraph, const Rules &comp) {
73   const auto &focus1 = getFoci()[0];
74   const auto &focus2 = getFoci()[1];
75 
76   const auto &internal = findInternalEdge(root1->getEdges(), focus1, focus2);
77   if (internal == nullptr) {
78     return Descriptor::UNKNOWN;
79   }
80   const auto &root2 = internal->getOther(root1);
81 
82   auto edges1 = root1->getEdges();
83   auto edges2 = root2->getEdges();
84   removeInternalEdges(edges1, focus1, focus2);
85   removeInternalEdges(edges2, focus1, focus2);
86 
87   auto carriers = std::vector<Atom *>(getCarriers());
88   auto config = d_cfg;
89 
90   if (root1->getAtom() == focus2) {
91     std::swap(carriers[0], carriers[1]);
92   }
93 
94   digraph.changeRoot(root1);
95   const auto &priority1 = comp.sort(root1, edges1);
96   if (!priority1.isUnique()) {
97     return Descriptor::UNKNOWN;
98   }
99   // swap
100   if (edges1.size() > 1 && carriers[0] == edges1[1]->getEnd()->getAtom()) {
101     if (config == Bond::STEREOCIS) {
102       config = Bond::STEREOTRANS;
103     } else {
104       config = Bond::STEREOCIS;
105     }
106   }
107   digraph.changeRoot(root2);
108   const auto &priority2 = comp.sort(root2, edges2);
109   if (!priority2.isUnique()) {
110     return Descriptor::UNKNOWN;
111   }
112   // swap
113   if (edges2.size() > 1 && carriers[1] == edges2[1]->getEnd()->getAtom()) {
114     if (config == Bond::STEREOCIS) {
115       config = Bond::STEREOTRANS;
116     } else {
117       config = Bond::STEREOCIS;
118     }
119   }
120 
121   if (config == Bond::STEREOCIS) {
122     if (priority1.isPseudoAsymetric() != priority2.isPseudoAsymetric()) {
123       return Descriptor::seqCis;
124     } else {
125       return Descriptor::Z;
126     }
127   } else if (config == Bond::STEREOTRANS) {
128     if (priority1.isPseudoAsymetric() != priority2.isPseudoAsymetric()) {
129       return Descriptor::seqTrans;
130     } else {
131       return Descriptor::E;
132     }
133   }
134   return Descriptor::UNKNOWN;
135 }
136 
137 } // namespace CIPLabeler
138 } // namespace RDKit