RDKit
Open-source cheminformatics and machine learning.
Loading...
Searching...
No Matches
EmbeddedFrag.h
Go to the documentation of this file.
1//
2// Copyright (C) 2003-2022 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_EMBEDDED_FRAG_H
12#define RD_EMBEDDED_FRAG_H
13
14#include <RDGeneral/types.h>
16#include <Geometry/point.h>
17#include "DepictUtils.h"
18#include <boost/smart_ptr.hpp>
19
20namespace RDKit {
21class ROMol;
22class Bond;
23} // namespace RDKit
24
25namespace RDDepict {
26typedef boost::shared_array<double> DOUBLE_SMART_PTR;
27
28//! Class that contains the data for an atoms that has already been embedded
30 public:
31 typedef enum {
35 } EAtomType;
36
37 EmbeddedAtom() { neighs.clear(); }
38
39 EmbeddedAtom(const EmbeddedAtom &other) = default;
40
41 EmbeddedAtom(unsigned int aid, const RDGeom::Point2D &pos)
42 : aid(aid),
43 angle(-1.0),
44 nbr1(-1),
45 nbr2(-1),
46 CisTransNbr(-1),
47 ccw(true),
48 rotDir(0),
49 d_density(-1.0),
50 df_fixed(false) {
51 loc = pos;
52 }
53
55 if (this == &other) {
56 return *this;
57 }
58
59 loc = other.loc;
60 angle = other.angle;
61 nbr1 = other.nbr1;
62 nbr2 = other.nbr2;
64 rotDir = other.rotDir;
65 normal = other.normal;
66 ccw = other.ccw;
67 neighs = other.neighs;
68 d_density = other.d_density;
69 df_fixed = other.df_fixed;
70 return *this;
71 }
72
73 void Transform(const RDGeom::Transform2D &trans) {
74 RDGeom::Point2D temp = loc + normal;
75 trans.TransformPoint(loc);
76 trans.TransformPoint(temp);
77 normal = temp - loc;
78 }
79
80 void Reflect(const RDGeom::Point2D &loc1, const RDGeom::Point2D &loc2) {
81 RDGeom::Point2D temp = loc + normal;
82 loc = reflectPoint(loc, loc1, loc2);
83 temp = reflectPoint(temp, loc1, loc2);
84 normal = temp - loc;
85 ccw = (!ccw);
86 }
87
88 unsigned int aid{0}; // the id of the atom
89
90 //! the angle that is already takes at this atom, so any new atom attaching to
91 /// this atom with have to fall in the available part
92 double angle{-1.0};
93
94 //! the first neighbor of this atom that form the 'angle'
95 int nbr1{-1};
96
97 //! the second neighbor of atom that from the 'angle'
98 int nbr2{-1};
99
100 //! is this is a cis/trans atom the neighbor of this atom that is involved in
101 /// the cis/trans system - defaults to -1
102 int CisTransNbr{-1};
103
104 //! which direction do we rotate this normal to add the next bond
105 //! if ccw is true we rotate counter clockwise, otherwise rotate clock wise,
106 /// by an angle that is <= PI/2
107 bool ccw{true};
108
109 //! rotation direction around this atom when adding new atoms,
110 /// we determine this for the first neighbor and stick to this direction
111 /// after that
112 //! useful only on atoms that are degree >= 4
113 int rotDir{0};
114
115 RDGeom::Point2D loc; // the current location of this atom
116 //! this is a normal vector to one of the bonds that added this atom
117 //! it provides the side on which we want to add a new bond to this atom
118 //! this is only relevant when we are dealing with non ring atoms. We would
119 /// like to draw chains in a zig-zag manner
121
122 //! and these are the atom IDs of the neighbors that still need to be embedded
124
125 // density of the atoms around this atoms
126 // - this is sum of inverse of the square of distances to other atoms from
127 // this atom. Used in the collision removal code
128 // - initialized to -1.0
129 double d_density{-1.0};
130
131 //! if set this atom is fixed: further operations on the fragment may not
132 //! move it.
133 bool df_fixed{false};
134};
135
136typedef std::map<unsigned int, EmbeddedAtom> INT_EATOM_MAP;
137typedef INT_EATOM_MAP::iterator INT_EATOM_MAP_I;
138typedef INT_EATOM_MAP::const_iterator INT_EATOM_MAP_CI;
139
140//! Class containing a fragment of a molecule that has already been embedded
141/*
142 Here is how this class is designed to be used
143 - find a set of fused rings and compute the coordinates for the atoms in those
144 ring
145 - them grow this system either by adding non ring neighbors
146 - or by adding other embedded fragment
147 - so at the end of the process the whole molecule end up being one these
148 embedded frag objects
149*/
151 // REVIEW: think about moving member functions up to global level and just
152 // using
153 // this class as a container
154
155 public:
156 //! Default constructor
158 d_eatoms.clear();
159 d_attachPts.clear();
160 }
161
162 //! Initializer from a single atom id
163 /*!
164 A single Embedded Atom with this atom ID is added and placed at the origin
165 */
166 EmbeddedFrag(unsigned int aid, const RDKit::ROMol *mol);
167
168 //! Constructor when the coordinates have been specified for a set of atoms
169 /*!
170 This simply initialized a set of EmbeddedAtom to have the same coordinates
171 as the one's specified. No testing is done to verify any kind of
172 correctness. Also this fragment is less ready (to expand and add new
173 neighbors) than when using other constructors. This is because:
174 - the user may have specified coords for only a part of the atoms in a
175 fused ring systems in which case we need to find these atoms and merge
176 these ring systems to this fragment
177 - The atoms are not yet aware of their neighbor (what is left to add etc.)
178 this again depends on atoms properly so that new neighbors can be added
179 to them
180 */
182 const RDGeom::INT_POINT2D_MAP &coordMap);
183
184 //! Initializer from a set of fused rings
185 /*!
186 ARGUMENTS:
187 \param mol the molecule of interest
188 \param fusedRings a vector of rings, each ring is a list of atom ids
189 \param useRingTemplates whether to use ring system templates for generating
190 initial coordinates
191 */
192 EmbeddedFrag(const RDKit::ROMol *mol, const RDKit::VECT_INT_VECT &fusedRings,
193 bool useRingTemplates);
194
195 //! Initializer for a cis/trans system using the double bond
196 /*!
197 ARGUMENTS:
198 \param dblBond the double bond that is involved in the cis/trans
199 configuration
200 */
201 explicit EmbeddedFrag(const RDKit::Bond *dblBond);
202
203 //! Expand this embedded system by adding neighboring atoms or other embedded
204 /// systems
205 /*!
206
207 Note that both nratms and efrags are modified in this function
208 as we start merging them with the current fragment
209
210 */
211 void expandEfrag(RDKit::INT_LIST &nratms, std::list<EmbeddedFrag> &efrags);
212
213 //! Add a new non-ring atom to this object
214 /*
215 ARGUMENTS:
216 \param aid ID of the atom to be added
217 \param toAid ID of the atom that is already in this object to which this
218 atom is added
219 */
220 void addNonRingAtom(unsigned int aid, unsigned int toAid);
221
222 //! Merge this embedded object with another embedded fragment
223 /*!
224
225 The transformation (rotation + translation required to attached
226 the passed in object will be computed and applied. The
227 coordinates of the atoms in this object will remain fixed We
228 will assume that there are no common atoms between the two
229 fragments to start with
230
231 ARGUMENTS:
232 \param embObj another EmbeddedFrag object to be merged with this object
233 \param toAid the atom in this embedded fragment to which the new object
234 will be attached
235 \param nbrAid the atom in the other fragment to attach to
236 */
237 void mergeNoCommon(EmbeddedFrag &embObj, unsigned int toAid,
238 unsigned int nbrAid);
239
240 //! Merge this embedded object with another embedded fragment
241 /*!
242
243 The transformation (rotation + translation required to attached
244 the passed in object will be computed and applied. The
245 coordinates of the atoms in this object will remain fixed This
246 already know there are a atoms in common and we will use them to
247 merge things
248
249 ARGUMENTS:
250 \param embObj another EmbeddedFrag object to be merged with this object
251 \param commAtms a vector of ids of the common atoms
252
253 */
255
256 void mergeFragsWithComm(std::list<EmbeddedFrag> &efrags);
257
258 //! Mark this fragment to be done for final embedding
259 void markDone() { d_done = true; }
260
261 //! If this fragment done for the final embedding
262 bool isDone() { return d_done; }
263
264 //! Get the molecule that this embedded fragment belongs to
265 const RDKit::ROMol *getMol() const { return dp_mol; }
266
267 //! Find the common atom ids between this fragment and a second one
269
270 //! Find a neighbor to a non-ring atom among the already embedded atoms
271 /*!
272 ARGUMENTS:
273 \param aid the atom id of interest
274
275 RETURNS:
276 \return the id of the atom if we found a neighbor
277 -1 otherwise
278
279 NOTE: by definition we can have only one neighbor in the embedded system.
280 */
281 int findNeighbor(unsigned int aid);
282
283 //! Transform this object to a new coordinates system
284 /*!
285 ARGUMENTS:
286 \param trans : the transformation that need to be applied to the atoms in
287 this object
288 */
289 void Transform(const RDGeom::Transform2D &trans);
290
291 void Reflect(const RDGeom::Point2D &loc1, const RDGeom::Point2D &loc2);
292
293 const INT_EATOM_MAP &GetEmbeddedAtoms() const { return d_eatoms; }
294
295 void Translate(const RDGeom::Point2D &shift) {
296 INT_EATOM_MAP_I eari;
297 for (eari = d_eatoms.begin(); eari != d_eatoms.end(); eari++) {
298 eari->second.loc += shift;
299 }
300 }
301
302 EmbeddedAtom GetEmbeddedAtom(unsigned int aid) const {
303 INT_EATOM_MAP_CI posi = d_eatoms.find(aid);
304 if (posi == d_eatoms.end()) {
305 PRECONDITION(0, "Embedded atom does not contain embedded atom specified");
306 }
307 return posi->second;
308 }
309
310 //! the number of atoms in the embedded system
311 int Size() const { return d_eatoms.size(); }
312
313 //! \brief compute a box that encloses the fragment
315
316 //! \brief Flip atoms on one side of a bond - used in removing collisions
317 /*!
318 ARGUMENTS:
319 \param bondId - the bond used as the mirror to flip
320 \param flipEnd - flip the atoms at the end of the bond
321
322 */
323 void flipAboutBond(unsigned int bondId, bool flipEnd = true);
324
325 void openAngles(const double *dmat, unsigned int aid1, unsigned int aid2);
326
327 std::vector<PAIR_I_I> findCollisions(const double *dmat,
328 bool includeBonds = 1);
329
331
333 double mimicDmatWt);
334
335 void permuteBonds(unsigned int aid, unsigned int aid1, unsigned int aid2);
336
337 void randomSampleFlipsAndPermutations(unsigned int nBondsPerSample = 3,
338 unsigned int nSamples = 100,
339 int seed = 100,
340 const DOUBLE_SMART_PTR *dmat = nullptr,
341 double mimicDmatWt = 0.0,
342 bool permuteDeg4Nodes = false);
343
344 //! Remove collisions in a structure by flipping rotatable bonds
345 //! along the shortest path between two colliding atoms
347
348 //! Remove collision by opening angles at the offending atoms
350
351 //! Remove collisions by shortening bonds along the shortest path between the
352 /// atoms
354
355 //! helpers functions to
356
357 //! \brief make list of neighbors for each atom in the embedded system that
358 //! still need to be embedded
360
361 //! update the unembedded neighbor atom list for a specified atom
362 void updateNewNeighs(unsigned int aid);
363
364 //! \brief Find all atoms in this embedded system that are
365 //! within a specified distant of a point
366 int findNumNeigh(const RDGeom::Point2D &pt, double radius);
367
368 inline double getBoxPx() { return d_px; }
369 inline double getBoxNx() { return d_nx; }
370 inline double getBoxPy() { return d_py; }
371 inline double getBoxNy() { return d_ny; }
372
374
375 private:
376 double totalDensity();
377
378 // returns true if fused rings found a template
379 bool matchToTemplate(const RDKit::INT_VECT &ringSystemAtoms);
380
381 void embedFusedRings(const RDKit::VECT_INT_VECT &fusedRings,
382 bool useRingTemplates);
383
384 void setupAttachmentPoints();
385
386 //! \brief Find a transform to join a ring to the current embedded frag when
387 /// we
388 //! have only on common atom
389 /*!
390 So this is the state of affairs assumed here:
391 - we already have some rings in the fused system embedded and the
392 coordinates for the atoms
393 - the coordinates for the atoms in the new ring (with the center
394 of rings at the origin) are available nringCors. we want to
395 translate and rotate this ring to join with the already
396 embeded rings.
397 - only one atom is common between this new ring and the atoms
398 that are already embedded
399 - so we need to compute a transform that includes a translation
400 so that the common atom overlaps and the rotation to minimize
401 overlap with other atoms.
402
403 Here's what is done:
404 - we bisect the remaining sweep angle at the common atom and
405 attach the new ring such that the center of the new ring falls
406 on this bisecting line
407
408 NOTE: It is assumed here that the original coordinates for the
409 new ring are such that the center is at the origin (this is the
410 way rings come out of embedRing)
411 */
412 RDGeom::Transform2D computeOneAtomTrans(unsigned int commAid,
413 const EmbeddedFrag &other);
414
415 RDGeom::Transform2D computeTwoAtomTrans(
416 unsigned int aid1, unsigned int aid2,
417 const RDGeom::INT_POINT2D_MAP &nringCor);
418
419 //! Merge a ring with already embedded atoms
420 /*!
421 It is assumed that the new rings has already been oriented
422 correctly etc. This function just update all the relevant data,
423 like the neighbor information and the sweep angle
424 */
425 void mergeRing(const EmbeddedFrag &embRing, unsigned int nCommon,
426 const RDKit::INT_VECT &pinAtoms);
427
428 //! Reflect a fragment if necessary through a line connecting two atoms
429 /*!
430
431 We want add the new fragment such that, most of its atoms fall
432 on the side opposite to where the atoms already embedded are aid1
433 and aid2 give the atoms that were used to align the new ring to
434 the embedded atoms and we will assume that that process has
435 already taken place (i.e. transformRing has been called)
436
437 */
438 void reflectIfNecessaryDensity(EmbeddedFrag &embFrag, unsigned int aid1,
439 unsigned int aid2);
440
441 //! Reflect a fragment if necessary based on the cis/trans specification
442 /*!
443
444 we want to add the new fragment such that the cis/trans
445 specification on bond between aid1 and aid2 is not violated. We
446 will assume that aid1 and aid2 from this fragments as well as
447 embFrag are already aligned to each other.
448
449 \param embFrag the fragment that will be reflected if necessary
450 \param ctCase which fragment if the cis/trans dbl bond
451 - 1 means embFrag is the cis/trans fragment
452 - 2 mean "this" is the cis/trans fragment
453 \param aid1 first atom that forms the plane (line) of reflection
454 \param aid2 second atom that forms the plane of reflection
455 */
456 void reflectIfNecessaryCisTrans(EmbeddedFrag &embFrag, unsigned int ctCase,
457 unsigned int aid1, unsigned int aid2);
458
459 //! Reflect a fragment if necessary based on a third common point
460 /*!
461
462 we want add the new fragment such that the third point falls on
463 the same side of aid1 and aid2. We will assume that aid1 and
464 aid2 from this fragments as well as embFrag are already aligned
465 to each other.
466
467 */
468 void reflectIfNecessaryThirdPt(EmbeddedFrag &embFrag, unsigned int aid1,
469 unsigned int aid2, unsigned int aid3);
470
471 //! \brief Initialize this fragment from a ring and coordinates for its atoms
472 /*!
473 ARGUMENTS:
474 /param ring a vector of atom ids in the ring; it is assumed that there
475 in
476 clockwise or anti-clockwise order
477 /param nringMap a map of atomId to coordinate map for the atoms in the ring
478 */
479 void initFromRingCoords(const RDKit::INT_VECT &ring,
480 const RDGeom::INT_POINT2D_MAP &nringMap);
481
482 //! Helper function to addNonRingAtom to a specified atoms in the fragment
483 /*
484 Add an atom to this embedded fragment when the fragment already
485 has at least two neighbors previously added to 'toAid'. In this
486 case we have to choose where the new neighbor goes based on
487 the angle that is already taken around the atom.
488
489 ARGUMENTS:
490 \param aid ID of the atom to be added
491 \param toAid ID of the atom that is already in this object to which this
492 atom is added
493 */
494 void addAtomToAtomWithAng(unsigned int aid, unsigned int toAid);
495
496 //! Helper function to addNonRingAtom to a specified atoms in the fragment
497 /*!
498
499 Add an atom (aid) to an atom (toAid) in this embedded fragment
500 when 'toAid' has one or no neighbors previously added. In this
501 case where the new atom should fall is determined by the degree
502 of 'toAid' and the congestion around it.
503
504 ARGUMENTS:
505 \param aid ID of the atom to be added
506 \param toAid ID of the atom that is already in this object to which this
507 atom is added
508 \param mol the molecule we are dealing with
509 */
510 void addAtomToAtomWithNoAng(
511 unsigned int aid,
512 unsigned int toAid); //, const RDKit::ROMol *mol);
513
514 //! Helper function to constructor that takes predefined coordinates
515 /*!
516
517 Given an atom with more than 2 neighbors all embedded in this
518 fragment this function tries to determine
519
520 - how much of an angle if left for any new neighbors yet to be
521 added
522 - which atom should we rotate when we add a new neighbor and in
523 which direction (clockwise or anticlockwise
524
525 This is how it works
526 - find the pair of nbrs that have the largest angle
527 - this will most likely be the angle that is available - unless
528 we have fused rings and we found on of the ring angle !!!! -
529 in this case we find the next best
530 - find the smallest angle that contains one of these nbrs -
531 this determined which
532 - way we want to rotate
533
534 ARGUMENTS:
535 \param aid the atom id where we are centered right now
536 \param doneNbrs list of neighbors that are already embedded around aid
537 */
538 void computeNbrsAndAng(unsigned int aid, const RDKit::INT_VECT &doneNbrs);
539 // const RDKit::ROMol *mol);
540
541 //! are we embedded with the final (molecule) coordinates
542 bool d_done = false;
543 double d_px = 0.0, d_nx = 0.0, d_py = 0.0, d_ny = 0.0;
544
545 //! a map that takes one from the atom id to the embeddedatom object for that
546 /// atom.
547 INT_EATOM_MAP d_eatoms;
548
549 // RDKit::INT_DEQUE d_attachPts;
550 RDKit::INT_LIST d_attachPts;
551
552 // pointer to the owning molecule
553 const RDKit::ROMol *dp_mol = nullptr;
554};
555} // namespace RDDepict
556
557#endif
#define PRECONDITION(expr, mess)
Definition Invariant.h:108
Class that contains the data for an atoms that has already been embedded.
EmbeddedAtom(const EmbeddedAtom &other)=default
int nbr1
the first neighbor of this atom that form the 'angle'
RDKit::INT_VECT neighs
and these are the atom IDs of the neighbors that still need to be embedded
RDGeom::Point2D normal
int nbr2
the second neighbor of atom that from the 'angle'
RDGeom::Point2D loc
EmbeddedAtom & operator=(const EmbeddedAtom &other)
EmbeddedAtom(unsigned int aid, const RDGeom::Point2D &pos)
void Transform(const RDGeom::Transform2D &trans)
void Reflect(const RDGeom::Point2D &loc1, const RDGeom::Point2D &loc2)
EmbeddedAtom GetEmbeddedAtom(unsigned int aid) const
void Transform(const RDGeom::Transform2D &trans)
Transform this object to a new coordinates system.
void updateNewNeighs(unsigned int aid)
update the unembedded neighbor atom list for a specified atom
void markDone()
Mark this fragment to be done for final embedding.
EmbeddedFrag(const RDKit::ROMol *mol, const RDKit::VECT_INT_VECT &fusedRings, bool useRingTemplates)
Initializer from a set of fused rings.
void flipAboutBond(unsigned int bondId, bool flipEnd=true)
Flip atoms on one side of a bond - used in removing collisions.
std::vector< PAIR_I_I > findCollisions(const double *dmat, bool includeBonds=1)
int Size() const
the number of atoms in the embedded system
void mergeNoCommon(EmbeddedFrag &embObj, unsigned int toAid, unsigned int nbrAid)
Merge this embedded object with another embedded fragment.
EmbeddedFrag(const RDKit::ROMol *mol, const RDGeom::INT_POINT2D_MAP &coordMap)
Constructor when the coordinates have been specified for a set of atoms.
EmbeddedFrag()
Default constructor.
EmbeddedFrag(const RDKit::Bond *dblBond)
Initializer for a cis/trans system using the double bond.
RDKit::INT_VECT findCommonAtoms(const EmbeddedFrag &efrag2)
Find the common atom ids between this fragment and a second one.
void expandEfrag(RDKit::INT_LIST &nratms, std::list< EmbeddedFrag > &efrags)
int findNumNeigh(const RDGeom::Point2D &pt, double radius)
Find all atoms in this embedded system that are within a specified distant of a point.
void removeCollisionsOpenAngles()
Remove collision by opening angles at the offending atoms.
EmbeddedFrag(unsigned int aid, const RDKit::ROMol *mol)
Initializer from a single atom id.
double mimicDistMatAndDensityCostFunc(const DOUBLE_SMART_PTR *dmat, double mimicDmatWt)
void Translate(const RDGeom::Point2D &shift)
void addNonRingAtom(unsigned int aid, unsigned int toAid)
Add a new non-ring atom to this object.
void permuteBonds(unsigned int aid, unsigned int aid1, unsigned int aid2)
const RDKit::ROMol * getMol() const
Get the molecule that this embedded fragment belongs to.
void removeCollisionsShortenBonds()
void setupNewNeighs()
helpers functions to
void Reflect(const RDGeom::Point2D &loc1, const RDGeom::Point2D &loc2)
void computeBox()
compute a box that encloses the fragment
void openAngles(const double *dmat, unsigned int aid1, unsigned int aid2)
const INT_EATOM_MAP & GetEmbeddedAtoms() const
void mergeWithCommon(EmbeddedFrag &embObj, RDKit::INT_VECT &commAtms)
Merge this embedded object with another embedded fragment.
void randomSampleFlipsAndPermutations(unsigned int nBondsPerSample=3, unsigned int nSamples=100, int seed=100, const DOUBLE_SMART_PTR *dmat=nullptr, double mimicDmatWt=0.0, bool permuteDeg4Nodes=false)
bool isDone()
If this fragment done for the final embedding.
void mergeFragsWithComm(std::list< EmbeddedFrag > &efrags)
int findNeighbor(unsigned int aid)
Find a neighbor to a non-ring atom among the already embedded atoms.
void computeDistMat(DOUBLE_SMART_PTR &dmat)
void TransformPoint(Point2D &pt) const
class for representing a bond
Definition Bond.h:46
#define RDKIT_DEPICTOR_EXPORT
Definition export.h:97
boost::shared_array< double > DOUBLE_SMART_PTR
INT_EATOM_MAP::iterator INT_EATOM_MAP_I
RDKIT_DEPICTOR_EXPORT RDGeom::Point2D reflectPoint(const RDGeom::Point2D &point, const RDGeom::Point2D &loc1, const RDGeom::Point2D &loc2)
std::map< unsigned int, EmbeddedAtom > INT_EATOM_MAP
INT_EATOM_MAP::const_iterator INT_EATOM_MAP_CI
std::map< int, Point2D > INT_POINT2D_MAP
Definition point.h:556
Std stuff.
std::list< int > INT_LIST
Definition types.h:232
std::vector< int > INT_VECT
Definition types.h:226
std::vector< INT_VECT > VECT_INT_VECT
Definition types.h:240