EmbeddedFrag.h

Go to the documentation of this file.
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

Generated on Tue Oct 7 06:10:10 2008 for RDCode by  doxygen 1.5.5