Coverage Report

Created: 2026-11-12 07:33

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
241k
void ROMol::destroy() {
39
241k
  d_atomBookmarks.clear();
40
241k
  d_bondBookmarks.clear();
41
42
241k
  auto atItP = boost::vertices(d_graph);
43
13.9M
  while (atItP.first != atItP.second) {
44
13.7M
    delete (d_graph)[*(atItP.first++)];
45
13.7M
  }
46
47
241k
  auto bondItP = boost::edges(d_graph);
48
10.0M
  while (bondItP.first != bondItP.second) {
49
9.75M
    delete (d_graph)[*(bondItP.first++)];
50
9.75M
  }
51
52
241k
  d_graph.clear();
53
54
241k
  delete dp_ringInfo;
55
56
241k
  d_sgroups.clear();
57
241k
  d_stereo_groups.clear();
58
241k
}
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
186k
void ROMol::initFromOther(const ROMol &other, bool quickCopy, int confId) {
76
186k
  if (this == &other) {
77
0
    return;
78
0
  }
79
186k
  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
186k
  d_graph.m_vertices.reserve(other.getNumAtoms());
85
878k
  for (const auto oatom : other.atoms()) {
86
878k
    constexpr bool updateLabel = false;
87
878k
    constexpr bool takeOwnership = true;
88
878k
    addAtom(oatom->copy(), updateLabel, takeOwnership);
89
878k
  }
90
91
  // and the bonds:
92
762k
  for (const auto obond : other.bonds()) {
93
762k
    addBond(obond->copy(), true);
94
762k
  }
95
96
  // ring information
97
186k
  delete dp_ringInfo;
98
186k
  if (other.dp_ringInfo) {
99
186k
    dp_ringInfo = new RingInfo(*(other.dp_ringInfo));
100
186k
  } else {
101
0
    dp_ringInfo = new RingInfo();
102
0
  }
103
104
  // enhanced stereochemical information
105
186k
  d_stereo_groups.clear();
106
186k
  d_stereo_groups.reserve(other.d_stereo_groups.size());
107
186k
  for (auto &otherGroup : other.d_stereo_groups) {
108
5.78k
    std::vector<Atom *> atoms;
109
17.1k
    for (auto &otherAtom : otherGroup.getAtoms()) {
110
17.1k
      atoms.push_back(getAtomWithIdx(otherAtom->getIdx()));
111
17.1k
    }
112
5.78k
    std::vector<Bond *> bonds;
113
5.78k
    for (auto &otherBond : otherGroup.getBonds()) {
114
0
      bonds.push_back(getBondWithIdx(otherBond->getIdx()));
115
0
    }
116
5.78k
    d_stereo_groups.emplace_back(otherGroup.getGroupType(), std::move(atoms),
117
5.78k
                                 std::move(bonds), otherGroup.getReadId());
118
5.78k
    d_stereo_groups.back().setWriteId(otherGroup.getWriteId());
119
5.78k
  }
120
121
186k
  if (other.dp_delAtoms) {
122
0
    dp_delAtoms.reset(new boost::dynamic_bitset<>(*other.dp_delAtoms));
123
186k
  } else {
124
186k
    dp_delAtoms.reset(nullptr);
125
186k
  }
126
186k
  if (other.dp_delBonds) {
127
0
    dp_delBonds.reset(new boost::dynamic_bitset<>(*other.dp_delBonds));
128
186k
  } else {
129
186k
    dp_delBonds.reset(nullptr);
130
186k
  }
131
132
186k
  if (!quickCopy) {
133
    // copy conformations
134
1.93k
    for (const auto &conf : other.d_confs) {
135
1.93k
      if (confId < 0 || rdcast<int>(conf->getId()) == confId) {
136
1.93k
        this->addConformer(new Conformer(*conf));
137
1.93k
      }
138
1.93k
    }
139
140
    // Copy sgroups
141
1.93k
    for (const auto &sg : getSubstanceGroups(other)) {
142
901
      addSubstanceGroup(*this, sg);
143
901
    }
144
145
1.93k
    d_props = other.d_props;
146
147
    // Bookmarks should be copied as well:
148
1.93k
    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
1.93k
    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
184k
  } else {
159
184k
    d_props.reset();
160
184k
    STR_VECT computed;
161
184k
    d_props.setVal(RDKit::detail::computedPropName, computed);
162
184k
  }
163
164
  // std::cerr<<"---------    done init from other: "<<this<<"
165
  // "<<&other<<std::endl;
166
186k
}
167
168
83.0k
void ROMol::initMol() {
169
83.0k
  d_props.reset();
170
83.0k
  dp_ringInfo = new RingInfo();
171
83.0k
}
172
173
19.0M
unsigned int ROMol::getAtomDegree(const Atom *at) const {
174
19.0M
  PRECONDITION(at, "no atom");
175
19.0M
  PRECONDITION(&at->getOwningMol() == this,
176
19.0M
               "atom not associated with this molecule");
177
19.0M
  return rdcast<unsigned int>(boost::out_degree(at->getIdx(), d_graph));
178
19.0M
};
179
180
390k
unsigned int ROMol::getNumAtoms(bool onlyExplicit) const {
181
390k
  int res = rdcast<int>(boost::num_vertices(d_graph));
182
390k
  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
390k
  return res;
190
390k
};
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
86.3M
Atom *ROMol::getAtomWithIdx(unsigned int idx) {
202
86.3M
  URANGE_CHECK(idx, getNumAtoms());
203
204
86.3M
  auto vd = boost::vertex(idx, d_graph);
205
86.3M
  auto res = d_graph[vd];
206
86.3M
  POSTCONDITION(res, "");
207
86.3M
  return res;
208
86.3M
}
209
210
7.36M
const Atom *ROMol::getAtomWithIdx(unsigned int idx) const {
211
7.36M
  URANGE_CHECK(idx, getNumAtoms());
212
213
7.36M
  auto vd = boost::vertex(idx, d_graph);
214
7.36M
  const auto res = d_graph[vd];
215
216
7.36M
  POSTCONDITION(res, "");
217
7.36M
  return res;
218
7.36M
}
219
220
// returns the first inserted atom with the given bookmark
221
9.21M
Atom *ROMol::getAtomWithBookmark(int mark) {
222
9.21M
  auto lu = d_atomBookmarks.find(mark);
223
9.21M
  PRECONDITION((lu != d_atomBookmarks.end() && !lu->second.empty()),
224
9.21M
               "atom bookmark not found");
225
9.21M
  return lu->second.front();
226
9.21M
};
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
142k
Atom *ROMol::getUniqueAtomWithBookmark(int mark) {
237
142k
  auto lu = d_atomBookmarks.find(mark);
238
142k
  PRECONDITION((lu != d_atomBookmarks.end()), "bookmark not found");
239
138k
  return lu->second.front();
240
142k
}
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
396k
ROMol::BOND_PTR_LIST &ROMol::getAllBondsWithBookmark(int mark) {
252
396k
  auto lu = d_bondBookmarks.find(mark);
253
396k
  PRECONDITION(lu != d_bondBookmarks.end(), "bond bookmark not found");
254
396k
  return lu->second;
255
396k
};
256
257
// returns the unique bond with the given bookmark
258
10.4k
Bond *ROMol::getUniqueBondWithBookmark(int mark) {
259
10.4k
  auto lu = d_bondBookmarks.find(mark);
260
10.4k
  PRECONDITION((lu != d_bondBookmarks.end()), "bookmark not found");
261
7.99k
  return lu->second.front();
262
10.4k
}
263
264
338k
void ROMol::clearAtomBookmark(const int mark) { d_atomBookmarks.erase(mark); }
265
266
72.6k
void ROMol::clearAtomBookmark(int mark, const Atom *atom) {
267
72.6k
  PRECONDITION(atom, "no atom");
268
72.6k
  auto lu = d_atomBookmarks.find(mark);
269
72.6k
  if (lu != d_atomBookmarks.end()) {
270
72.6k
    auto &marks = lu->second;
271
72.6k
    unsigned int tgtIdx = atom->getIdx();
272
72.6k
    auto entry = std::find_if(marks.begin(), marks.end(), [&tgtIdx](auto ptr) {
273
72.6k
      return ptr->getIdx() == tgtIdx;
274
72.6k
    });
275
72.6k
    if (entry != marks.end()) {
276
72.6k
      marks.erase(entry);
277
72.6k
    }
278
72.6k
    if (marks.empty()) {
279
15.8k
      d_atomBookmarks.erase(mark);
280
15.8k
    }
281
72.6k
  }
282
72.6k
}
283
284
0
void ROMol::clearBondBookmark(int mark) { d_bondBookmarks.erase(mark); }
285
76.9k
void ROMol::clearBondBookmark(int mark, const Bond *bond) {
286
76.9k
  PRECONDITION(bond, "no bond");
287
76.9k
  auto lu = d_bondBookmarks.find(mark);
288
76.9k
  if (lu != d_bondBookmarks.end()) {
289
76.9k
    auto &marks = lu->second;
290
76.9k
    unsigned int tgtIdx = bond->getIdx();
291
76.9k
    auto entry = std::find_if(marks.begin(), marks.end(), [&tgtIdx](auto ptr) {
292
76.9k
      return ptr->getIdx() == tgtIdx;
293
76.9k
    });
294
76.9k
    if (entry != marks.end()) {
295
76.9k
      marks.erase(entry);
296
76.9k
    }
297
76.9k
    if (marks.empty()) {
298
15.8k
      d_bondBookmarks.erase(mark);
299
15.8k
    }
300
76.9k
  }
301
76.9k
}
302
303
19.2M
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
19.2M
  auto res = numBonds;
