File: ConjugHybrid.cpp

package info (click to toggle)
rdkit 202503.1-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 220,160 kB
  • sloc: cpp: 399,240; python: 77,453; ansic: 25,517; java: 8,173; javascript: 4,005; sql: 2,389; yacc: 1,565; lex: 1,263; cs: 1,081; makefile: 580; xml: 229; fortran: 183; sh: 105
file content (232 lines) | stat: -rw-r--r-- 7,339 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
//
//  Copyright (C) 2001-2024 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
//  which is included in the file license.txt, found at the root
//  of the RDKit source tree.
//
#include "ROMol.h"
#include "RWMol.h"
#include "Atom.h"
#include "Bond.h"
#include "MolOps.h"
#include "PeriodicTable.h"
#include "AtomIterators.h"
#include "BondIterators.h"

namespace RDKit {
// local utility namespace:
namespace {
bool isAtomConjugCand(const Atom *at) {
  PRECONDITION(at, "bad atom");
  // return false for neutral atoms where the current valence exceeds the
  // minimal valence for the atom. logic: if we're hypervalent we aren't
  // conjugated
  const auto &vals =
      PeriodicTable::getTable()->getValenceList(at->getAtomicNum());
  if (!at->getFormalCharge() && vals.front() >= 0 &&
      at->getTotalValence() > static_cast<unsigned int>(vals.front())) {
    return false;
  }
  // the second check here is for Issue211, where the c-P bonds in
  // Pc1ccccc1 were being marked as conjugated.  This caused the P atom
  // itself to be SP2 hybridized.  This is wrong.  For now we'll do a quick
  // hack and forbid this check from adding conjugation to anything out of
  // the first row of the periodic table.  (Conjugation in aromatic rings
  // has already been attended to, so this is safe.)
  int nouter = PeriodicTable::getTable()->getNouterElecs(at->getAtomicNum());
  auto res = ((at->getAtomicNum() <= 10) || (nouter != 5 && nouter != 6) ||
              (nouter == 6 && at->getTotalDegree() < 2u)) &&
             MolOps::countAtomElec(at) > 0;
  return res;
}

void markConjAtomBonds(Atom *at) {
  PRECONDITION(at, "bad atom");
  if (!isAtomConjugCand(at)) {
    return;
  }
  auto &mol = at->getOwningMol();

  int atx = at->getIdx();
  // make sure that have either 2 or 3 substitutions on this atom
  int sbo = at->getDegree() + at->getTotalNumHs();
  if ((sbo < 2) || (sbo > 3)) {
    return;
  }

  for (const auto bnd1 : mol.atomBonds(at)) {
    if (bnd1->getValenceContrib(at) < 1.5 ||
        !isAtomConjugCand(bnd1->getOtherAtom(at))) {
      continue;
    }
    for (const auto bnd2 : mol.atomBonds(at)) {
      if (bnd1 == bnd2) {
        continue;
      }
      auto at2 = mol.getAtomWithIdx(bnd2->getOtherAtomIdx(atx));
      sbo = at2->getDegree() + at2->getTotalNumHs();
      if (sbo > 3) {
        continue;
      }
      if (isAtomConjugCand(at2)) {
        bnd1->setIsConjugated(true);
        bnd2->setIsConjugated(true);
      }
    }
  }
}

int numBondsPlusLonePairs(Atom *at) {
  PRECONDITION(at, "bad atom");
  int deg = at->getTotalDegree();

  auto &mol = at->getOwningMol();
  for (const auto bond : mol.atomBonds(at)) {
    if (bond->getBondType() == Bond::ZERO ||
        (isDative(*bond) && at->getIdx() != bond->getEndAtomIdx())) {
      --deg;
    }
  }

  if (at->getAtomicNum() <= 1) {
    return deg;
  }
  int nouter = PeriodicTable::getTable()->getNouterElecs(at->getAtomicNum());
  int totalValence = at->getTotalValence();
  int chg = at->getFormalCharge();

  int numFreeElectrons = nouter - (totalValence + chg);
  if (totalValence + nouter - chg < 8) {
    // we're below an octet, so we need to think
    // about radicals:
    int numRadicals = at->getNumRadicalElectrons();
    int numLonePairs = (numFreeElectrons - numRadicals) / 2;
    return deg + numLonePairs + numRadicals;
  } else {
    int numLonePairs = numFreeElectrons / 2;
    return deg + numLonePairs;
  }
}
}  // namespace

namespace MolOps {
bool atomHasConjugatedBond(const Atom *at) {
  PRECONDITION(at, "bad atom");

  auto &mol = at->getOwningMol();
  for (const auto bnd : mol.atomBonds(at)) {
    if (bnd->getIsConjugated()) {
      return true;
    }
  }
  return false;
}

void setConjugation(ROMol &mol) {
  // start with all bonds being marked unconjugated
  // except for aromatic bonds
  for (auto bond : mol.bonds()) {
    bond->setIsConjugated(bond->getIsAromatic());
  }

  // loop over each atom and check if the bonds connecting to it can
  // be conjugated
  for (auto atom : mol.atoms()) {
    markConjAtomBonds(atom);
  }
}

void setHybridization(ROMol &mol) {
  for (auto atom : mol.atoms()) {
    if (atom->getAtomicNum() == 0) {
      atom->setHybridization(Atom::UNSPECIFIED);
    } else {
      // if the stereo spec matches the coordination number, this is easy
      switch (atom->getChiralTag()) {
        case Atom::ChiralType::CHI_TETRAHEDRAL:
        case Atom::ChiralType::CHI_TETRAHEDRAL_CW:
        case Atom::ChiralType::CHI_TETRAHEDRAL_CCW:
          if (atom->getTotalDegree() == 4) {
            atom->setHybridization(Atom::HybridizationType::SP3);
            continue;
          }
          break;
        case Atom::ChiralType::CHI_SQUAREPLANAR:
          if (atom->getTotalDegree() <= 4 && atom->getTotalDegree() >= 2) {
            atom->setHybridization(Atom::HybridizationType::SP2D);
            continue;
          }
          break;
        case Atom::ChiralType::CHI_TRIGONALBIPYRAMIDAL:
          if (atom->getTotalDegree() <= 5 && atom->getTotalDegree() >= 2) {
            atom->setHybridization(Atom::HybridizationType::SP3D);
            continue;
          }
          break;
        case Atom::ChiralType::CHI_OCTAHEDRAL:
          if (atom->getTotalDegree() <= 6 && atom->getTotalDegree() >= 2) {
            atom->setHybridization(Atom::HybridizationType::SP3D2);
            continue;
          }
          break;
        default:
          break;
      }
      // otherwise we have to do some work
      int norbs;
      // try to be smart for early elements, but for later
      // ones just use the degree
      // FIX: we should probably also be using the degree for metals
      if (atom->getAtomicNum() < 89) {
        norbs = numBondsPlusLonePairs(atom);
      } else {
        norbs = atom->getTotalDegree();
      }
      switch (norbs) {
        case 0:
          // This occurs for things like Na+
          atom->setHybridization(Atom::S);
          break;
        case 1:
          atom->setHybridization(Atom::S);
          break;
        case 2:
          atom->setHybridization(Atom::SP);
          break;
        case 3:
          atom->setHybridization(Atom::SP2);
          break;
        case 4:
          // potentially SP3, but we'll set it down to SP2
          // if we have a conjugated bond (like the second O
          // in O=CO)
          // we'll also avoid setting the hybridization down to
          // SP2 in the case of an atom with degree higher than 3
          // (e.g. things like CP1(C)=CC=CN=C1C, where the P
          //   has norbs = 4, and a conjugated bond, but clearly should
          //   not be SP2)
          // This is Issue276
          if (atom->getTotalDegree() > 3 ||
              !MolOps::atomHasConjugatedBond(atom)) {
            atom->setHybridization(Atom::SP3);
          } else {
            atom->setHybridization(Atom::SP2);
          }
          break;
        case 5:
          atom->setHybridization(Atom::SP3D);
          break;
        case 6:
          atom->setHybridization(Atom::SP3D2);
          break;
        default:
          atom->setHybridization(Atom::UNSPECIFIED);
      }
    }
  }
}
}  // end of namespace MolOps
}  // end of namespace RDKit