Coverage Report

Created: 2026-11-12 07:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/rdkit/Code/GraphMol/Bond.h
Line
Count
Source
1
//
2
//  Copyright (C) 2001-2021 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 <RDGeneral/export.h>
11
#ifndef RD_BOND_H
12
#define RD_BOND_H
13
14
// std stuff
15
#include <memory>
16
#include <utility>
17
18
// Ours
19
#include <RDGeneral/Invariant.h>
20
#include <Query/QueryObjects.h>
21
#include <RDGeneral/types.h>
22
#include <RDGeneral/RDProps.h>
23
#include <GraphMol/details.h>
24
#include <GraphMol/MacroBondInfo.h>
25
26
namespace RDKit {
27
class ROMol;
28
class RWMol;
29
class Atom;
30
31
//! class for representing a bond
32
/*!
33
34
  <b>Notes:</b>
35
    - many of the methods of Atom require that the Atom be associated
36
      with a molecule (an ROMol).
37
    - each Bond maintains a Dict of \c properties:
38
        - Each \c property is keyed by name and can store an
39
          arbitrary type.
40
        - \c Properties can be marked as \c calculated, in which case
41
          they will be cleared when the \c clearComputedProps() method
42
          is called.
43
        - Because they have no impact upon chemistry, all \c property
44
          operations are \c const, this allows extra flexibility for
45
          clients who need to store extra data on Bond objects.
46
47
*/
48
class RDKIT_GRAPHMOL_EXPORT Bond : public RDProps {
49
  friend class RWMol;
50
  friend class ROMol;
51
52
 public:
53
  // FIX: grn...
54
  using QUERYBOND_QUERY = Queries::Query<int, Bond const *, true>;
55
56
  //! the type of Bond
57
  enum BondType : std::uint8_t {
58
    UNSPECIFIED = 0,
59
    SINGLE,
60
    DOUBLE,
61
    TRIPLE,
62
    QUADRUPLE,
63
    QUINTUPLE,
64
    HEXTUPLE,
65
    ONEANDAHALF,
66
    TWOANDAHALF,
67
    THREEANDAHALF,
68
    FOURANDAHALF,
69
    FIVEANDAHALF,
70
    AROMATIC,
71
    IONIC,
72
    HYDROGEN,
73
    THREECENTER,
74
    DATIVEONE,  //!< one-electron dative (e.g. from a C in a Cp ring to a metal)
75
    DATIVE,     //!< standard two-electron dative
76
    DATIVEL,    //!< standard two-electron dative
77
    DATIVER,    //!< standard two-electron dative
78
    OTHER,
79
    ZERO  //!< Zero-order bond (from
80
    // http://pubs.acs.org/doi/abs/10.1021/ci200488k)
81
  };
82
83
  //! the bond's direction (for chirality)
84
  enum BondDir : std::uint8_t {
85
    NONE = 0,    //!< no special style
86
    BEGINWEDGE,  //!< wedged: narrow at begin
87
    BEGINDASH,   //!< dashed: narrow at begin
88
    // FIX: this may not really be adequate
89
    ENDDOWNRIGHT,  //!< for cis/trans
90
    ENDUPRIGHT,    //!<  ditto
91
    EITHERDOUBLE,  //!< a "crossed" double bond
92
    UNKNOWN,       //!< intentionally unspecified stereochemistry
93
  };
94
95
  //! the nature of the bond's stereochem (for cis/trans)
96
  enum BondStereo : std::uint8_t {  // stereochemistry of double bonds
97
    STEREONONE = 0,                 // no special style
98
    STEREOANY,                      // intentionally unspecified
99
    // -- Put any true specifications about this point so
100
    // that we can do comparisons like if(bond->getStereo()>Bond::STEREOANY)
101
    STEREOZ,         // Z double bond
102
    STEREOE,         // E double bond
103
    STEREOCIS,       // cis double bond
104
    STEREOTRANS,     // trans double bond
105
    STEREOATROPCW,   //  atropisomer clockwise rotation
106
    STEREOATROPCCW,  //  atropisomer counter clockwise rotation
107
  };
108
109
  Bond();
110
  //! construct with a particular BondType
111
  explicit Bond(BondType bT);
112
  Bond(const Bond &other);
113
  virtual ~Bond();
114
  Bond &operator=(const Bond &other);
115
116
0
  Bond(Bond &&o) noexcept : RDProps(std::move(o)) {
117
0
    df_isAromatic = o.df_isAromatic;
118
0
    df_isConjugated = o.df_isConjugated;
119
0
    d_bondType = o.d_bondType;
120
0
    d_dirTag = o.d_dirTag;
121
0
    d_stereo = o.d_stereo;
122
0
    d_index = o.d_index;
123
0
    d_beginAtomIdx = o.d_beginAtomIdx;
124
0
    d_endAtomIdx = o.d_endAtomIdx;
125
0
    // NOTE: this is somewhat fraught for bonds associated with molecules since
126
0
    // the molecule will still be pointing to the original object
127
0
    dp_mol = std::exchange(o.dp_mol, nullptr);
128
0
    dp_stereoAtoms = std::exchange(o.dp_stereoAtoms, nullptr);
129
0
    dp_macroBondInfo = std::move(o.dp_macroBondInfo);
130
0
    d_flags = std::exchange(o.d_flags, 0);
131
0
  }
132
0
  Bond &operator=(Bond &&o) noexcept {
133
0
    if (this == &o) {
134
0
      return *this;
135
0
    }
136
0
    RDProps::operator=(std::move(o));
137
0
    df_isAromatic = o.df_isAromatic;
138
0
    df_isConjugated = o.df_isConjugated;
139
0
    d_bondType = o.d_bondType;
140
0
    d_dirTag = o.d_dirTag;
141
0
    d_stereo = o.d_stereo;
142
0
    d_index = o.d_index;
143
0
    d_beginAtomIdx = o.d_beginAtomIdx;
144
0
    d_endAtomIdx = o.d_endAtomIdx;
145
0
    // NOTE: this is somewhat fraught for bonds associated with molecules since
146
0
    // the molecule will still be pointing to the original object
147
0
    delete dp_stereoAtoms;
148
0
    dp_mol = std::exchange(o.dp_mol, nullptr);
149
0
    dp_stereoAtoms = std::exchange(o.dp_stereoAtoms, nullptr);
150
0
    dp_macroBondInfo = std::move(o.dp_macroBondInfo);
151
0
    d_flags = std::exchange(o.d_flags, 0);
152
0
    return *this;
153
0
  }
154
155
  //! returns a copy
156
  /*!
157
    <b>Note:</b> the caller is responsible for <tt>delete</tt>ing
158
     the returned pointer.
159
  */
160
  virtual Bond *copy() const;
161
162
  //! returns our \c bondType
163
191M
  BondType getBondType() const { return static_cast<BondType>(d_bondType); }
164
  //! sets our \c bondType
165
3.91M
  void setBondType(BondType bT) { d_bondType = bT; }
166
  //! \brief returns our \c bondType as a double
167
  //!   (e.g. SINGLE->1.0, AROMATIC->1.5, etc.)
168
  double getBondTypeAsDouble() const;
169
170
  //! returns our contribution to the explicit valence of an Atom
171
  /*!
172
    <b>Notes:</b>
173
      - requires an owning molecule
174
  */
175
  virtual double getValenceContrib(const Atom *at) const;
176
177
  //! sets our \c isAromatic flag
178
4.34M
  void setIsAromatic(bool what) { df_isAromatic = what; }
179
  //! returns the status of our \c isAromatic flag
180
79.7M
  bool getIsAromatic() const { return df_isAromatic; }
181
182
  //! sets our \c isConjugated flag
183
2.90M
  void setIsConjugated(bool what) { df_isConjugated = what; }
184
  //! returns the status of our \c isConjugated flag
185
390k
  bool getIsConjugated() const { return df_isConjugated; }
186
187
  //! returns whether or not this instance belongs to a molecule
188
4.62M
  bool hasOwningMol() const { return dp_mol != nullptr; }
189
190
  //! returns a reference to the ROMol that owns this instance
191
25.9M
  ROMol &getOwningMol() const {
192
25.9M
    PRECONDITION(dp_mol, "no owner");
193
25.9M
    return *dp_mol;
194
25.9M
  }
195
  //! sets our owning molecule
196
  void setOwningMol(ROMol *other);
197
  //! sets our owning molecule
198
0
  void setOwningMol(ROMol &other) { setOwningMol(&other); }
199
200
  // inverts the chirality of an atropisomer
201
  bool invertChirality();
202
203
  //! returns our index within the ROMol
204
  /*!
205
    <b>Notes:</b>
206
      - this makes no sense if we do not have an owning molecule
207
208
  */
209
39.6M
  unsigned int getIdx() const { return d_index; }
210
  //! sets our index within the ROMol
211
  /*!
212
    <b>Notes:</b>
213
      - this makes no sense if we do not have an owning molecule
214
      - the index should be <tt>< this->getOwningMol()->getNumBonds()</tt>
215
  */
216
20.9M
  void setIdx(unsigned int index) { d_index = index; }
217
218
  //! returns the index of our begin Atom
219
  /*!
220
    <b>Notes:</b>
221
      - this makes no sense if we do not have an owning molecule
222
  */
223
30.9M
  unsigned int getBeginAtomIdx() const { return d_beginAtomIdx; }
224
225
  //! returns the index of our end Atom
226
  /*!
227
    <b>Notes:</b>
228
      - this makes no sense if we do not have an owning molecule
229
  */
230
31.7M
  unsigned int getEndAtomIdx() const { return d_endAtomIdx; }
231
232
  //! given the index of one Atom, returns the index of the other
233
  /*!
234
    <b>Notes:</b>
235
      - this makes no sense if we do not have an owning molecule
236
  */
237
  unsigned int getOtherAtomIdx(unsigned int thisIdx) const;
238
239
  //! sets the index of our begin Atom
240
  /*!
241
    <b>Notes:</b>
242
      - requires an owning molecule
243
  */
244
  void setBeginAtomIdx(unsigned int what);
245
  //! sets the index of our end Atom
246
  /*!
247
    <b>Notes:</b>
248
      - requires an owning molecule
249
  */
250
  void setEndAtomIdx(unsigned int what);
251
252
  //! sets our begin Atom
253
  /*!
254
    <b>Notes:</b>
255
      - requires an owning molecule
256
  */
257
  void setBeginAtom(Atom *at);
258
  //! sets our end Atom
259
  /*!
260
    <b>Notes:</b>
261
      - requires an owning molecule
262
  */
263
  void setEndAtom(Atom *at);
264
265
  //! returns a pointer to our begin Atom
266
  /*!
267
    <b>Notes:</b>
268
      - requires an owning molecule
269
  */
270
  Atom *getBeginAtom() const;
271
  //! returns a pointer to our end Atom
272
  /*!
273
    <b>Notes:</b>
274
      - requires an owning molecule
275
  */
276
  Atom *getEndAtom() const;
277
  //! returns a pointer to the other Atom
278
  /*!
279
    <b>Notes:</b>
280
      - requires an owning molecule
281
  */
282
  Atom *getOtherAtom(Atom const *what) const;
283
284
  // ------------------------------------
285
  // Please see the note in Atom.h for some explanation
286
  // of these methods
287
  // ------------------------------------
288
289
  // This method can be used to distinguish query bonds from standard bonds
290
23.5M
  virtual bool hasQuery() const { return false; }
291
292
  // FIX: the const crap here is all mucked up.
293
  //! NOT CALLABLE
294
  virtual void setQuery(QUERYBOND_QUERY *what);
295
  //! NOT CALLABLE
296
  virtual QUERYBOND_QUERY *getQuery() const;
297
298
  //! NOT CALLABLE
299
  virtual void expandQuery(
300
      QUERYBOND_QUERY *what,
301
      Queries::CompositeQueryType how = Queries::COMPOSITE_AND,
302
      bool maintainOrder = true);
303
304
  //! returns whether or not we match the argument
305
  /*!
306
      <b>Notes:</b>
307
        - for Bond objects, "match" means that either one of the Bonds
308
          has \c bondType Bond::UNSPECIFIED or both Bonds have the
309
          same \c bondType.
310
  */
311
  virtual bool Match(Bond const *what) const;
312
313
  //! sets our direction
314
854k
  void setBondDir(BondDir what) { d_dirTag = what; }
315
  //! returns our direction
316
130M
  BondDir getBondDir() const { return static_cast<BondDir>(d_dirTag); }
317
318
  //! sets our stereo code
319
  /*!
320
      STEREONONE, STEREOANY, STEREOE and STEREOZ can be set without
321
      neighboring atoms specified in getStereoAtoms since they are
322
      defined by the topology of the molecular graph. In order to set
323
      STEREOCIS or STEREOTRANS the neighboring atoms must be set first
324
      (using setStereoBonds()) to know what atoms are being considered.
325
326
      <b>Notes:</b>
327
        - MolOps::findPotentialStereoBonds can be used to set
328
          getStereoAtoms before setting CIS/TRANS
329
  */
330
534k
  void setStereo(BondStereo what) {
331
534k
    PRECONDITION(((what != STEREOCIS && what != STEREOTRANS) ||
332
534k
                  getStereoAtoms().size() == 2),
333
534k
                 "Stereo atoms should be specified before specifying CIS/TRANS "
334
534k
                 "bond stereochemistry")
335
534k
    d_stereo = what;
336
534k
  }
337
  //! returns our stereo code
338
15.9M
  BondStereo getStereo() const { return static_cast<BondStereo>(d_stereo); }
339
340
  //! sets the atoms to be considered as reference points for bond stereo
341
  /*!
342
      These do not necessarily need to be the highest 'ranking' atoms
343
      like CIP stereo requires. They can be any arbitrary atoms
344
      neighboring the begin and end atoms of this bond
345
      respectively. STEREOCIS or STEREOTRANS is then set relative to
346
      only these atoms.
347
348
      If CIP rankings are desired, use
349
      MolOps::findPotentialStereoBonds, but this is a more costly
350
      function as it takes the whole molecule topology into account.
351
  */
352
  void setStereoAtoms(unsigned int bgnIdx, unsigned int endIdx);
353
354
  //! returns the indices of our stereo atoms
355
12
  const INT_VECT &getStereoAtoms() const {
356
12
    if (!dp_stereoAtoms) {
357
0
      const_cast<Bond *>(this)->dp_stereoAtoms = new INT_VECT();
358
0
    }
359
12
    return *dp_stereoAtoms;
360
12
  }
361
  //! \overload
362
18.3M
  INT_VECT &getStereoAtoms() {
363
18.3M
    if (!dp_stereoAtoms) {
364
1.06M
      dp_stereoAtoms = new INT_VECT();
365
1.06M
    }
366
18.3M
    return *dp_stereoAtoms;
367
18.3M
  }
368
369
0
  MacroBondInfo *getMacroBondInfo() { return dp_macroBondInfo.get(); }
370
0
  const MacroBondInfo *getMacroBondInfo() const {
371
0
    return dp_macroBondInfo.get();
372
0
  }
373
  //! takes ownership of the pointer
374
  void setMacroBondInfo(MacroBondInfo *info);
375
376
  //! calculates any of our lazy \c properties
377
  /*!
378
    <b>Notes:</b>
379
      - requires an owning molecule
380
  */
381
4.99M
  void updatePropertyCache(bool strict = true) { (void)strict; }
382
383
  //! Flags that can be used by to store information on bonds.
384
  //!   These are not serialized and should be treated as temporary values.
385
  //!   No guarantees are made about preserving these flags across library
386
  //!   calls.
387
0
  void setFlags(std::uint64_t flags) { d_flags = flags; }
388
0
  std::uint64_t getFlags() const { return d_flags; }
389
0
  std::uint64_t &getFlags() { return d_flags; }
390
391
 protected:
392
  //! sets our owning molecule
393
  /// void setOwningMol(ROMol *other);
394
  //! sets our owning molecule
395
  /// void setOwningMol(ROMol &other) { setOwningMol(&other); }
396
  ROMol *dp_mol;
397
  INT_VECT *dp_stereoAtoms;
398
  std::unique_ptr<MacroBondInfo> dp_macroBondInfo;
399
  atomindex_t d_index;
400
  atomindex_t d_beginAtomIdx, d_endAtomIdx;
401
  bool df_isAromatic;
402
  bool df_isConjugated;
403
  std::uint8_t d_bondType;
404
  std::uint8_t d_dirTag;
405
  std::uint8_t d_stereo;
406
  std::uint64_t d_flags = 0;
407
408
  void initBond();
409
};
410
411
4.37M
inline bool isDative(const Bond::BondType bt) {
412
4.37M
  return bt == Bond::BondType::DATIVE || bt == Bond::BondType::DATIVEL ||
413
4.36M
         bt == Bond::BondType::DATIVER || bt == Bond::BondType::DATIVEONE;
414
4.37M
}
415
416
2.74M
inline bool isDative(const Bond &bond) {
417
2.74M
  auto bt = bond.getBondType();
418
2.74M
  return isDative(bt);
419
2.74M
}
420
421
0
inline bool canSetDoubleBondStereo(const Bond &bond) {
422
0
  auto bondType = bond.getBondType();
423
0
  return (bondType == Bond::SINGLE || bondType == Bond::AROMATIC ||
424
0
          isDative(bond));
425
0
}
426
427
2.38M
inline bool canHaveDirection(const Bond &bond) {
428
2.38M
  auto bondType = bond.getBondType();
429
2.38M
  return (bondType == Bond::SINGLE || bondType == Bond::AROMATIC);
430
2.38M
}
431
432
//! returns twice the \c bondType
433
//! (e.g. SINGLE->2, AROMATIC->3, etc.)
434
RDKIT_GRAPHMOL_EXPORT extern uint8_t getTwiceBondType(const RDKit::Bond &b);
435
436
};  // namespace RDKit
437
438
//! allows Bond objects to be dumped to streams
439
RDKIT_GRAPHMOL_EXPORT extern std::ostream &operator<<(std::ostream &target,
440
                                                      const RDKit::Bond &b);
441
442
#endif