307
19.2M
  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
19.2M
  return res;
314
19.2M
}
315
316
885k
Bond *ROMol::getBondWithIdx(unsigned int idx) {
317
885k
  return const_cast<Bond *>(static_cast<const ROMol *>(this)->getBondWithIdx(
318
885k
      idx));  // avoid code duplication
319
885k
}
320
321
903k
const Bond *ROMol::getBondWithIdx(unsigned int idx) const {
322
903k
  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
903k
  auto [iter, end] = getEdges();
327
567M
  for (unsigned int i = 0; i < idx; i++) {
328
567M
    ++iter;
329
567M
  }
330
903k
  const Bond *res = d_graph[*iter];
331
332
903k
  POSTCONDITION(res != nullptr, "Invalid bond requested");
333
903k
  return res;
334
903k
}
335
336
8.54M
Bond *ROMol::getBondBetweenAtoms(unsigned int idx1, unsigned int idx2) {
337
8.54M
  return const_cast<Bond *>(
338
8.54M
      static_cast<const ROMol *>(this)->getBondBetweenAtoms(
339
8.54M
          idx1, idx2));  // avoid code duplication
340
8.54M
}
341
342
const Bond *ROMol::getBondBetweenAtoms(unsigned int idx1,
343
10.0M
                                       unsigned int idx2) const {
344
10.0M
  URANGE_CHECK(idx1, getNumAtoms());
345
10.0M
  URANGE_CHECK(idx2, getNumAtoms());
346
10.0M
  const Bond *res = nullptr;
347
348
10.0M
  auto [edge, found] = boost::edge(boost::vertex(idx1, d_graph),
349
10.0M
                                   boost::vertex(idx2, d_graph), d_graph);
350
10.0M
  if (found) {
351
10.0M
    res = d_graph[edge];
352
10.0M
  }
353
10.0M
  return res;
354
10.0M
}
355
356
13.2M
ROMol::ADJ_ITER_PAIR ROMol::getAtomNeighbors(Atom const *at) const {
357
13.2M
  PRECONDITION(at, "no atom");
358
13.2M
  PRECONDITION(&at->getOwningMol() == this,
359
13.2M
               "atom not associated with this molecule");
360
13.2M
  return boost::adjacent_vertices(at->getIdx(), d_graph);
361
13.2M
};
362
363
82.5M
ROMol::OBOND_ITER_PAIR ROMol::getAtomBonds(Atom const *at) const {
364
82.5M
  PRECONDITION(at, "no atom");
365
82.5M
  PRECONDITION(&at->getOwningMol() == this,
366
82.5M
               "atom not associated with this molecule");
367
82.5M
  return boost::out_edges(at->getIdx(), d_graph);
368
82.5M
}
369
370
0
ROMol::ATOM_ITER_PAIR ROMol::getVertices() { return boost::vertices(d_graph); }
371
1.52k
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
903k
ROMol::BOND_ITER_PAIR ROMol::getEdges() const { return boost::edges(d_graph); }
376
377
unsigned int ROMol::addAtom(Atom *atom_pin, bool updateLabel,
378
13.7M
                            bool takeOwnership) {
379
13.7M
  PRECONDITION(atom_pin, "null atom passed in");
380
13.7M
  PRECONDITION(!takeOwnership || !atom_pin->hasOwningMol() ||
381
13.7M
                   &atom_pin->getOwningMol() == this,
382
13.7M
               "cannot take ownership of an atom which already has an owner");
383
13.7M
  Atom *atom_p;
384
13.7M
  if (!takeOwnership) {
385
0
    atom_p = atom_pin->copy();
386
13.7M
  } else {
387
13.7M
    atom_p = atom_pin;
388
13.7M
  }
389
390
13.7M
  atom_p->setOwningMol(this);
391
13.7M
  auto which = boost::add_vertex(d_graph);
392
13.7M
  d_graph[which] = atom_p;
393
13.7M
  atom_p->setIdx(which);
394
13.7M
  if (updateLabel) {
395
11.4M
    replaceAtomBookmark(atom_p, ci_RIGHTMOST_ATOM);
396
11.4M
  }
397
13.7M
  for (auto &conf : d_confs) {
398
1.01M
    conf->setAtomPos(which, RDGeom::Point3D(0.0, 0.0, 0.0));
399
1.01M
  }
400
13.7M
  return rdcast<unsigned int>(which);
401
13.7M
};
402
403
4.81M
unsigned int ROMol::addBond(Bond *bond_pin, bool takeOwnership) {
404
4.81M
  PRECONDITION(bond_pin, "null bond passed in");
405
4.81M
  PRECONDITION(!takeOwnership || !bond_pin->hasOwningMol() ||
406
4.81M
                   &bond_pin->getOwningMol() == this,
407
4.81M
               "cannot take ownership of an bond which already has an owner");
408
4.81M
  URANGE_CHECK(bond_pin->getBeginAtomIdx(), getNumAtoms());
409
4.81M
  URANGE_CHECK(bond_pin->getEndAtomIdx(), getNumAtoms());
410
4.81M
  PRECONDITION(bond_pin->getBeginAtomIdx() != bond_pin->getEndAtomIdx(),
411
4.81M
               "attempt to add self-bond");
412
4.81M
  PRECONDITION(!(boost::edge(bond_pin->getBeginAtomIdx(),
413
4.81M
                             bond_pin->getEndAtomIdx(), d_graph)
414
4.81M
                     .second),
415
4.81M
               "bond already exists");
416
417
4.81M
  Bond *bond_p;
418
4.81M
  if (!takeOwnership) {
419
187k
    bond_p = bond_pin->copy();
420
4.62M
  } else {
421
4.62M
    bond_p = bond_pin;
422
4.62M
  }
423
424
4.81M
  bond_p->setOwningMol(this);
425
4.81M
  auto [which, ok] = boost::add_edge(bond_p->getBeginAtomIdx(),
426
4.81M
                                     bond_p->getEndAtomIdx(), d_graph);
427
4.81M
  CHECK_INVARIANT(ok, "bond could not be added");
428
4.81M
  d_graph[which] = bond_p;
429
4.81M
  bond_p->setIdx(numBonds);
430
4.81M
  numBonds++;
431
4.81M
  return numBonds;
432
4.81M
}
433
434
19.1k
void ROMol::setStereoGroups(std::vector<StereoGroup> stereo_groups) {
435
29.3k
  auto is_abs = [](const auto &sg) {
436
29.3k
    return sg.getGroupType() == StereoGroupType::STEREO_ABSOLUTE;
437
29.3k
  };
438
439
  // if there's more than one ABS group, merge them
440
19.1k
  if (auto num_abs = std::ranges::count_if(stereo_groups, is_abs);
441
19.1k
      num_abs <= 1) {
442
19.1k
    d_stereo_groups = std::move(stereo_groups);
443
19.1k
  } 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
19.1k
}
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(Atom const *what) {
553
0
  return QueryAtomIterator(this, what);
554
0
}
555
0
ROMol::ConstQueryAtomIterator ROMol::beginQueryAtoms(Atom const *what) const {
556
0
  return ConstQueryAtomIterator(this, what);
557
0
}
558
0
ROMol::QueryAtomIterator ROMol::endQueryAtoms() {
559
0
  return QueryAtomIterator(this, getNumAtoms());
560
0
}
561
0
ROMol::ConstQueryAtomIterator ROMol::endQueryAtoms() const {
562
0
  return ConstQueryAtomIterator(this, getNumAtoms());
563
0
}
564
0
ROMol::MatchingAtomIterator ROMol::beginMatchingAtoms(bool (*what)(Atom *)) {
565
0
  return MatchingAtomIterator(this, what);
566
0
}
567
ROMol::ConstMatchingAtomIterator ROMol::beginMatchingAtoms(
568
0
    bool (*what)(const Atom *)) const {
569
0
  return ConstMatchingAtomIterator(this, what);
570
0
}
571
0
ROMol::MatchingAtomIterator ROMol::endMatchingAtoms() {
572
0
  return MatchingAtomIterator(this, getNumAtoms());
573
0
}
574
0
ROMol::ConstMatchingAtomIterator ROMol::endMatchingAtoms() const {
575
0
  return ConstMatchingAtomIterator(this, getNumAtoms());
576
0
}
577
578
0
ROMol::BondIterator ROMol::beginBonds() { return BondIterator(this); }
579
0
ROMol::ConstBondIterator ROMol::beginBonds() const {
580
0
  return ConstBondIterator(this);
581
0
}
582
0
ROMol::BondIterator ROMol::endBonds() {
583
0
  auto [beg, end] = getEdges();
584
0
  return BondIterator(this, end);
585
0
}
586
0
ROMol::ConstBondIterator ROMol::endBonds() const {
587
0
  auto [beg, end] = getEdges();
588
0
  return ConstBondIterator(this, end);
589
0
}
590
591
0
void ROMol::setName(const std::string &name) const {
592
0
  setProp(common_properties::_Name, name);
593
0
}
594
595
0
std::string ROMol::getName() const {
596
0
  std::string name;
597
0
  getPropIfPresent(common_properties::_Name, name);
598
0
  return name;
599
0
}
600
601
11.2k
void ROMol::clearComputedProps(bool includeRings) const {
602
  // the SSSR information:
603
11.2k
  if (includeRings) {
604
11.2k
    this->dp_ringInfo->reset();
605
11.2k
  }
606
607
11.2k
  RDProps::clearComputedProps();
608
609
10.5M
  for (auto atom : atoms()) {
610
10.5M
    atom->clearComputedProps();
611
10.5M
  }
612
613
5.50M
  for (auto bond : bonds()) {
614
5.50M
    bond->clearComputedProps();
615
5.50M
  }
616
11.2k
}
617
618
18.6k
void ROMol::updatePropertyCache(bool strict) {
619
8.74M
  for (auto atom : atoms()) {
620
8.74M
    atom->updatePropertyCache(strict);
621
8.74M
  }
622
4.99M
  for (auto bond : bonds()) {
623
4.99M
    bond->updatePropertyCache(strict);
624
4.99M
  }
625
18.6k
}
626
627
4.53k
bool ROMol::needsUpdatePropertyCache() const {
628
1.39M
  for (const auto atom : atoms()) {
629
1.39M
    if (atom->needsUpdatePropertyCache()) {
630
269
      return true;
631
269
    }
632
1.39M
  }
633
  // there is no test for bonds yet since they do not obtain a valence property
634
4.26k
  return false;
635
4.53k
}
636
637
0
void ROMol::clearPropertyCache() {
638
0
  for (auto atom : atoms()) {
639
0
    atom->clearPropertyCache();
640
0
  }
641
0
}
642
643
1.03M
const Conformer &ROMol::getConformer(int id) const {
644
  // make sure we have more than one conformation
645
1.03M
  if (d_confs.size() == 0) {
646
0
    throw ConformerException("No conformations available on the molecule");
647
0
  }
648
649
1.03M
  if (id < 0) {
650
1.02M
    return *(d_confs.front());
651
1.02M
  }
652
4.58k
  auto cid = (unsigned int)id;
653
13.2k
  for (auto conf : d_confs) {
654
13.2k
    if (conf->getId() == cid) {
655
4.58k
      return *conf;
656
4.58k
    }
657
13.2k
  }
658
  // we did not find a conformation with the specified ID
659
0
  std::string mesg = "Can't find conformation with ID: ";
660
0
  mesg += id;
661
0
  throw ConformerException(mesg);
662
4.58k
}
663
664
1.03M
Conformer &ROMol::getConformer(int id) {
665
1.03M
  return const_cast<Conformer &>(
666
1.03M
      static_cast<const ROMol *>(this)->getConformer(id));
667
1.03M
}
668
669
0
void ROMol::removeConformer(unsigned int id) {
670
0
  for (auto ci = d_confs.begin(); ci != d_confs.end(); ++ci) {
671
0
    if ((*ci)->getId() == id) {
672
0
      d_confs.erase(ci);
673
0
      return;
674
0
    }
675
0
  }
676
0
}
677
678
20.9k
unsigned int ROMol::addConformer(Conformer *conf, bool assignId) {
679
20.9k
  PRECONDITION(conf, "bad conformer");
680
20.9k
  PRECONDITION(conf->getNumAtoms() == this->getNumAtoms(),
681
20.9k
               "Number of atom mismatch");
682
20.9k
  if (assignId) {
683
8.85k
    int maxId = -1;
684
8.85k
    for (auto cptr : d_confs) {
685
0
      maxId = std::max((int)(cptr->getId()), maxId);
686
0
    }
687
8.85k
    maxId++;
688
8.85k
    conf->setId((unsigned int)maxId);
689
8.85k
  }
690
20.9k
  conf->setOwningMol(this);
691
20.9k
  CONFORMER_SPTR nConf(conf);
692
20.9k
  d_confs.push_back(nConf);
693
20.9k
  return conf->getId();
694
20.9k
}
695
696
#ifdef RDK_USE_BOOST_SERIALIZATION
697
template <class Archive>
698
0
void ROMol::save(Archive &ar, const unsigned int) const {
699
0
  std::string pkl;
700
0
  MolPickler::pickleMol(*this, pkl, PicklerOps::AllProps);
701
0
  ar << pkl;
702
0
}
703
704
template <class Archive>
705
0
void ROMol::load(Archive &ar, const unsigned int) {
706
0
  std::string pkl;
707
0
  ar >> pkl;
708
709
0
  delete dp_ringInfo;
710
0
  initMol();
711
712
0
  numBonds = 0;
713
0
  MolPickler::molFromPickle(pkl, *this, PicklerOps::AllProps);
714
0
  numBonds = rdcast<unsigned int>(boost::num_edges(d_graph));
715
0
}
716
717
template RDKIT_GRAPHMOL_EXPORT void ROMol::save<boost::archive::text_oarchive>(
718
    boost::archive::text_oarchive &, const unsigned int) const;
719
template RDKIT_GRAPHMOL_EXPORT void ROMol::load<boost::archive::text_iarchive>(
720
    boost::archive::text_iarchive &, const unsigned int);
721
#endif
722
723
}  // namespace RDKit