/src/rdkit/Code/GraphMol/NontetrahedralStereo.cpp
Line | Count | Source |
1 | | // |
2 | | // Copyright (C) 2022 Greg Landrum and other RDKit contributors |
3 | | // |
4 | | // @@ All Rights Reserved @@ |
5 | | // This file is part of the RDKit. |
6 | | // The contents are covered by the terms of the BSD license |
7 | | // which is included in the file license.txt, found at the root |
8 | | // of the RDKit source tree. |
9 | | // |
10 | | #include <GraphMol/RDKitBase.h> |
11 | | #include <RDGeneral/types.h> |
12 | | #include <RDGeneral/Invariant.h> |
13 | | #include <RDGeneral/RDLog.h> |
14 | | |
15 | | #include "Chirality.h" |
16 | | |
17 | | namespace RDKit { |
18 | | |
19 | | namespace Chirality { |
20 | | |
21 | | constexpr unsigned char swap_squareplanar_table[4][6] = { |
22 | | // 0 0 0 1 1 2 |
23 | | // 1 2 3 2 3 3 |
24 | | {0, 0, 0, 0, 0, 0}, |
25 | | {3, 1, 2, 2, 1, 3}, // SP1 |
26 | | {2, 3, 1, 1, 3, 2}, // SP2 |
27 | | {1, 2, 3, 3, 2, 1} // SP3 |
28 | | }; |
29 | | |
30 | | constexpr unsigned char swap_trigonalbipyramidal_table[21][10] = { |
31 | | // 0 0 0 0 1 1 1 2 2 3 |
32 | | // 1 2 3 4 2 3 4 3 4 4 |
33 | | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, |
34 | | {9, 20, 17, 2, 2, 2, 7, 2, 6, 3}, // TB1 |
35 | | {11, 15, 18, 1, 1, 1, 8, 1, 5, 4}, // TB2 |
36 | | {10, 19, 4, 18, 4, 8, 4, 5, 4, 1}, // TB3 |
37 | | {12, 16, 3, 17, 3, 7, 3, 6, 3, 2}, // TB4 |
38 | | {13, 6, 16, 20, 7, 6, 6, 3, 2, 6}, // TB5 |
39 | | {14, 5, 19, 15, 8, 5, 5, 4, 1, 5}, // TB6 |
40 | | {8, 14, 10, 11, 5, 4, 1, 8, 8, 8}, // TB7 |
41 | | {7, 13, 12, 9, 6, 3, 2, 7, 7, 7}, // TB8 |
42 | | {1, 11, 11, 8, 15, 18, 11, 11, 14, 10}, // TB9 |
43 | | {3, 12, 7, 12, 16, 12, 17, 13, 12, 9}, // TB10 |
44 | | {2, 9, 9, 7, 20, 17, 9, 9, 13, 12}, // TB11 |
45 | | {4, 10, 8, 10, 19, 10, 18, 14, 10, 11}, // TB12 |
46 | | {5, 8, 14, 14, 14, 19, 15, 10, 11, 14}, // TB13 |
47 | | {6, 7, 13, 13, 13, 16, 20, 12, 9, 13}, // TB14 |
48 | | {20, 2, 20, 6, 9, 20, 13, 17, 20, 16}, // TB15 |
49 | | {19, 4, 5, 19, 10, 14, 19, 19, 18, 15}, // TB16 |
50 | | {18, 18, 1, 4, 18, 11, 10, 15, 19, 18}, // TB17 |
51 | | {17, 17, 2, 3, 17, 9, 12, 20, 16, 17}, // TB18 |
52 | | {16, 3, 6, 16, 12, 13, 16, 16, 17, 20}, // TB19 |
53 | | {15, 1, 15, 5, 11, 15, 14, 18, 15, 19} // TB20 |
54 | | }; |
55 | | |
56 | | constexpr unsigned char swap_octahedral_table[31][15] = { |
57 | | // 0 0 0 0 0 1 1 1 1 2 2 2 3 3 4 |
58 | | // 1 2 3 4 5 2 3 4 5 3 4 5 4 5 5 |
59 | | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, |
60 | | {17, 16, 30, 21, 2, 14, 2, 10, 25, 8, 2, 22, 4, 7, 3}, // OH1 |
61 | | {7, 3, 25, 22, 1, 4, 1, 8, 30, 10, 1, 21, 14, 17, 16}, // OH2 |
62 | | {18, 2, 29, 16, 22, 15, 16, 26, 11, 9, 21, 16, 6, 5, 1}, // OH3 |
63 | | {15, 18, 19, 28, 14, 2, 8, 14, 27, 14, 10, 24, 1, 6, 5}, // OH4 |
64 | | {14, 17, 20, 15, 27, 16, 9, 28, 15, 15, 23, 11, 7, 3, 4}, // OH5 |
65 | | {16, 14, 18, 26, 24, 17, 29, 18, 13, 19, 12, 18, 3, 4, 7}, // OH6 |
66 | | {2, 15, 17, 23, 25, 18, 30, 12, 17, 20, 17, 13, 5, 1, 6}, // OH7 |
67 | | {23, 26, 11, 12, 10, 10, 4, 2, 29, 1, 14, 20, 10, 13, 9}, // OH8 |
68 | | {24, 25, 10, 11, 13, 11, 5, 30, 16, 3, 19, 15, 12, 11, 8}, // OH9 |
69 | | {20, 29, 9, 13, 8, 8, 14, 1, 26, 2, 4, 23, 8, 12, 11}, // OH10 |
70 | | {19, 30, 8, 9, 12, 9, 15, 25, 3, 16, 24, 5, 13, 9, 10}, // OH11 |
71 | | {22, 27, 13, 8, 11, 13, 28, 7, 18, 21, 6, 17, 9, 10, 13}, // OH12 |
72 | | {21, 28, 12, 10, 9, 12, 27, 17, 6, 22, 18, 7, 11, 8, 12}, // OH13 |
73 | | {5, 6, 24, 27, 4, 1, 10, 4, 28, 4, 8, 19, 2, 18, 15}, // OH14 |
74 | | {4, 7, 23, 5, 28, 3, 11, 27, 5, 5, 20, 9, 17, 16, 14}, // OH15 |
75 | | {6, 1, 26, 3, 21, 5, 3, 29, 9, 11, 22, 3, 18, 15, 2}, // OH16 |
76 | | {1, 5, 7, 20, 30, 6, 25, 13, 7, 23, 7, 12, 15, 2, 18}, // OH17 |
77 | | {3, 4, 6, 29, 19, 7, 26, 6, 12, 24, 13, 6, 16, 14, 17}, // OH18 |
78 | | {11, 24, 4, 30, 18, 25, 23, 24, 22, 6, 9, 14, 21, 24, 20}, // OH19 |
79 | | {10, 23, 5, 17, 29, 26, 24, 21, 23, 7, 15, 8, 23, 22, 19}, // OH20 |
80 | | {13, 22, 28, 1, 16, 27, 22, 20, 24, 12, 3, 2, 19, 23, 22}, // OH21 |
81 | | {12, 21, 27, 2, 3, 28, 21, 23, 19, 13, 16, 1, 24, 20, 21}, // OH22 |
82 | | {8, 20, 15, 7, 26, 29, 19, 22, 20, 17, 5, 10, 20, 21, 24}, // OH23 |
83 | | {9, 19, 14, 25, 6, 30, 20, 19, 21, 18, 11, 4, 22, 19, 23}, // OH24 |
84 | | {30, 9, 2, 24, 7, 19, 17, 11, 1, 29, 30, 28, 27, 30, 26}, // OH25 |
85 | | {29, 8, 16, 6, 23, 20, 18, 3, 10, 30, 27, 29, 29, 28, 25}, // OH26 |
86 | | {28, 12, 22, 14, 5, 21, 13, 15, 4, 28, 26, 30, 25, 29, 28}, // OH27 |
87 | | {27, 13, 21, 4, 15, 22, 12, 5, 14, 27, 29, 25, 30, 26, 27}, // OH28 |
88 | | {26, 10, 3, 18, 20, 23, 6, 16, 8, 25, 28, 26, 26, 27, 30}, // OH29 |
89 | | {25, 11, 1, 19, 17, 24, 7, 9, 2, 26, 25, 27, 28, 25, 29} // OH30 |
90 | | }; |
91 | | |
92 | | static unsigned int swap_squareplanar(unsigned int perm, unsigned int x, |
93 | 1.89k | unsigned int y) { |
94 | 1.89k | constexpr unsigned int offset[3] = {0, 2, 3}; |
95 | 1.89k | unsigned int swapidx; |
96 | 1.89k | if (x == y) { |
97 | 0 | return perm; |
98 | 0 | } |
99 | 1.89k | if (x < y) { |
100 | 1.89k | if (y > 3) { |
101 | 0 | return 0; |
102 | 0 | } |
103 | 1.89k | swapidx = offset[x] + (y - 1); |
104 | 1.89k | } else /* x > y */ { |
105 | 0 | if (x > 3) { |
106 | 0 | return 0; |
107 | 0 | } |
108 | 0 | swapidx = offset[y] + (x - 1); |
109 | 0 | } |
110 | 1.89k | return perm < 4 ? swap_squareplanar_table[perm][swapidx] : 0; |
111 | 1.89k | } |
112 | | |
113 | | static unsigned int swap_trigonalbipyramidal(unsigned int perm, unsigned int x, |
114 | 929 | unsigned int y) { |
115 | 929 | constexpr unsigned int offset[4] = {0, 3, 5, 6}; |
116 | 929 | unsigned int swapidx; |
117 | 929 | if (x == y) { |
118 | 0 | return perm; |
119 | 0 | } |
120 | 929 | if (x < y) { |
121 | 929 | if (y > 4) { |
122 | 0 | return 0; |
123 | 0 | } |
124 | 929 | swapidx = offset[x] + (y - 1); |
125 | 929 | } else /* x > y */ { |
126 | 0 | if (x > 4) { |
127 | 0 | return 0; |
128 | 0 | } |
129 | 0 | swapidx = offset[y] + (x - 1); |
130 | 0 | } |
131 | 929 | return perm < 21 ? swap_trigonalbipyramidal_table[perm][swapidx] : 0; |
132 | 929 | } |
133 | | |
134 | | static unsigned int swap_octahedral(unsigned int perm, unsigned int x, |
135 | 726 | unsigned int y) { |
136 | 726 | constexpr unsigned int offset[5] = {0, 4, 7, 9, 10}; |
137 | 726 | unsigned int swapidx; |
138 | 726 | if (x == y) { |
139 | 0 | return perm; |
140 | 0 | } |
141 | 726 | if (x < y) { |
142 | 726 | if (y > 5) { |
143 | 0 | return 0; |
144 | 0 | } |
145 | 726 | swapidx = offset[x] + (y - 1); |
146 | 726 | } else /* x > y */ { |
147 | 0 | if (x > 5) { |
148 | 0 | return 0; |
149 | 0 | } |
150 | 0 | swapidx = offset[y] + (x - 1); |
151 | 0 | } |
152 | 726 | return perm < 31 ? swap_octahedral_table[perm][swapidx] : 0; |
153 | 726 | } |
154 | | |
155 | | constexpr unsigned char squareplanar_across[4][4] = { |
156 | | {4, 4, 4, 4}, // |
157 | | {2, 3, 0, 1}, // SP1 |
158 | | {1, 0, 3, 2}, // SP2 |
159 | | {3, 2, 1, 0} // SP3 |
160 | | }; |
161 | | |
162 | | constexpr unsigned char trigonalbipyramidal_across[21][5] = { |
163 | | {5, 5, 5, 5, 5}, // |
164 | | {4, 5, 5, 5, 0}, // TB1 |
165 | | {4, 5, 5, 5, 0}, // TB2 |
166 | | {3, 5, 5, 0, 5}, // TB3 |
167 | | {3, 5, 5, 0, 5}, // TB4 |
168 | | {2, 5, 0, 5, 5}, // TB5 |
169 | | {2, 5, 0, 5, 5}, // TB6 |
170 | | {1, 0, 5, 5, 5}, // TB7 |
171 | | {1, 0, 5, 5, 5}, // TB8 |
172 | | {5, 4, 5, 5, 1}, // TB9 |
173 | | {5, 3, 5, 1, 5}, // TB10 |
174 | | {5, 4, 5, 5, 1}, // TB11 |
175 | | {5, 3, 5, 1, 5}, // TB12 |
176 | | {5, 2, 1, 5, 5}, // TB13 |
177 | | {5, 2, 1, 5, 5}, // TB14 |
178 | | {5, 5, 4, 5, 2}, // TB15 |
179 | | {5, 5, 3, 2, 5}, // TB16 |
180 | | {5, 5, 5, 4, 3}, // TB17 |
181 | | {5, 5, 5, 4, 3}, // TB18 |
182 | | {5, 5, 3, 2, 5}, // TB19 |
183 | | {5, 5, 4, 5, 2} // TB20 |
184 | | }; |
185 | | |
186 | | constexpr unsigned char octahedral_across[31][6] = { |
187 | | {6, 6, 6, 6, 6, 6}, // |
188 | | {5, 3, 4, 1, 2, 0}, // OH1 |
189 | | {5, 3, 4, 1, 2, 0}, // OH2 |
190 | | {4, 3, 5, 1, 0, 2}, // OH3 |
191 | | {5, 4, 3, 2, 1, 0}, // OH4 |
192 | | {4, 5, 3, 2, 0, 1}, // OH5 |
193 | | {3, 4, 5, 0, 1, 2}, // OH6 |
194 | | {3, 5, 4, 0, 2, 1}, // OH7 |
195 | | {5, 2, 1, 4, 3, 0}, // OH8 |
196 | | {4, 2, 1, 5, 0, 3}, // OH9 |
197 | | {5, 2, 1, 4, 3, 0}, // OH10 |
198 | | {4, 2, 1, 5, 0, 3}, // OH11 |
199 | | {3, 2, 1, 0, 5, 4}, // OH12 |
200 | | {3, 2, 1, 0, 5, 4}, // OH13 |
201 | | {5, 4, 3, 2, 1, 0}, // OH14 |
202 | | {4, 5, 3, 2, 0, 1}, // OH15 |
203 | | {4, 3, 5, 1, 0, 2}, // OH16 |
204 | | {3, 5, 4, 0, 2, 1}, // OH17 |
205 | | {3, 4, 5, 0, 1, 2}, // OH18 |
206 | | {2, 4, 0, 5, 1, 3}, // OH19 |
207 | | {2, 5, 0, 4, 3, 1}, // OH20 |
208 | | {2, 3, 0, 1, 5, 4}, // OH21 |
209 | | {2, 3, 0, 1, 5, 4}, // OH22 |
210 | | {2, 5, 0, 4, 3, 1}, // OH23 |
211 | | {2, 4, 0, 5, 1, 3}, // OH24 |
212 | | {1, 0, 4, 5, 2, 3}, // OH25 |
213 | | {1, 0, 5, 4, 3, 2}, // OH26 |
214 | | {1, 0, 3, 2, 5, 4}, // OH27 |
215 | | {1, 0, 3, 2, 5, 4}, // OH28 |
216 | | {1, 0, 5, 4, 3, 2}, // OH29 |
217 | | {1, 0, 4, 5, 2, 3} // OH30 |
218 | | }; |
219 | | |
220 | | constexpr unsigned char trigonalbipyramidal_axial[21][2] = { |
221 | | {5, 5}, // |
222 | | {0, 4}, // TB1 |
223 | | {0, 4}, // TB2 |
224 | | {0, 3}, // TB3 |
225 | | {0, 3}, // TB4 |
226 | | {0, 2}, // TB5 |
227 | | {0, 2}, // TB6 |
228 | | {0, 1}, // TB7 |
229 | | {0, 1}, // TB8 |
230 | | {1, 4}, // TB9 |
231 | | {1, 4}, // TB10 |
232 | | {1, 3}, // TB11 |
233 | | {1, 3}, // TB12 |
234 | | {1, 2}, // TB13 |
235 | | {1, 2}, // TB14 |
236 | | {2, 4}, // TB15 |
237 | | {2, 3}, // TB16 |
238 | | {3, 4}, // TB17 |
239 | | {3, 4}, // TB18 |
240 | | {2, 3}, // TB19 |
241 | | {2, 4} // TB20 |
242 | | }; |
243 | | |
244 | 0 | int isTrigonalBipyramidalAxialBond(const Atom *cen, const Bond *qry) { |
245 | 0 | PRECONDITION(cen, "bad center pointer"); |
246 | 0 | PRECONDITION(qry, "bad query pointer"); |
247 | 0 | PRECONDITION(cen->hasOwningMol() && qry->hasOwningMol(), "no owning mol"); |
248 | 0 | PRECONDITION(&cen->getOwningMol() == &qry->getOwningMol(), |
249 | 0 | "center and query must come from the same molecule"); |
250 | 0 | if (cen->getDegree() > 5 || |
251 | 0 | cen->getChiralTag() != Atom::CHI_TRIGONALBIPYRAMIDAL) { |
252 | 0 | return false; |
253 | 0 | } |
254 | 0 | unsigned int perm = 0; |
255 | 0 | cen->getPropIfPresent(RDKit::common_properties::_chiralPermutation, perm); |
256 | 0 | if (perm == 0 || perm > 20) { |
257 | 0 | return 0; |
258 | 0 | } |
259 | | |
260 | 0 | unsigned int count = 0; |
261 | 0 | for (const auto bnd : cen->getOwningMol().atomBonds(cen)) { |
262 | 0 | if (bnd == qry) { |
263 | 0 | if (count == trigonalbipyramidal_axial[perm][0]) { |
264 | 0 | return 1; |
265 | 0 | } |
266 | 0 | if (count == trigonalbipyramidal_axial[perm][1]) { |
267 | 0 | return -1; |
268 | 0 | } |
269 | 0 | return 0; |
270 | 0 | } |
271 | 0 | count++; |
272 | 0 | } |
273 | 0 | return 0; |
274 | 0 | } |
275 | | |
276 | 0 | int isTrigonalBipyramidalAxialAtom(const Atom *cen, const Atom *qry) { |
277 | 0 | PRECONDITION(cen, "bad center pointer"); |
278 | 0 | PRECONDITION(qry, "bad query pointer"); |
279 | 0 | PRECONDITION(cen->hasOwningMol(), "no owning mol"); |
280 | 0 | auto bnd = |
281 | 0 | cen->getOwningMol().getBondBetweenAtoms(cen->getIdx(), qry->getIdx()); |
282 | 0 | if (!bnd) { |
283 | 0 | return 0; |
284 | 0 | } |
285 | 0 | return isTrigonalBipyramidalAxialBond(cen, bnd); |
286 | 0 | } |
287 | | |
288 | 5.18k | unsigned int getMaxNbors(const Atom::ChiralType tag) { |
289 | 5.18k | switch (tag) { |
290 | 0 | case Atom::CHI_TETRAHEDRAL_CW: |
291 | 0 | case Atom::CHI_TETRAHEDRAL_CCW: |
292 | 0 | case Atom::CHI_TETRAHEDRAL: // fall through |
293 | 0 | return 4; |
294 | 0 | case Atom::CHI_ALLENE: |
295 | 0 | return 2; // not used other than SMI/SMA parsers? |
296 | 3.57k | case Atom::CHI_SQUAREPLANAR: |
297 | 3.57k | return 4; |
298 | 862 | case Atom::CHI_TRIGONALBIPYRAMIDAL: |
299 | 862 | return 5; |
300 | 747 | case Atom::CHI_OCTAHEDRAL: |
301 | 747 | return 6; |
302 | 0 | default: |
303 | 0 | BOOST_LOG(rdWarningLog) |
304 | 0 | << "Warning: unexpected chiral tag getMaxNbors(): " << tag |
305 | 0 | << std::endl; |
306 | 0 | return 0; |
307 | 5.18k | } |
308 | 5.18k | } |
309 | | |
310 | 0 | Bond *getChiralAcrossBond(const Atom *cen, const Bond *qry) { |
311 | 0 | PRECONDITION(cen, "bad center pointer"); |
312 | 0 | PRECONDITION(qry, "bad query pointer"); |
313 | 0 | PRECONDITION(cen->hasOwningMol() && qry->hasOwningMol(), "no owning mol"); |
314 | 0 | PRECONDITION(&cen->getOwningMol() == &qry->getOwningMol(), |
315 | 0 | "center and query must come from the same molecule"); |
316 | |
|
317 | 0 | Atom::ChiralType tag = cen->getChiralTag(); |
318 | 0 | unsigned int perm = 0; |
319 | 0 | cen->getPropIfPresent(common_properties::_chiralPermutation, perm); |
320 | 0 | if (!perm) { |
321 | 0 | return nullptr; |
322 | 0 | } |
323 | | |
324 | 0 | auto &mol = cen->getOwningMol(); |
325 | 0 | unsigned int count = 0; |
326 | 0 | Bond *ref[6]; |
327 | 0 | int found = -1; |
328 | |
|
329 | 0 | unsigned int ref_max = getMaxNbors(tag); |
330 | 0 | for (auto bnd : mol.atomBonds(cen)) { |
331 | 0 | if (count == ref_max) { |
332 | 0 | return nullptr; |
333 | 0 | } |
334 | 0 | ref[count] = bnd; |
335 | 0 | if (bnd == qry) { |
336 | 0 | found = count; |
337 | 0 | } |
338 | 0 | count++; |
339 | 0 | } |
340 | | |
341 | 0 | if (found >= 0) { |
342 | 0 | switch (tag) { |
343 | 0 | case Atom::ChiralType::CHI_SQUAREPLANAR: |
344 | 0 | if (perm <= 3) { |
345 | 0 | found = squareplanar_across[perm][found]; |
346 | 0 | } else { |
347 | 0 | found = 4; |
348 | 0 | } |
349 | 0 | break; |
350 | 0 | case Atom::ChiralType::CHI_TRIGONALBIPYRAMIDAL: |
351 | 0 | if (perm <= 20) { |
352 | 0 | found = trigonalbipyramidal_across[perm][found]; |
353 | 0 | } else { |
354 | 0 | found = 5; |
355 | 0 | } |
356 | 0 | break; |
357 | 0 | case Atom::ChiralType::CHI_OCTAHEDRAL: |
358 | 0 | if (perm <= 30) { |
359 | 0 | found = octahedral_across[perm][found]; |
360 | 0 | } else { |
361 | 0 | found = 6; |
362 | 0 | } |
363 | 0 | break; |
364 | 0 | default: |
365 | 0 | return nullptr; |
366 | 0 | } |
367 | 0 | if (static_cast<unsigned int>(found) < count) { |
368 | 0 | return ref[found]; |
369 | 0 | } |
370 | 0 | } |
371 | 0 | return nullptr; |
372 | 0 | } |
373 | | |
374 | | /* Alternate wrappers */ |
375 | 0 | Bond *getChiralAcrossBond(const Atom *cen, const Atom *qry) { |
376 | 0 | PRECONDITION(cen, "bad center pointer"); |
377 | 0 | PRECONDITION(qry, "bad query pointer"); |
378 | 0 | PRECONDITION(cen->hasOwningMol(), "no owning mol"); |
379 | 0 | auto bnd = |
380 | 0 | cen->getOwningMol().getBondBetweenAtoms(cen->getIdx(), qry->getIdx()); |
381 | 0 | if (!bnd) { |
382 | 0 | return nullptr; |
383 | 0 | } |
384 | 0 | return getChiralAcrossBond(cen, bnd); |
385 | 0 | } |
386 | | |
387 | 0 | Atom *getChiralAcrossAtom(const Atom *cen, const Bond *qry) { |
388 | 0 | auto bnd = getChiralAcrossBond(cen, qry); |
389 | 0 | return bnd ? bnd->getOtherAtom(cen) : nullptr; |
390 | 0 | } |
391 | | |
392 | 0 | Atom *getChiralAcrossAtom(const Atom *cen, const Atom *qry) { |
393 | 0 | PRECONDITION(cen, "bad center pointer"); |
394 | 0 | PRECONDITION(qry, "bad query pointer"); |
395 | 0 | PRECONDITION(cen->hasOwningMol(), "no owning mol"); |
396 | 0 | auto bnd = |
397 | 0 | cen->getOwningMol().getBondBetweenAtoms(cen->getIdx(), qry->getIdx()); |
398 | 0 | if (!bnd) { |
399 | 0 | return nullptr; |
400 | 0 | } |
401 | 0 | bnd = getChiralAcrossBond(cen, bnd); |
402 | 0 | return bnd ? bnd->getOtherAtom(cen) : nullptr; |
403 | 0 | } |
404 | | |
405 | | double getIdealAngleBetweenLigands(const Atom *cen, const Atom *lig1, |
406 | 0 | const Atom *lig2) { |
407 | 0 | PRECONDITION(cen, "bad center pointer"); |
408 | 0 | PRECONDITION(lig1 && lig2, "bad ligand pointer"); |
409 | 0 | PRECONDITION( |
410 | 0 | cen->hasOwningMol() && lig1->hasOwningMol() && lig2->hasOwningMol(), |
411 | 0 | "no owning mol"); |
412 | 0 | PRECONDITION(&cen->getOwningMol() == &lig1->getOwningMol() && |
413 | 0 | &cen->getOwningMol() == &lig2->getOwningMol(), |
414 | 0 | "center and ligands must come from the same molecule"); |
415 | 0 | auto tag = cen->getChiralTag(); |
416 | 0 | switch (tag) { |
417 | 0 | case Atom::ChiralType::CHI_SQUAREPLANAR: |
418 | 0 | case Atom::ChiralType::CHI_OCTAHEDRAL: |
419 | 0 | return getChiralAcrossAtom(cen, lig1) == lig2 ? 180 : 90; |
420 | 0 | break; |
421 | 0 | case Atom::ChiralType::CHI_TRIGONALBIPYRAMIDAL: |
422 | 0 | if (getChiralAcrossAtom(cen, lig1) == lig2) { |
423 | | // both are axial |
424 | 0 | return 180; |
425 | 0 | } else if (isTrigonalBipyramidalAxialAtom(cen, lig1) || |
426 | 0 | isTrigonalBipyramidalAxialAtom(cen, lig2)) { |
427 | | // one is axial, the other equatorial |
428 | 0 | return 90; |
429 | 0 | } else { |
430 | | // both axial |
431 | 0 | return 120; |
432 | 0 | } |
433 | 0 | break; |
434 | 0 | default: |
435 | 0 | return 0; |
436 | 0 | } |
437 | 0 | } |
438 | | |
439 | 0 | Bond *getTrigonalBipyramidalAxialBond(const Atom *cen, int axial) { |
440 | 0 | PRECONDITION(cen, "bad center pointer"); |
441 | 0 | PRECONDITION(cen->hasOwningMol(), "no owning mol"); |
442 | 0 | if (cen->getChiralTag() != RDKit::Atom::ChiralType::CHI_TRIGONALBIPYRAMIDAL || |
443 | 0 | cen->getDegree() > 5) { |
444 | 0 | return nullptr; |
445 | 0 | } |
446 | | |
447 | 0 | unsigned int perm = 0; |
448 | 0 | cen->getPropIfPresent(RDKit::common_properties::_chiralPermutation, perm); |
449 | 0 | if (perm == 0 || perm > 20) { |
450 | 0 | return nullptr; |
451 | 0 | } |
452 | | |
453 | 0 | unsigned int idx = (axial != -1) ? trigonalbipyramidal_axial[perm][0] |
454 | 0 | : trigonalbipyramidal_axial[perm][1]; |
455 | |
|
456 | 0 | unsigned int count = 0; |
457 | 0 | for (const auto bnd : cen->getOwningMol().atomBonds(cen)) { |
458 | 0 | if (count == idx) { |
459 | 0 | return bnd; |
460 | 0 | } |
461 | 0 | count++; |
462 | 0 | } |
463 | 0 | return nullptr; |
464 | 0 | } |
465 | | |
466 | 0 | Atom *getTrigonalBipyramidalAxialAtom(const Atom *cen, int axial) { |
467 | 0 | PRECONDITION(cen, "bad center pointer"); |
468 | 0 | PRECONDITION(cen->hasOwningMol(), "no owning mol"); |
469 | 0 | auto bnd = getTrigonalBipyramidalAxialBond(cen, axial); |
470 | 0 | return bnd ? bnd->getOtherAtom(cen) : nullptr; |
471 | 0 | } |
472 | 10.1k | bool hasNonTetrahedralStereo(const Atom *cen) { |
473 | 10.1k | PRECONDITION(cen, "bad center pointer"); |
474 | 10.1k | if (!cen->hasOwningMol()) { |
475 | 0 | return false; |
476 | 0 | } |
477 | 10.1k | auto tag = cen->getChiralTag(); |
478 | 10.1k | return tag == Atom::ChiralType::CHI_SQUAREPLANAR || |
479 | 10.0k | tag == Atom::ChiralType::CHI_TRIGONALBIPYRAMIDAL || |
480 | 9.83k | tag == Atom::ChiralType::CHI_OCTAHEDRAL; |
481 | 10.1k | } |
482 | | |
483 | | unsigned int getChiralPermutation(const Atom *cen, const INT_LIST &probe, |
484 | 5.18k | bool inverse) { |
485 | 5.18k | PRECONDITION(cen, "bad center pointer"); |
486 | 5.18k | PRECONDITION(cen->hasOwningMol(), "no owning mol"); |
487 | | |
488 | 5.18k | int perm = -1; |
489 | 5.18k | if (!cen->getPropIfPresent(common_properties::_chiralPermutation, perm) || |
490 | 4.31k | perm <= 0) { |
491 | 1.06k | return 0; |
492 | 1.06k | } |
493 | | |
494 | 4.11k | decltype(&swap_octahedral) swap_func = nullptr; |
495 | 4.11k | switch (cen->getChiralTag()) { |
496 | 747 | case Atom::ChiralType::CHI_OCTAHEDRAL: |
497 | 747 | if (probe.size() > 6) { |
498 | 72 | return 0; |
499 | 72 | } |
500 | 675 | swap_func = swap_octahedral; |
501 | 675 | break; |
502 | 749 | case Atom::ChiralType::CHI_TRIGONALBIPYRAMIDAL: |
503 | 749 | if (probe.size() > 5) { |
504 | 348 | return 0; |
505 | 348 | } |
506 | 401 | swap_func = swap_trigonalbipyramidal; |
507 | 401 | break; |
508 | 2.62k | case Atom::ChiralType::CHI_SQUAREPLANAR: |
509 | 2.62k | if (probe.size() > 4) { |
510 | 617 | return 0; |
511 | 617 | } |
512 | 2.00k | swap_func = swap_squareplanar; |
513 | 2.00k | break; |
514 | 0 | default: |
515 | 0 | break; |
516 | 4.11k | } |
517 | 3.08k | if (!swap_func) { |
518 | 0 | return 0; |
519 | 0 | } |
520 | 3.08k | unsigned int nbrIdx = 0; |
521 | 3.08k | std::vector<int> order(cen->getOwningMol().getNumBonds(), -1); |
522 | 7.11k | for (const auto bnd : cen->getOwningMol().atomBonds(cen)) { |
523 | 7.11k | order[bnd->getIdx()] = nbrIdx++; |
524 | 7.11k | } |
525 | | |
526 | | // nbrPerm maps original index to array position |
527 | 3.08k | std::vector<unsigned int> nbrPerm(nbrIdx); |
528 | 3.08k | std::iota(nbrPerm.begin(), nbrPerm.end(), 0); |
529 | 3.08k | std::vector<unsigned int> probePerm(probe.size()); |
530 | 3.08k | nbrIdx = 0; |
531 | 14.0k | for (auto v : probe) { |
532 | 14.0k | probePerm[nbrIdx++] = v < 0 ? -1 : order[v]; |
533 | 14.0k | } |
534 | | |
535 | | // Missing (implicit) neighbors are at the end when in storage order |
536 | 3.08k | if (nbrPerm.size() < nbrIdx) { |
537 | 2.49k | nbrPerm.insert(nbrPerm.end(), nbrIdx - nbrPerm.size(), -1); |
538 | 2.49k | } |
539 | | |
540 | 3.08k | CHECK_INVARIANT(nbrPerm.size() == probePerm.size(), |
541 | 3.08k | "probe vector size does not match"); |
542 | | |
543 | 3.08k | if (inverse) { |
544 | 3.08k | std::swap(nbrPerm, probePerm); |
545 | 3.08k | } |
546 | | |
547 | 3.08k | boost::dynamic_bitset<> swapped(probe.size()); |
548 | 14.0k | for (unsigned int i = 0; i < probePerm.size() - 1; ++i) { |
549 | 10.9k | auto pval = probePerm[i]; |
550 | 10.9k | if (nbrPerm[i] == pval) { |
551 | 7.44k | continue; |
552 | 7.44k | } |
553 | 3.54k | auto tgt = std::find(nbrPerm.begin() + i, nbrPerm.end(), pval); |
554 | 3.54k | TEST_ASSERT(tgt != nbrPerm.end()); |
555 | 3.54k | perm = swap_func(perm, i, tgt - nbrPerm.begin()); |
556 | 3.54k | std::swap(*tgt, nbrPerm[i]); |
557 | 3.54k | } |
558 | | |
559 | 3.08k | return perm; |
560 | 3.08k | } |
561 | | |
562 | | } // namespace Chirality |
563 | | } // namespace RDKit |