/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 |