Coverage Report

Created: 2026-06-23 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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