Coverage Report

Created: 2026-06-23 06:55

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