/src/rdkit/Code/GraphMol/ROMol.cpp
Line | Count | Source |
1 | | // |
2 | | // Copyright (C) 2003-2024 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 | | |
11 | | // our stuff |
12 | | #include <RDGeneral/Invariant.h> |
13 | | #include <RDGeneral/RDLog.h> |
14 | | #include "ROMol.h" |
15 | | #include "Atom.h" |
16 | | #include "QueryAtom.h" |
17 | | #include "Bond.h" |
18 | | #include "QueryBond.h" |
19 | | #include "MolPickler.h" |
20 | | #include "Conformer.h" |
21 | | #include "SubstanceGroup.h" |
22 | | |
23 | | #ifdef RDK_USE_BOOST_SERIALIZATION |
24 | | #include <RDGeneral/BoostStartInclude.h> |
25 | | #include <boost/archive/text_oarchive.hpp> |
26 | | #include <boost/archive/text_iarchive.hpp> |
27 | | #include <RDGeneral/BoostEndInclude.h> |
28 | | #endif |
29 | | |
30 | | namespace RDKit { |
31 | | class QueryAtom; |
32 | | class QueryBond; |
33 | | |
34 | | const int ci_RIGHTMOST_ATOM = -0xBADBEEF; |
35 | | const int ci_LEADING_BOND = -0xBADBEEF + 1; |
36 | | const int ci_ATOM_HOLDER = -0xDEADD06; |
37 | | |
38 | 546k | void ROMol::destroy() { |
39 | 546k | d_atomBookmarks.clear(); |
40 | 546k | d_bondBookmarks.clear(); |
41 | | |
42 | 546k | auto atItP = boost::vertices(d_graph); |
43 | 19.4M | while (atItP.first != atItP.second) { |
44 | 18.9M | delete (d_graph)[*(atItP.first++)]; |
45 | 18.9M | } |
46 | | |
47 | 546k | auto bondItP = boost::edges(d_graph); |
48 | 15.3M | while (bondItP.first != bondItP.second) { |
49 | 14.7M | delete (d_graph)[*(bondItP.first++)]; |
50 | 14.7M | } |
51 | | |
52 | 546k | d_graph.clear(); |
53 | | |
54 | 546k | delete dp_ringInfo; |
55 | | |
56 | 546k | d_sgroups.clear(); |
57 | 546k | d_stereo_groups.clear(); |
58 | 546k | } |
59 | | |
60 | 0 | ROMol::ROMol(const std::string &pickle) : RDProps() { |
61 | 0 | initMol(); |
62 | 0 | numBonds = 0; |
63 | 0 | MolPickler::molFromPickle(pickle, *this); |
64 | 0 | numBonds = rdcast<unsigned int>(boost::num_edges(d_graph)); |
65 | 0 | } |
66 | | |
67 | | ROMol::ROMol(const std::string &pickle, unsigned int propertyFlags) |
68 | 0 | : RDProps() { |
69 | 0 | initMol(); |
70 | 0 | numBonds = 0; |
71 | 0 | MolPickler::molFromPickle(pickle, *this, propertyFlags); |
72 | 0 | numBonds = rdcast<unsigned int>(boost::num_edges(d_graph)); |
73 | 0 | } |
74 | | |
75 | 471k | void ROMol::initFromOther(const ROMol &other, bool quickCopy, int confId) { |
76 | 471k | if (this == &other) { |
77 | 0 | return; |
78 | 0 | } |
79 | 471k | numBonds = 0; |
80 | | // std::cerr<<" init from other: "<<this<<" "<<&other<<std::endl; |
81 | | // copy over the atoms |
82 | | // Avoid repeated reallocations when copying: for MolGraph's vecS vertex |
83 | | // container, reserving upfront can reduce allocation churn. |
84 | 471k | d_graph.m_vertices.reserve(other.getNumAtoms()); |
85 | 2.40M | for (const auto oatom : other.atoms()) { |
86 | 2.40M | constexpr bool updateLabel = false; |
87 | 2.40M | constexpr bool takeOwnership = true; |
88 | 2.40M | addAtom(oatom->copy(), updateLabel, takeOwnership); |
89 | 2.40M | } |
90 | | |
91 | | // and the bonds: |
92 | 2.26M | for (const auto obond : other.bonds()) { |
93 | 2.26M | addBond(obond->copy(), true); |
94 | 2.26M | } |
95 | | |
96 | | // ring information |
97 | 471k | delete dp_ringInfo; |
98 | 471k | if (other.dp_ringInfo) { |
99 | 471k | dp_ringInfo = new RingInfo(*(other.dp_ringInfo)); |
100 | 471k | } else { |
101 | 0 | dp_ringInfo = new RingInfo(); |
102 | 0 | } |
103 | | |
104 | | // enhanced stereochemical information |
105 | 471k | d_stereo_groups.clear(); |
106 | 471k | d_stereo_groups.reserve(other.d_stereo_groups.size()); |
107 | 471k | for (auto &otherGroup : other.d_stereo_groups) { |
108 | 132k | std::vector<Atom *> atoms; |
109 | 177k | for (auto &otherAtom : otherGroup.getAtoms()) { |
110 | 177k | atoms.push_back(getAtomWithIdx(otherAtom->getIdx())); |
111 | 177k | } |
112 | 132k | std::vector<Bond *> bonds; |
113 | 132k | for (auto &otherBond : otherGroup.getBonds()) { |
114 | 0 | bonds.push_back(getBondWithIdx(otherBond->getIdx())); |
115 | 0 | } |
116 | 132k | d_stereo_groups.emplace_back(otherGroup.getGroupType(), std::move(atoms), |
117 | 132k | std::move(bonds), otherGroup.getReadId()); |
118 | 132k | d_stereo_groups.back().setWriteId(otherGroup.getWriteId()); |
119 | 132k | } |
120 | | |
121 | 471k | if (other.dp_delAtoms) { |
122 | 0 | dp_delAtoms.reset(new boost::dynamic_bitset<>(*other.dp_delAtoms)); |
123 | 471k | } else { |
124 | 471k | dp_delAtoms.reset(nullptr); |
125 | 471k | } |
126 | 471k | if (other.dp_delBonds) { |
127 | 0 | dp_delBonds.reset(new boost::dynamic_bitset<>(*other.dp_delBonds)); |
128 | 471k | } else { |
129 | 471k | dp_delBonds.reset(nullptr); |
130 | 471k | } |
131 | | |
132 | 471k | if (!quickCopy) { |
133 | | // copy conformations |
134 | 2.52k | for (const auto &conf : other.d_confs) { |
135 | 1.79k | if (confId < 0 || rdcast<int>(conf->getId()) == confId) { |
136 | 1.79k | this->addConformer(new Conformer(*conf)); |
137 | 1.79k | } |
138 | 1.79k | } |
139 | | |
140 | | // Copy sgroups |
141 | 2.52k | for (const auto &sg : getSubstanceGroups(other)) { |
142 | 1.43k | addSubstanceGroup(*this, sg); |
143 | 1.43k | } |
144 | | |
145 | 2.52k | d_props = other.d_props; |
146 | | |
147 | | // Bookmarks should be copied as well: |
148 | 2.52k | for (auto abmI : other.d_atomBookmarks) { |
149 | 0 | for (const auto *aptr : abmI.second) { |
150 | 0 | setAtomBookmark(getAtomWithIdx(aptr->getIdx()), abmI.first); |
151 | 0 | } |
152 | 0 | } |
153 | 2.52k | for (auto bbmI : other.d_bondBookmarks) { |
154 | 0 | for (const auto *bptr : bbmI.second) { |
155 | 0 | setBondBookmark(getBondWithIdx(bptr->getIdx()), bbmI.first); |
156 | 0 | } |
157 | 0 | } |
158 | 469k | } else { |
159 | 469k | d_props.reset(); |
160 | 469k | STR_VECT computed; |
161 | 469k | d_props.setVal(RDKit::detail::computedPropName, computed); |
162 | 469k | } |
163 | | |
164 | | // std::cerr<<"--------- done init from other: "<<this<<" |
165 | | // "<<&other<<std::endl; |
166 | 471k | } |
167 | | |
168 | 74.5k | void ROMol::initMol() { |
169 | 74.5k | d_props.reset(); |
170 | 74.5k | dp_ringInfo = new RingInfo(); |
171 | 74.5k | } |
172 | | |
173 | 42.1M | unsigned int ROMol::getAtomDegree(const Atom *at) const { |
174 | 42.1M | PRECONDITION(at, "no atom"); |
175 | 42.1M | PRECONDITION(&at->getOwningMol() == this, |
176 | 42.1M | "atom not associated with this molecule"); |
177 | 42.1M | return rdcast<unsigned int>(boost::out_degree(at->getIdx(), d_graph)); |
178 | 42.1M | }; |
179 | | |
180 | 778k | unsigned int ROMol::getNumAtoms(bool onlyExplicit) const { |
181 | 778k | int res = rdcast<int>(boost::num_vertices(d_graph)); |
182 | 778k | if (!onlyExplicit) { |
183 | | // if we are interested in hydrogens as well add them up from |
184 | | // each |
185 | 0 | for (const auto atom : atoms()) { |
186 | 0 | res += atom->getTotalNumHs(); |
187 | 0 | } |
188 | 0 | } |
189 | 778k | return res; |
190 | 778k | }; |
191 | 0 | unsigned int ROMol::getNumHeavyAtoms() const { |
192 | 0 | unsigned int res = 0; |
193 | 0 | for (const auto atom : atoms()) { |
194 | 0 | if (atom->getAtomicNum() > 1) { |
195 | 0 | ++res; |
196 | 0 | } |
197 | 0 | } |
198 | 0 | return res; |
199 | 0 | }; |
200 | | |
201 | 239M | Atom *ROMol::getAtomWithIdx(unsigned int idx) { |
202 | 239M | URANGE_CHECK(idx, getNumAtoms()); |
203 | | |
204 | 239M | auto vd = boost::vertex(idx, d_graph); |
205 | 239M | auto res = d_graph[vd]; |
206 | 239M | POSTCONDITION(res, ""); |
207 | 239M | return res; |
208 | 239M | } |
209 | | |
210 | 108M | const Atom *ROMol::getAtomWithIdx(unsigned int idx) const { |
211 | 108M | URANGE_CHECK(idx, getNumAtoms()); |
212 | | |
213 | 108M | auto vd = boost::vertex(idx, d_graph); |
214 | 108M | const auto res = d_graph[vd]; |
215 | | |
216 | 108M | POSTCONDITION(res, ""); |
217 | 108M | return res; |
218 | 108M | } |
219 | | |
220 | | // returns the first inserted atom with the given bookmark |
221 | 12.9M | Atom *ROMol::getAtomWithBookmark(int mark) { |
222 | 12.9M | auto lu = d_atomBookmarks.find(mark); |
223 | 12.9M | PRECONDITION((lu != d_atomBookmarks.end() && !lu->second.empty()), |
224 | 12.9M | "atom bookmark not found"); |
225 | 12.9M | return lu->second.front(); |
226 | 12.9M | }; |
227 | | |
228 | | // returns all atoms with the given bookmark |
229 | 0 | ROMol::ATOM_PTR_LIST &ROMol::getAllAtomsWithBookmark(int mark) { |
230 | 0 | auto lu = d_atomBookmarks.find(mark); |
231 | 0 | PRECONDITION(lu != d_atomBookmarks.end(), "atom bookmark not found"); |
232 | 0 | return lu->second; |
233 | 0 | }; |
234 | | |
235 | | // returns the unique atom with the given bookmark |
236 | 23.0k | Atom *ROMol::getUniqueAtomWithBookmark(int mark) { |
237 | 23.0k | auto lu = d_atomBookmarks.find(mark); |
238 | 23.0k | PRECONDITION((lu != d_atomBookmarks.end()), "bookmark not found"); |
239 | 20.2k | return lu->second.front(); |
240 | 23.0k | } |
241 | | |
242 | | // returns the first inserted bond with the given bookmark |
243 | 0 | Bond *ROMol::getBondWithBookmark(int mark) { |
244 | 0 | auto lu = d_bondBookmarks.find(mark); |
245 | 0 | PRECONDITION((lu != d_bondBookmarks.end() && !lu->second.empty()), |
246 | 0 | "bond bookmark not found"); |
247 | 0 | return lu->second.front(); |
248 | 0 | }; |
249 | | |
250 | | // returns all bonds with the given bookmark |
251 | 970k | ROMol::BOND_PTR_LIST &ROMol::getAllBondsWithBookmark(int mark) { |
252 | 970k | auto lu = d_bondBookmarks.find(mark); |
253 | 970k | PRECONDITION(lu != d_bondBookmarks.end(), "bond bookmark not found"); |
254 | 970k | return lu->second; |
255 | 970k | }; |
256 | | |
257 | | // returns the unique bond with the given bookmark |
258 | 13.9k | Bond *ROMol::getUniqueBondWithBookmark(int mark) { |
259 | 13.9k | auto lu = d_bondBookmarks.find(mark); |
260 | 13.9k | PRECONDITION((lu != d_bondBookmarks.end()), "bookmark not found"); |
261 | 11.5k | return lu->second.front(); |
262 | 13.9k | } |
263 | | |
264 | 647k | void ROMol::clearAtomBookmark(const int mark) { d_atomBookmarks.erase(mark); } |
265 | | |
266 | 440k | void ROMol::clearAtomBookmark(int mark, const Atom *atom) { |
267 | 440k | PRECONDITION(atom, "no atom"); |
268 | 440k | auto lu = d_atomBookmarks.find(mark); |
269 | 440k | if (lu != d_atomBookmarks.end()) { |
270 | 440k | auto &marks = lu->second; |
271 | 440k | unsigned int tgtIdx = atom->getIdx(); |
272 | 440k | auto entry = std::find_if(marks.begin(), marks.end(), [&tgtIdx](auto ptr) { |
273 | 440k | return ptr->getIdx() == tgtIdx; |
274 | 440k | }); |
275 | 440k | if (entry != marks.end()) { |
276 | 440k | marks.erase(entry); |
277 | 440k | } |
278 | 440k | if (marks.empty()) { |
279 | 33.2k | d_atomBookmarks.erase(mark); |
280 | 33.2k | } |
281 | 440k | } |
282 | 440k | } |
283 | | |
284 | 0 | void ROMol::clearBondBookmark(int mark) { d_bondBookmarks.erase(mark); } |
285 | 447k | void ROMol::clearBondBookmark(int mark, const Bond *bond) { |
286 | 447k | PRECONDITION(bond, "no bond"); |
287 | 447k | auto lu = d_bondBookmarks.find(mark); |
288 | 447k | if (lu != d_bondBookmarks.end()) { |
289 | 447k | auto &marks = lu->second; |
290 | 447k | unsigned int tgtIdx = bond->getIdx(); |
291 | 447k | auto entry = std::find_if(marks.begin(), marks.end(), [&tgtIdx](auto ptr) { |
292 | 447k | return ptr->getIdx() == tgtIdx; |
293 | 447k | }); |
294 | 447k | if (entry != marks.end()) { |
295 | 447k | marks.erase(entry); |
296 | 447k | } |
297 | 447k | if (marks.empty()) { |
298 | 33.2k | d_bondBookmarks.erase(mark); |
299 | 33.2k | } |
300 | 447k | } |
301 | 447k | } |
302 | | |
303 | 58.8M | unsigned int ROMol::getNumBonds(bool onlyHeavy) const { |
304 | | // By default return the bonds that connect only the heavy atoms |
305 | | // hydrogen connecting bonds are ignores |
306 | 58.8M | auto res = numBonds; |
307 | 58.8M | if (!onlyHeavy) { |
308 | | // If we need hydrogen connecting bonds add them up |
309 | 0 | for (const auto atom : atoms()) { |
310 | 0 | res += atom->getTotalNumHs(); |
311 | 0 | } |
312 | 0 | } |
313 | 58.8M | return res; |
314 | 58.8M | } |
315 | | |
316 | 19.6M | Bond *ROMol::getBondWithIdx(unsigned int idx) { |
317 | 19.6M | return const_cast<Bond *>(static_cast<const ROMol *>(this)->getBondWithIdx( |
318 | 19.6M | idx)); // avoid code duplication |
319 | 19.6M | } |
320 | | |
321 | 19.9M | const Bond *ROMol::getBondWithIdx(unsigned int idx) const { |
322 | 19.9M | URANGE_CHECK(idx, getNumBonds()); |
323 | | |
324 | | // boost::graph doesn't give us random-access to edges, |
325 | | // so we have to iterate to it |
326 | 19.9M | auto [iter, end] = getEdges(); |
327 | 9.57G | for (unsigned int i = 0; i < idx; i++) { |
328 | 9.55G | ++iter; |
329 | 9.55G | } |
330 | 19.9M | const Bond *res = d_graph[*iter]; |
331 | | |
332 | 19.9M | POSTCONDITION(res != nullptr, "Invalid bond requested"); |
333 | 19.9M | return res; |
334 | 19.9M | } |
335 | | |
336 | 35.1M | Bond *ROMol::getBondBetweenAtoms(unsigned int idx1, unsigned int idx2) { |
337 | 35.1M | return const_cast<Bond *>( |
338 | 35.1M | static_cast<const ROMol *>(this)->getBondBetweenAtoms( |
339 | 35.1M | idx1, idx2)); // avoid code duplication |
340 | 35.1M | } |
341 | | |
342 | | const Bond *ROMol::getBondBetweenAtoms(unsigned int idx1, |
343 | 54.3M | unsigned int idx2) const { |
344 | 54.3M | URANGE_CHECK(idx1, getNumAtoms()); |
345 | 54.3M | URANGE_CHECK(idx2, getNumAtoms()); |
346 | 54.3M | const Bond *res = nullptr; |
347 | | |
348 | 54.3M | auto [edge, found] = boost::edge(boost::vertex(idx1, d_graph), |
349 | 54.3M | boost::vertex(idx2, d_graph), d_graph); |
350 | 54.3M | if (found) { |
351 | 54.1M | res = d_graph[edge]; |
352 | 54.1M | } |
353 | 54.3M | return res; |
354 | 54.3M | } |
355 | | |
356 | 44.0M | ROMol::ADJ_ITER_PAIR ROMol::getAtomNeighbors(Atom const *at) const { |
357 | 44.0M | PRECONDITION(at, "no atom"); |
358 | 44.0M | PRECONDITION(&at->getOwningMol() == this, |
359 | 44.0M | "atom not associated with this molecule"); |
360 | 44.0M | return boost::adjacent_vertices(at->getIdx(), d_graph); |
361 | 44.0M | }; |
362 | | |
363 | 270M | ROMol::OBOND_ITER_PAIR ROMol::getAtomBonds(Atom const *at) const { |
364 | 270M | PRECONDITION(at, "no atom"); |
365 | 270M | PRECONDITION(&at->getOwningMol() == this, |
366 | 270M | "atom not associated with this molecule"); |
367 | 270M | return boost::out_edges(at->getIdx(), d_graph); |
368 | 270M | } |
369 | | |
370 | 0 | ROMol::ATOM_ITER_PAIR ROMol::getVertices() { return boost::vertices(d_graph); } |
371 | 13.7k | ROMol::BOND_ITER_PAIR ROMol::getEdges() { return boost::edges(d_graph); } |
372 | 0 | ROMol::ATOM_ITER_PAIR ROMol::getVertices() const { |
373 | 0 | return boost::vertices(d_graph); |
374 | 0 | } |
375 | 19.9M | ROMol::BOND_ITER_PAIR ROMol::getEdges() const { return boost::edges(d_graph); } |
376 | | |
377 | | unsigned int ROMol::addAtom(Atom *atom_pin, bool updateLabel, |
378 | 18.9M | bool takeOwnership) { |
379 | 18.9M | PRECONDITION(atom_pin, "null atom passed in"); |
380 | 18.9M | PRECONDITION(!takeOwnership || !atom_pin->hasOwningMol() || |
381 | 18.9M | &atom_pin->getOwningMol() == this, |
382 | 18.9M | "cannot take ownership of an atom which already has an owner"); |
383 | 18.9M | Atom *atom_p; |
384 | 18.9M | if (!takeOwnership) { |
385 | 0 | atom_p = atom_pin->copy(); |
386 | 18.9M | } else { |
387 | 18.9M | atom_p = atom_pin; |
388 | 18.9M | } |
389 | | |
390 | 18.9M | atom_p->setOwningMol(this); |
391 | 18.9M | auto which = boost::add_vertex(d_graph); |
392 | 18.9M | d_graph[which] = atom_p; |
393 | 18.9M | atom_p->setIdx(which); |
394 | 18.9M | if (updateLabel) { |
395 | 14.8M | replaceAtomBookmark(atom_p, ci_RIGHTMOST_ATOM); |
396 | 14.8M | } |
397 | 18.9M | for (auto &conf : d_confs) { |
398 | 1.57M | conf->setAtomPos(which, RDGeom::Point3D(0.0, 0.0, 0.0)); |
399 | 1.57M | } |
400 | 18.9M | return rdcast<unsigned int>(which); |
401 | 18.9M | }; |
402 | | |
403 | 4.26M | unsigned int ROMol::addBond(Bond *bond_pin, bool takeOwnership) { |
404 | 4.26M | PRECONDITION(bond_pin, "null bond passed in"); |
405 | 4.26M | PRECONDITION(!takeOwnership || !bond_pin->hasOwningMol() || |
406 | 4.26M | &bond_pin->getOwningMol() == this, |
407 | 4.26M | "cannot take ownership of an bond which already has an owner"); |
408 | 4.26M | URANGE_CHECK(bond_pin->getBeginAtomIdx(), getNumAtoms()); |
409 | 4.26M | URANGE_CHECK(bond_pin->getEndAtomIdx(), getNumAtoms()); |
410 | 4.26M | PRECONDITION(bond_pin->getBeginAtomIdx() != bond_pin->getEndAtomIdx(), |
411 | 4.26M | "attempt to add self-bond"); |
412 | 4.26M | PRECONDITION(!(boost::edge(bond_pin->getBeginAtomIdx(), |
413 | 4.26M | bond_pin->getEndAtomIdx(), d_graph) |
414 | 4.26M | .second), |
415 | 4.26M | "bond already exists"); |
416 | | |
417 | 4.26M | Bond *bond_p; |
418 | 4.26M | if (!takeOwnership) { |
419 | 38.6k | bond_p = bond_pin->copy(); |
420 | 4.22M | } else { |
421 | 4.22M | bond_p = bond_pin; |
422 | 4.22M | } |
423 | | |
424 | 4.26M | bond_p->setOwningMol(this); |
425 | 4.26M | auto [which, ok] = boost::add_edge(bond_p->getBeginAtomIdx(), |
426 | 4.26M | bond_p->getEndAtomIdx(), d_graph); |
427 | 4.26M | CHECK_INVARIANT(ok, "bond could not be added"); |
428 | 4.26M | d_graph[which] = bond_p; |
429 | 4.26M | bond_p->setIdx(numBonds); |
430 | 4.26M | numBonds++; |
431 | 4.26M | return numBonds; |
432 | 4.26M | } |
433 | | |
434 | 18.0k | void ROMol::setStereoGroups(std::vector<StereoGroup> stereo_groups) { |
435 | 18.0k | auto is_abs = [](const auto &sg) { |
436 | 15.7k | return sg.getGroupType() == StereoGroupType::STEREO_ABSOLUTE; |
437 | 15.7k | }; |
438 | | |
439 | | // if there's more than one ABS group, merge them |
440 | 18.0k | if (auto num_abs = std::ranges::count_if(stereo_groups, is_abs); |
441 | 18.0k | num_abs <= 1) { |
442 | 18.0k | d_stereo_groups = std::move(stereo_groups); |
443 | 18.0k | } else { |
444 | 0 | std::vector<Atom *> abs_atoms; |
445 | 0 | std::vector<Bond *> abs_bonds; |
446 | 0 | std::vector<StereoGroup> new_stereo_groups; |
447 | 0 | new_stereo_groups.reserve(stereo_groups.size() - num_abs + 1); |
448 | 0 | for (auto &&sg : stereo_groups) { |
449 | 0 | if (is_abs(sg)) { |
450 | 0 | auto &other_atoms = sg.getAtoms(); |
451 | 0 | auto &other_bonds = sg.getBonds(); |
452 | 0 | abs_atoms.insert(abs_atoms.begin(), other_atoms.begin(), |
453 | 0 | other_atoms.end()); |
454 | 0 | abs_bonds.insert(abs_bonds.begin(), other_bonds.begin(), |
455 | 0 | other_bonds.end()); |
456 | 0 | } else { |
457 | 0 | new_stereo_groups.push_back(std::move(sg)); |
458 | 0 | } |
459 | 0 | } |
460 | 0 | new_stereo_groups.emplace_back(StereoGroupType::STEREO_ABSOLUTE, |
461 | 0 | std::move(abs_atoms), std::move(abs_bonds)); |
462 | 0 | d_stereo_groups = std::move(new_stereo_groups); |
463 | 0 | } |
464 | 18.0k | } |
465 | | |
466 | 0 | void ROMol::debugMol(std::ostream &str) const { |
467 | 0 | str << "Atoms:" << std::endl; |
468 | 0 | for (const auto atom : atoms()) { |
469 | 0 | str << "\t" << *atom << std::endl; |
470 | 0 | } |
471 | |
|
472 | 0 | str << "Bonds:" << std::endl; |
473 | 0 | for (const auto bond : bonds()) { |
474 | 0 | str << "\t" << *bond << std::endl; |
475 | 0 | } |
476 | |
|
477 | 0 | const auto &sgs = getSubstanceGroups(*this); |
478 | 0 | if (!sgs.empty()) { |
479 | 0 | str << "Substance Groups:" << std::endl; |
480 | 0 | for (const auto &sg : sgs) { |
481 | 0 | str << "\t" << sg << std::endl; |
482 | 0 | } |
483 | 0 | } |
484 | |
|
485 | 0 | const auto &stgs = getStereoGroups(); |
486 | 0 | if (!stgs.empty()) { |
487 | 0 | unsigned idx = 0; |
488 | 0 | str << "Stereo Groups:" << std::endl; |
489 | 0 | for (const auto &stg : stgs) { |
490 | 0 | str << "\t" << idx << ' ' << stg << std::endl; |
491 | 0 | ++idx; |
492 | 0 | } |
493 | 0 | } |
494 | 0 | } |
495 | | |
496 | | // -------------------------------------------- |
497 | | // |
498 | | // Iterators |
499 | | // |
500 | | // -------------------------------------------- |
501 | 0 | ROMol::AtomIterator ROMol::beginAtoms() { return AtomIterator(this); } |
502 | 0 | ROMol::ConstAtomIterator ROMol::beginAtoms() const { |
503 | 0 | return ConstAtomIterator(this); |
504 | 0 | } |
505 | 0 | ROMol::AtomIterator ROMol::endAtoms() { |
506 | 0 | return AtomIterator(this, getNumAtoms()); |
507 | 0 | } |
508 | 0 | ROMol::ConstAtomIterator ROMol::endAtoms() const { |
509 | 0 | return ConstAtomIterator(this, getNumAtoms()); |
510 | 0 | } |
511 | | |
512 | 0 | ROMol::AromaticAtomIterator ROMol::beginAromaticAtoms() { |
513 | 0 | return AromaticAtomIterator(this); |
514 | 0 | } |
515 | 0 | ROMol::ConstAromaticAtomIterator ROMol::beginAromaticAtoms() const { |
516 | 0 | return ConstAromaticAtomIterator(this); |
517 | 0 | } |
518 | 0 | ROMol::AromaticAtomIterator ROMol::endAromaticAtoms() { |
519 | 0 | return AromaticAtomIterator(this, getNumAtoms()); |
520 | 0 | } |
521 | 0 | ROMol::ConstAromaticAtomIterator ROMol::endAromaticAtoms() const { |
522 | 0 | return ConstAromaticAtomIterator(this, getNumAtoms()); |
523 | 0 | } |
524 | | |
525 | 0 | ROMol::HeteroatomIterator ROMol::beginHeteros() { |
526 | 0 | return HeteroatomIterator(this); |
527 | 0 | } |
528 | 0 | ROMol::ConstHeteroatomIterator ROMol::beginHeteros() const { |
529 | 0 | return ConstHeteroatomIterator(this); |
530 | 0 | } |
531 | 0 | ROMol::HeteroatomIterator ROMol::endHeteros() { |
532 | 0 | return HeteroatomIterator(this, getNumAtoms()); |
533 | 0 | } |
534 | 0 | ROMol::ConstHeteroatomIterator ROMol::endHeteros() const { |
535 | 0 | return ConstHeteroatomIterator(this, getNumAtoms()); |
536 | 0 | } |
537 | | |
538 | 0 | bool ROMol::hasQuery() const { |
539 | 0 | for (auto atom : atoms()) { |
540 | 0 | if (atom->hasQuery()) { |
541 | 0 | return true; |
542 | 0 | } |
543 | 0 | } |
544 | 0 | for (auto bond : bonds()) { |
545 | 0 | if (bond->hasQuery()) { |
546 | 0 | return true; |
547 | 0 | } |
548 | 0 | } |
549 | 0 | return false; |
550 | 0 | } |
551 | | |
552 | 0 | ROMol::QueryAtomIterator ROMol::beginQueryAtoms(QueryAtom const *what) { |
553 | 0 | return QueryAtomIterator(this, what); |
554 | 0 | } |
555 | | ROMol::ConstQueryAtomIterator ROMol::beginQueryAtoms( |
556 | 0 | QueryAtom const *what) const { |
557 | 0 | return ConstQueryAtomIterator(this, what); |
558 | 0 | } |
559 | 0 | ROMol::QueryAtomIterator ROMol::endQueryAtoms() { |
560 | 0 | return QueryAtomIterator(this, getNumAtoms()); |
561 | 0 | } |
562 | 0 | ROMol::ConstQueryAtomIterator ROMol::endQueryAtoms() const { |
563 | 0 | return ConstQueryAtomIterator(this, getNumAtoms()); |
564 | 0 | } |
565 | 0 | ROMol::MatchingAtomIterator ROMol::beginMatchingAtoms(bool (*what)(Atom *)) { |
566 | 0 | return MatchingAtomIterator(this, what); |
567 | 0 | } |
568 | | ROMol::ConstMatchingAtomIterator ROMol::beginMatchingAtoms( |
569 | 0 | bool (*what)(const Atom *)) const { |
570 | 0 | return ConstMatchingAtomIterator(this, what); |
571 | 0 | } |
572 | 0 | ROMol::MatchingAtomIterator ROMol::endMatchingAtoms() { |
573 | 0 | return MatchingAtomIterator(this, getNumAtoms()); |
574 | 0 | } |
575 | 0 | ROMol::ConstMatchingAtomIterator ROMol::endMatchingAtoms() const { |
576 | 0 | return ConstMatchingAtomIterator(this, getNumAtoms()); |
577 | 0 | } |
578 | | |
579 | 0 | ROMol::BondIterator ROMol::beginBonds() { return BondIterator(this); } |
580 | 0 | ROMol::ConstBondIterator ROMol::beginBonds() const { |
581 | 0 | return ConstBondIterator(this); |
582 | 0 | } |
583 | 0 | ROMol::BondIterator ROMol::endBonds() { |
584 | 0 | auto [beg, end] = getEdges(); |
585 | 0 | return BondIterator(this, end); |
586 | 0 | } |
587 | 0 | ROMol::ConstBondIterator ROMol::endBonds() const { |
588 | 0 | auto [beg, end] = getEdges(); |
589 | 0 | return ConstBondIterator(this, end); |
590 | 0 | } |
591 | | |
592 | 32.3k | void ROMol::clearComputedProps(bool includeRings) const { |
593 | | // the SSSR information: |
594 | 32.3k | if (includeRings) { |
595 | 32.3k | this->dp_ringInfo->reset(); |
596 | 32.3k | } |
597 | | |
598 | 32.3k | RDProps::clearComputedProps(); |
599 | | |
600 | 22.2M | for (auto atom : atoms()) { |
601 | 22.2M | atom->clearComputedProps(); |
602 | 22.2M | } |
603 | | |
604 | 17.6M | for (auto bond : bonds()) { |
605 | 17.6M | bond->clearComputedProps(); |
606 | 17.6M | } |
607 | 32.3k | } |
608 | | |
609 | 36.7k | void ROMol::updatePropertyCache(bool strict) { |
610 | 18.6M | for (auto atom : atoms()) { |
611 | 18.6M | atom->updatePropertyCache(strict); |
612 | 18.6M | } |
613 | 14.3M | for (auto bond : bonds()) { |
614 | 14.3M | bond->updatePropertyCache(strict); |
615 | 14.3M | } |
616 | 36.7k | } |
617 | | |
618 | 9.43k | bool ROMol::needsUpdatePropertyCache() const { |
619 | 2.75M | for (const auto atom : atoms()) { |
620 | 2.75M | if (atom->needsUpdatePropertyCache()) { |
621 | 520 | return true; |
622 | 520 | } |
623 | 2.75M | } |
624 | | // there is no test for bonds yet since they do not obtain a valence property |
625 | 8.91k | return false; |
626 | 9.43k | } |
627 | | |
628 | 0 | void ROMol::clearPropertyCache() { |
629 | 0 | for (auto atom : atoms()) { |
630 | 0 | atom->clearPropertyCache(); |
631 | 0 | } |
632 | 0 | } |
633 | | |
634 | 1.59M | const Conformer &ROMol::getConformer(int id) const { |
635 | | // make sure we have more than one conformation |
636 | 1.59M | if (d_confs.size() == 0) { |
637 | 0 | throw ConformerException("No conformations available on the molecule"); |
638 | 0 | } |
639 | | |
640 | 1.59M | if (id < 0) { |
641 | 1.58M | return *(d_confs.front()); |
642 | 1.58M | } |
643 | 9.35k | auto cid = (unsigned int)id; |
644 | 274k | for (auto conf : d_confs) { |
645 | 274k | if (conf->getId() == cid) { |
646 | 9.35k | return *conf; |
647 | 9.35k | } |
648 | 274k | } |
649 | | // we did not find a conformation with the specified ID |
650 | 0 | std::string mesg = "Can't find conformation with ID: "; |
651 | 0 | mesg += id; |
652 | 0 | throw ConformerException(mesg); |
653 | 9.35k | } |
654 | | |
655 | 1.59M | Conformer &ROMol::getConformer(int id) { |
656 | 1.59M | return const_cast<Conformer &>( |
657 | 1.59M | static_cast<const ROMol *>(this)->getConformer(id)); |
658 | 1.59M | } |
659 | | |
660 | 0 | void ROMol::removeConformer(unsigned int id) { |
661 | 0 | for (auto ci = d_confs.begin(); ci != d_confs.end(); ++ci) { |
662 | 0 | if ((*ci)->getId() == id) { |
663 | 0 | d_confs.erase(ci); |
664 | 0 | return; |
665 | 0 | } |
666 | 0 | } |
667 | 0 | } |
668 | | |
669 | 35.9k | unsigned int ROMol::addConformer(Conformer *conf, bool assignId) { |
670 | 35.9k | PRECONDITION(conf, "bad conformer"); |
671 | 35.9k | PRECONDITION(conf->getNumAtoms() == this->getNumAtoms(), |
672 | 35.9k | "Number of atom mismatch"); |
673 | 35.9k | if (assignId) { |
674 | 14.3k | int maxId = -1; |
675 | 14.3k | for (auto cptr : d_confs) { |
676 | 0 | maxId = std::max((int)(cptr->getId()), maxId); |
677 | 0 | } |
678 | 14.3k | maxId++; |
679 | 14.3k | conf->setId((unsigned int)maxId); |
680 | 14.3k | } |
681 | 35.9k | conf->setOwningMol(this); |
682 | 35.9k | CONFORMER_SPTR nConf(conf); |
683 | 35.9k | d_confs.push_back(nConf); |
684 | 35.9k | return conf->getId(); |
685 | 35.9k | } |
686 | | |
687 | | #ifdef RDK_USE_BOOST_SERIALIZATION |
688 | | template <class Archive> |
689 | 0 | void ROMol::save(Archive &ar, const unsigned int) const { |
690 | 0 | std::string pkl; |
691 | 0 | MolPickler::pickleMol(*this, pkl, PicklerOps::AllProps); |
692 | 0 | ar << pkl; |
693 | 0 | } |
694 | | |
695 | | template <class Archive> |
696 | 0 | void ROMol::load(Archive &ar, const unsigned int) { |
697 | 0 | std::string pkl; |
698 | 0 | ar >> pkl; |
699 | |
|
700 | 0 | delete dp_ringInfo; |
701 | 0 | initMol(); |
702 | |
|
703 | 0 | numBonds = 0; |
704 | 0 | MolPickler::molFromPickle(pkl, *this, PicklerOps::AllProps); |
705 | 0 | numBonds = rdcast<unsigned int>(boost::num_edges(d_graph)); |
706 | 0 | } |
707 | | |
708 | | template RDKIT_GRAPHMOL_EXPORT void ROMol::save<boost::archive::text_oarchive>( |
709 | | boost::archive::text_oarchive &, const unsigned int) const; |
710 | | template RDKIT_GRAPHMOL_EXPORT void ROMol::load<boost::archive::text_iarchive>( |
711 | | boost::archive::text_iarchive &, const unsigned int); |
712 | | #endif |
713 | | |
714 | | } // namespace RDKit |