00001 // 00002 // Copyright (C) 2003-2008 Greg Landrum and Rational Discovery LLC 00003 // 00004 // @@ All Rights Reserved @@ 00005 // 00006 #ifndef _RD_EMBEDDED_FRAG_H_ 00007 #define _RD_EMBEDDED_FRAG_H_ 00008 00009 #include <RDGeneral/types.h> 00010 #include <Geometry/Transform2D.h> 00011 #include <Geometry/point.h> 00012 #include "DepictUtils.h" 00013 #include <boost/smart_ptr.hpp> 00014 00015 00016 namespace RDKit { 00017 class ROMol; 00018 class Bond; 00019 } 00020 00021 namespace RDDepict { 00022 typedef boost::shared_array<double> DOUBLE_SMART_PTR; 00023 00024 //! Class that contains the data for an atoms that has alredy been embedded 00025 class EmbeddedAtom { 00026 public: 00027 typedef enum { 00028 UNSPECIFIED=0, 00029 CISTRANS, 00030 RING} EAtomType; 00031 00032 EmbeddedAtom() : aid(0), angle(-1.0), nbr1(-1), nbr2(-1), 00033 CisTransNbr(-1), ccw(true), rotDir(0), d_density(-1.0){ 00034 neighs.clear(); 00035 } 00036 00037 EmbeddedAtom(unsigned int aid, const RDGeom::Point2D &pos) : 00038 aid(aid), angle(-1.0), nbr1(-1), nbr2(-1), 00039 CisTransNbr(-1), ccw(true), rotDir(0), d_density(-1.0) { 00040 loc = pos; 00041 } 00042 00043 EmbeddedAtom& operator=(const EmbeddedAtom& other) { 00044 loc = other.loc; 00045 angle = other.angle; 00046 nbr1 = other.nbr1; 00047 nbr2 = other.nbr2; 00048 CisTransNbr = other.CisTransNbr; 00049 rotDir = other.rotDir; 00050 normal = other.normal; 00051 ccw = other.ccw; 00052 neighs = other.neighs; 00053 d_density = other.d_density; 00054 return *this; 00055 } 00056 00057 void Transform(const RDGeom::Transform2D &trans) { 00058 RDGeom::Point2D temp = loc + normal; 00059 trans.TransformPoint(loc); 00060 trans.TransformPoint(temp); 00061 normal = temp - loc; 00062 } 00063 00064 void Reflect(const RDGeom::Point2D &loc1, 00065 const RDGeom::Point2D &loc2) { 00066 RDGeom::Point2D temp = loc + normal; 00067 loc = reflectPoint(loc, loc1, loc2); 00068 temp = reflectPoint(temp, loc1, loc2); 00069 normal = temp - loc; 00070 ccw = (!ccw); 00071 } 00072 00073 unsigned int aid; // the id of the atom 00074 00075 //! the angle that is already takes at this atom, so any new atom attaching to this 00076 //! atom with have to fall in the available part 00077 double angle; 00078 00079 //! the first neighbor of this atom that form the 'angle' 00080 int nbr1; 00081 00082 //! the second neighbor of atom that from the 'angle' 00083 int nbr2; 00084 00085 //! is this is a cis/trans atom the neighbor of this atom that is involved in the 00086 //! cis/trans system - defaults to -1 00087 int CisTransNbr; 00088 00089 //! which direction do we rotate this normal to add the next bond 00090 //! if ccw is true we rotate counter cloack wise, otherwise rotate clock wise, by an angle that is 00091 //! <= PI/2 00092 bool ccw; 00093 00094 //! rotation direction around this atom when adding new atoms, 00095 //! we determine this for the first neighbor and stick to this direction after that 00096 //! useful only on atoms that are degree >= 4 00097 int rotDir; 00098 00099 RDGeom::Point2D loc; // the current location of this atom 00100 //! this is a normal vector to one of the bonds that added this atom 00101 //! it provides the side on which we want to add a new bond to this atom, 00102 //! this is only relevant when we are dealing with non ring atoms. We would like to draw chains in 00103 //! a zig-zag manner 00104 RDGeom::Point2D normal; 00105 00106 //! and these are the atom IDs of the neighbors that still need to be embedded 00107 RDKit::INT_VECT neighs; 00108 00109 // density of the atoms around this atoms 00110 // - this is sum of inverse of the square of distances to other atoms from this atom 00111 // Used in the collision removal code - initialized to -1.0 00112 double d_density; 00113 }; 00114 00115 00116 00117 typedef std::map<unsigned int, EmbeddedAtom> INT_EATOM_MAP; 00118 typedef INT_EATOM_MAP::iterator INT_EATOM_MAP_I; 00119 typedef INT_EATOM_MAP::const_iterator INT_EATOM_MAP_CI; 00120 00121 00122 //! Class containing a fragment of a molecule that has already been embedded 00123 /* 00124 Here is how this class is designed to be used 00125 - find a set of fused rings and compte the coordinates for the atoms in those ring 00126 - them grow this sytem either by adding non ring neighbors 00127 - or by adding other embedded fragment 00128 - so at the end of the process the whole molecule end up being one these embedded frag objects 00129 */ 00130 class EmbeddedFrag { 00131 // REVIEW: think about moving member functions up to global level and just using 00132 // this class as a container 00133 00134 public: 00135 //! Default constructor 00136 EmbeddedFrag() : d_done(false), dp_mol(0){}; 00137 00138 //! Intializer from a single atom id 00139 /*! 00140 A single Embedded Atom with this atom ID is added and placed at the origin 00141 */ 00142 EmbeddedFrag(unsigned int aid, const RDKit::ROMol *mol); 00143 00144 //! Constructor when the coordinates have been specified for a set of atoms 00145 /*! 00146 This simply initialized a set of EmbeddedAtom to have the same coordinates as the 00147 one's specified. No testing is done to verify any kind of ocrrectlness. Also 00148 this fragment is less ready (to expand and add new neighbors) than when using other 00149 constructors. This is because: 00150 - the user may have specified coords for only a part of the atoms in a fused ring systems 00151 in which case we need to find these atoms and merge these ring systems to this fragment 00152 - The atoms are not yet aware of their neighbor (what is left to add etc.) this again 00153 depends on atoms properly so that new 00154 neighbors can be added to them 00155 */ 00156 EmbeddedFrag(const RDKit::ROMol *mol, const RDGeom::INT_POINT2D_MAP &coordMap); 00157 00158 //! Initializer from a set of fused rings 00159 /*! 00160 ARGUMENTS: 00161 \param mol the molecule of interest 00162 \param fusedRings a vector of rings, each ring is a list of atom ids 00163 */ 00164 EmbeddedFrag(const RDKit::ROMol *mol, const RDKit::VECT_INT_VECT &fusedRings); 00165 00166 //! Initializer for a cis/trans system using the double bond 00167 /*! 00168 ARGUMENTS: 00169 \param dblBond the double bond that is involed in the cis/trans configuration 00170 */ 00171 explicit EmbeddedFrag(const RDKit::Bond* dblBond); 00172 00173 //! Expand this embedded system by adding negihboring atoms or other embedded systems 00174 /*! 00175 00176 Note that both nratms and efrags are modified in this function 00177 as we start merging them with the current fragment 00178 00179 */ 00180 void expandEfrag(RDKit::INT_LIST &nratms, std::list<EmbeddedFrag> &efrags); 00181 00182 //! Add a new non-ring atom to this object 00183 /* 00184 ARGUMENTS: 00185 \param aid ID of the atom to be added 00186 \param toAid ID of the atom that is already in this object to which this atom is added 00187 */ 00188 void addNonRingAtom(unsigned int aid, unsigned int toAid); 00189 00190 //! Merge this embedded object with another embedded fragment 00191 /*! 00192 00193 The transformation (rotation + translation required to attached 00194 the passed in object will be computed and applied. The 00195 coordinates of the atoms in this object will remain fixed We 00196 will assume that there are no common atoms between the two 00197 fragments to start with 00198 00199 ARGUMENTS: 00200 \param embObj another EmbeddedFrag object to be merged with this object 00201 \param toAid the atom in this embedded fragment to which the new object will be attached 00202 \param nbrAid the atom in the other fragment to attach to 00203 \param mol the molecule of interest 00204 */ 00205 void mergeNoCommon(EmbeddedFrag &embObj, unsigned int toAid, 00206 unsigned int nbrAid); //, const RDKit::ROMol *mol); 00207 00208 //! Merge this embedded object with another embedded fragment 00209 /*! 00210 00211 The transformation (rotation + translation required to attached 00212 the passed in object will be computed and applied. The 00213 coordinates of the atoms in this object will remain fixed This 00214 already know there are a atoms in common and we will use them to 00215 merge things 00216 00217 ARGUMENTS: 00218 \param embObj another EmbeddedFrag object to be merged with this object 00219 \param commAtms a vector of ids of the common atoms 00220 \param mol the molecule of interest 00221 00222 */ 00223 void mergeWithCommon(EmbeddedFrag &embObj, RDKit::INT_VECT &commAtms); 00224 //const RDKit::ROMol *mol); 00225 00226 void mergeFragsWithComm(std::list<EmbeddedFrag> &efrags); //, const RDKit::ROMol *mol); 00227 00228 //! Mark this fragment to be done for final embedding 00229 void markDone() { 00230 d_done = true; 00231 } 00232 00233 //! If this fragment done for the final embedding 00234 bool isDone() { 00235 return d_done; 00236 } 00237 00238 //! Get the molecule that this embedded fragmetn blongs to 00239 const RDKit::ROMol *getMol() const { return dp_mol;} 00240 00241 //! Find the common atom ids between this fragment and a second one 00242 RDKit::INT_VECT findCommonAtoms(const EmbeddedFrag &efrag2); 00243 00244 //! Find a neighbor to a non-ring atom among the already embedded atoms 00245 /*! 00246 ARGUMENTS: 00247 \param aid the atom id of interest 00248 \param mol molecule object 00249 00250 RETURNS: 00251 \return the id of the atom if we found a neighbor 00252 -1 otherwise 00253 00254 NOTE: by definition we can have only one neighbor in the embdded system. 00255 */ 00256 int findNeighbor(unsigned int aid); //, const RDKit::ROMol *mol); 00257 00258 //! Tranform this object to a new coordinates system 00259 /*! 00260 ARGUMENTS: 00261 \param trans : the transformation that need to be applied to the atoms in this object 00262 */ 00263 void Transform(const RDGeom::Transform2D &trans); 00264 00265 void Reflect(const RDGeom::Point2D &loc1, 00266 const RDGeom::Point2D &loc2); 00267 00268 const INT_EATOM_MAP &GetEmbeddedAtoms() const { 00269 return d_eatoms; 00270 } 00271 00272 void Translate(const RDGeom::Point2D &shift) { 00273 INT_EATOM_MAP_I eari; 00274 for (eari = d_eatoms.begin(); eari != d_eatoms.end(); eari++) { 00275 eari->second.loc += shift; 00276 } 00277 } 00278 00279 EmbeddedAtom GetEmbeddedAtom(unsigned int aid) const { 00280 INT_EATOM_MAP_CI posi = d_eatoms.find(aid); 00281 if (posi == d_eatoms.end()) { 00282 PRECONDITION(0, "Embedded atom does not contain embedded atom specified"); 00283 } 00284 return posi->second; 00285 } 00286 00287 //! the number of atoms in the embedded system 00288 int Size() const { 00289 return d_eatoms.size(); 00290 } 00291 00292 //! \brief compute a box that encloses the fragment 00293 void computeBox(); 00294 00295 //! \brief Flip atoms on one side of a bond - used in removing colissions 00296 /*! 00297 ARGUMENTS: 00298 \param mol - molecule involved in the frgament 00299 \param bondId - the bond used as the mirror to flip 00300 */ 00301 void flipAboutBond(unsigned int bondId); //const RDKit::ROMol *mol, unsigned int bondId); 00302 00303 void openAngles(const double *dmat, unsigned int aid1, unsigned int aid2); 00304 00305 std::vector<PAIR_I_I> findCollisions(const double *dmat, bool includeBonds=1); 00306 00307 void computeDistMat(DOUBLE_SMART_PTR &dmat); 00308 00309 double mimicDistMatAndDensityCostFunc(const DOUBLE_SMART_PTR *dmat, 00310 double mimicDmatWt); 00311 00312 void permuteBonds(unsigned int aid, unsigned int aid1, unsigned int aid2); 00313 00314 void randomSampleFlipsAndPermutations(unsigned int nBondsPerSample=3, 00315 unsigned int nSamples=100, int seed=100, 00316 const DOUBLE_SMART_PTR *dmat=0, 00317 double mimicDmatWt=0.0, 00318 bool permuteDeg4Nodes=false); 00319 00320 //! Remove collisions in a structure by flipping rotable bonds 00321 //! along the shortest path between two colliding atoms 00322 void removeCollisionsBondFlip(); 00323 00324 //! Remove collision by opening angles at the offending atoms 00325 void removeCollisionsOpenAngles(); 00326 00327 //! Remove collisions by shortening bonds along the shortest path between the atoms 00328 void removeCollisionsShortenBonds(); 00329 00330 //! helpers funtions to 00331 00332 00333 //! \brief make list of neighbors for each atom in the embedded system that 00334 //! still need to be embedded 00335 void setupNewNeighs(); //const RDKit::ROMol *mol); 00336 00337 //! update the unembedded neighbor atom list for a specified atom 00338 void updateNewNeighs(unsigned int aid); //, const RDKit::ROMol *mol); 00339 00340 //! \brief Find all atoms in this embedded system that are 00341 //! within a specified distant of a point 00342 int findNumNeigh(const RDGeom::Point2D &pt, double radius); 00343 00344 00345 inline double getBoxPx() {return d_px;} 00346 inline double getBoxNx() {return d_nx;} 00347 inline double getBoxPy() {return d_py;} 00348 inline double getBoxNy() {return d_ny;} 00349 00350 void canonicalizeOrientation(); 00351 00352 00353 private: 00354 00355 double totalDensity(); 00356 00357 void embedFusedRings(const RDKit::VECT_INT_VECT &fusedRings); 00358 00359 //! \brief Find a transform to join a ring to the current embedded frag when we 00360 //! have only on common atom 00361 /*! 00362 So this is the state of affairs assumed here: 00363 - we already have some rings in the fused system embeded and the 00364 coordinates for the atoms 00365 - the coordinates for the atoms in the new ring (with the center 00366 of rings at the origin) are available nringCors. we want to 00367 translate and rotate this ring to join with the already 00368 embeded rings. 00369 - only one atom is common between this new ring and the atoms 00370 that are already embedded 00371 - so we need to compute a transform that includes a translation 00372 so that the common atom overlaps and the rotation to minimize 00373 overalp with other atoms. 00374 00375 Here's what is done: 00376 - we bisect the remaining sweep angle at the common atom and 00377 attach the new ring such that the center of the new ring falls 00378 on this bisecting line 00379 00380 NOTE: It is assumed here that the original coordinates for the 00381 new ring are such that the center is at the origin (this is the 00382 way rings come out of embedRing) 00383 */ 00384 RDGeom::Transform2D computeOneAtomTrans(unsigned int commAid, 00385 const EmbeddedFrag &other); 00386 00387 00388 RDGeom::Transform2D computeTwoAtomTrans(unsigned int aid1, unsigned int aid2, 00389 const RDGeom::INT_POINT2D_MAP &nringCor); 00390 00391 //! Merge a ring with already embedded atoms 00392 /*! 00393 It is assumed that the new rings has already been oriented 00394 correctly etc. This function just update all the relevant data, 00395 like the neighbor information and the sweep angle 00396 */ 00397 void mergeRing(const EmbeddedFrag &embRing, unsigned int nCommon, 00398 const RDKit::INT_VECT &pinAtoms); 00399 00400 //! Reflect a fragment if necessary through a line connecting two atoms 00401 /*! 00402 00403 We want add the new fragment such that, most of its atoms fall 00404 on the side opoiste to where the atoms already embedded are aid1 00405 and aid2 give the atoms that were used to align the new ring to 00406 the embedded atoms and we will assume that that process has 00407 already taken place (i.e. transformRing has been called) 00408 00409 */ 00410 void reflectIfNecessaryDensity(EmbeddedFrag &embFrag, unsigned int aid1, 00411 unsigned int aid2); 00412 00413 //! Reflect a fragment if necessary based on the cis/trans specification 00414 /*! 00415 00416 we want to add the new fragment such that the cis/trans 00417 specification on bond between aid1 and aid2 is not violated. We 00418 will assume that aid1 and aid2 from this fragments as well as 00419 embFrag are already aligned to each other. 00420 00421 \param embFrag the fragment that will be reflected if necessary 00422 \param ctCase which fragment if the cis/trans dbl bond 00423 - 1 means embFrag is the cis/trans fragment 00424 - 2 mean "this" is the cis/trans fragment 00425 \param aid1 first atom that forms the plane (line) of reflection 00426 \param aid2 seconf atom that forms the plane of reflection 00427 */ 00428 void reflectIfNecessaryCisTrans(EmbeddedFrag &embFrag, unsigned int ctCase, 00429 unsigned int aid1, unsigned int aid2); 00430 00431 //! Reflect a fragment if necessary based on a thrid common point 00432 /*! 00433 00434 we want add the new fragment such that the thrid point falls on 00435 the same side of aid1 and aid2. We will assume that aid1 and 00436 aid2 from this fragments as well as embFrag are already aligned 00437 to each other. 00438 00439 */ 00440 void reflectIfNecessaryThirdPt(EmbeddedFrag &embFrag, unsigned int aid1, unsigned int aid2, 00441 unsigned int aid3); 00442 00443 //! \brief Initialize this fragment from a ring and coordinates for its atoms 00444 /*! 00445 ARGUMENTS: 00446 /param ring a vector of atom ids in the ring; it is assumed that there in 00447 clockwise or anti-clockwise order 00448 /param nringMap a map of atomId to coordinate map for the atoms in the ring 00449 */ 00450 void initFromRingCoords(const RDKit::INT_VECT &ring, 00451 const RDGeom::INT_POINT2D_MAP &nringMap); 00452 00453 //! Helper function to addNonRingAtom to a specified atoms in the fragment 00454 /* 00455 Add an atom to this embedded fragment when the fragment already 00456 has a atleast two previously added neighbors to 'toAid'. In this 00457 case we have to choose where the the new neighbor goes based on 00458 the angle that is already taken around the atom. 00459 00460 ARGUMENTS: 00461 \param aid ID of the atom to be added 00462 \param toAid ID of the atom that is already in this object to which this atom is added 00463 */ 00464 void addAtomToAtomWithAng(unsigned int aid, unsigned int toAid); 00465 00466 00467 //! Helper function to addNonRingAtom to a specified atoms in the fragment 00468 /*! 00469 00470 Add an atom (aid) to an atom (toAid) in this embedded fragment 00471 when 'toAid' has one or no neighbors previously added. In this 00472 case where the new atom should fall is determined by the degree 00473 of 'toAid' and the congestion around it. 00474 00475 ARGUMENTS: 00476 \param aid ID of the atom to be added 00477 \param toAid ID of the atom that is already in this object to which this atom is added 00478 \param mol the molecule we are dealing with 00479 */ 00480 void addAtomToAtomWithNoAng(unsigned int aid, 00481 unsigned int toAid); //, const RDKit::ROMol *mol); 00482 00483 //! Helper funtion to contructor that takes predefined coordinates 00484 /*! 00485 00486 Given an atom with more than 2 neighbors all embedded in this 00487 fragment this function tries to determine 00488 00489 - how much of an angle if left for any new neighbors yet to be 00490 added 00491 - which atom should we rotate when we add a new neighbor and in 00492 which direction (clockwise or anticlockwise 00493 00494 This is how it works 00495 - find the pair of nbrs that have the largest angle 00496 - this will most likely be the angle that is available - unless 00497 we have fused rings and we found on of the ring angle !!!! - 00498 in this cae we find the next best 00499 - find the smallest anngle that contains one of these nbrs - 00500 this determined which 00501 - way we want to rotate 00502 00503 ARGUMENTS: 00504 \param aid the atom id where we are centered right now 00505 \param doneNbrs list of neighbors that are already embedded around aid 00506 */ 00507 void computeNbrsAndAng(unsigned int aid, const RDKit::INT_VECT &doneNbrs); 00508 //const RDKit::ROMol *mol); 00509 00510 //! are we embedded with the final (molecule) coordinates 00511 bool d_done; 00512 double d_px, d_nx, d_py, d_ny; 00513 00514 //! a map that takes one from teh atom id to the embeddedatom object for that atom. 00515 INT_EATOM_MAP d_eatoms; 00516 00517 //RDKit::INT_DEQUE d_attachPts; 00518 RDKit::INT_LIST d_attachPts; 00519 00520 // pointer to the owning molecule 00521 const RDKit::ROMol *dp_mol; 00522 00523 }; 00524 00525 00526 } 00527 00528 #endif
1.5.6