1 /**********************************************************************
2 rotor.h - Rotate torsional according to rotor rules.
3 
4 Copyright (C) 1998-2000 by OpenEye Scientific Software, Inc.
5 Some portions Copyright (C) 2001-2005 by Geoffrey R. Hutchison
6 
7 This file is part of the Open Babel project.
8 For more information, see <http://openbabel.org/>
9 
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation version 2 of the License.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 ***********************************************************************/
19 
20 #ifndef OB_ROTOR_H
21 #define OB_ROTOR_H
22 
23 #include <openbabel/parsmart.h>
24 #include <openbabel/typer.h>
25 #include <openbabel/bitvec.h>
26 
27 #ifdef UNUSED
28 #elif (__GNUC__ == 4)
29 # define UNUSED(x) UNUSED_ ## x __attribute__((unused))
30 #elif defined(__LCLINT__)
31 # define UNUSED(x) /*@unused@*/ x
32 #else
33 # define UNUSED(x) x
34 #endif
35 
36 namespace OpenBabel
37 {
38   class OBRing;
39 
40 #ifndef SQUARE
41 #define SQUARE(x) ((x)*(x))
42 #endif
43 
44   //! \class OBRotorRule rotor.h <openbabel/rotor.h>
45   //! \brief A rule for torsional conformer searching, defined by a SMARTS pattern
46   //!
47   //! Rules define a SMARTS pattern to match and a set of 4 reference atoms
48   //! defining the dihedral angle. The rule can either define a set of possible
49   //! dihedral angles in degrees and/or a "delta" (i.e., all multiples of delta will
50   //! be considered)
51   class OBAPI OBRotorRule
52   {
53     int                 _ref[4]; //!< Reference atoms specifying the dihedral angle (as integers), numbered from 1 inside the SMARTS pattern
54     double              _delta;  //!< (optional) the resolution of a dihedral step in degrees
55     std::string         _s;      //!< Text of the SMARTS pattern
56     OBSmartsPattern*    _sp;     //!< The SMARTS pattern for the rotation rule
57     std::vector<double> _vals;   //!< At least one torsion angle (in radians) to evaluate
58   public:
59 
OBRotorRule(char * buffer,int ref[4],std::vector<double> & vals,double d)60   OBRotorRule(char *buffer,int ref[4],std::vector<double> &vals,double d):
61     _delta(d), _s(buffer), _vals(vals)
62     {
63       _sp = new OBSmartsPattern;
64       _sp->Init(buffer);
65       memcpy(_ref,ref,sizeof(int)*4);
66     }
67 
~OBRotorRule()68     ~OBRotorRule()
69       {
70         if (_sp)
71           {
72             delete _sp;
73             _sp = nullptr;
74           }
75       }
76 
77     //! \return whether this rotor rule is valid (i.e., is the SMARTS pattern valid)
IsValid()78     bool    IsValid()    {        return(_sp->IsValid());       }
79     //! \return a copy of the reference atom indexes inside the SMARTS pattern
80     //!
81     //!  These should be freed after use.
GetReferenceAtoms(int ref[4])82     void    GetReferenceAtoms(int ref[4]) { memcpy(ref,_ref,sizeof(int)*4); }
83     //! Set the resolution (delta) of a torsional step in degrees
SetDelta(double d)84     void    SetDelta(double d)    {       _delta = d;           }
85     //! \return the resolution (delta) of a torsional step in degrees
GetDelta()86     double  GetDelta()            {       return(_delta);       }
87     //! \return a reference to the dihedral angles to evaluate (in radians)
GetTorsionVals()88     std::vector<double>   &GetTorsionVals()    { return(_vals); }
89     //! \return the text of the SMARTS pattern for this rule
GetSmartsString()90     std::string  &GetSmartsString(){      return(_s);           }
91     //! \return the exact OBSmartsPattern object for this rule
GetSmartsPattern()92     OBSmartsPattern *GetSmartsPattern() {  return(_sp);         }
93   };
94 
95   //! \class OBRotorRules rotor.h <openbabel/rotor.h>
96   //! \brief Database of default hybridization torsional rules and SMARTS-defined OBRotorRule objects
97   //!
98   //! Use to automatically evaluate potentially rotatable bonds to generate
99   //! lists of dihedral angles to consider.
100   //! e.g., rotamer/conformer energy calculations
101   class OBAPI OBRotorRules : public OBGlobalDataBase
102   {
103     bool                       _quiet;  //!< Control debugging output from GetRotorIncrements()
104     std::vector<OBRotorRule*>  _vr;     //!< Database of specific OBRotorRules defined by SMARTS patterns
105     std::vector<double>        _sp3sp3; //!< Default dihedral angles to check for generic sp3 - sp3 hybridized rotatable bonds (in radians)
106     std::vector<double>        _sp3sp2; //!< Default dihedral angles to check for generic sp3 - sp2 hybridized rotatable bonds (in radians)
107     std::vector<double>        _sp2sp2; //!< Default dihedral angles to check for generic sp2 - sp2 hybridized rotatable bonds (in radians)
108   public:
109     OBRotorRules();
110     ~OBRotorRules();
111 
112     void ParseLine(const char*);
113     //! \return the number of rotor rules
GetSize()114     size_t GetSize()                 { return _vr.size();}
115 
116     //! Set the filename to be used for the database. Default = torlib.txt
SetFilename(std::string & s)117     void SetFilename(std::string &s)       { _filename = s;    }
118 
119     //! Determine the torsional angles to evaluate based on the database
120     //! \param mol molecule to evaluate
121     //! \param bond rotatable bond to evaluate
122     //! \param refs set to be the atom indexes (in mol) of the dihedral angle
123     //! \param vals set to be the list of angles to evaluate (in radians)
124     //! \param delta potential dihedral angle steps (in degrees)
125     void GetRotorIncrements(OBMol& mol,OBBond* bond,int refs[4],
126                             std::vector<double> &vals,double &delta);
127     //! Turn off debugging output from GetRotorIncrements()
Quiet()128     void Quiet()                           { _quiet=true;      }
129   };
130 
131   /**
132    * @class OBRotor rotor.h <openbabel/rotor.h>
133    * @brief A single rotatable OBBond as part of rotamer searching
134    */
135   class OBAPI OBRotor
136   {
137     int _idx; //!< the index in an OBRotorList
138     std::vector<int> _rotatoms; //!< the atoms to rotate
139     double _imag, _refang; //!< inverse magnitude and reference angle (see Precompute())
140     OBBond *_bond; //!< the bond associated with this rotor
141     std::vector<int> _ref, _torsion; //!< indexes for atom coordinates (from 0, multiplied by 3)
142     OBBitVec _fixedatoms,_fixedbonds, _evalatoms; //!< fixed atoms/bonds
143     std::vector<double> _torsionAngles;  //!< torsion resolution
144     std::vector<double> _invmag; //!< the inverse magnitudes (see Precalc)
145     std::vector<std::vector<double> > _sn,_cs,_t; //!< the rotation matrix (see Precalc())
146     std::vector<OBRing *> _rings; //!< the parent ring (if this is a rotor in a ring)
147   public:
148     /**
149      * Constructor.
150      */
151     OBRotor();
152     /**
153      * Destructor.
154      */
~OBRotor()155     ~OBRotor()
156       {
157       }
158 
159     ///@name Setup
160     ///@{
161     /**
162      * Set the OBBond associated with this OBRotor.
163      */
SetBond(OBBond * bond)164     void SetBond(OBBond *bond)
165     {
166       _bond = bond;
167       SetRings();
168     }
169     /**
170      * Set the rings associated with this bond (if it's a ring bond)
171      * \since Version 2.4
172      */
173     void SetRings();
174     /**
175      * Set the index for this rotor. Used by OBRotorList
176      */
SetIdx(int idx)177     void SetIdx(int idx)
178     {
179       _idx = idx;
180     }
181     /**
182      * Set the dihedral atoms.
183      * @param ref The dihedral atom indexes. These indexes start from 1.
184      */
185     void SetDihedralAtoms(std::vector<int> &ref);
186     /**
187      * Set the dihedral atoms.
188      * @param ref The dihedral atom indexes. These indexes start from 1.
189      */
190     void SetDihedralAtoms(int ref[4]);
191     /**
192      * Set the atom indexes that will be displaced when this rotor
193      * changes torsion angle. These indexes start from 0 and are multiplied
194      * by 3 for easy coordinate access.
195      */
196     void SetRotAtoms(std::vector<int> &atoms);
197     /**
198      * Set the possible torsion values or angles.
199      */
SetTorsionValues(std::vector<double> & angles)200     void SetTorsionValues(std::vector<double> &angles)
201     {
202       _torsionAngles = angles;
203     }
204     /**
205      * Set the bonds that will be fixed.
206      */
SetFixedBonds(OBBitVec & bv)207     void SetFixedBonds(OBBitVec &bv)
208     {
209       _fixedbonds = bv;
210     }
211     ///@}
212 
213     ///@name Performing rotations
214     ///@{
215     /**
216      * Rotate the atoms in the specified @p coordinates to the specified angle.
217      * @param coordinates The coordinates to rotate.
218      * @param setang The new torsion angle in radians.
219      */
SetToAngle(double * coordinates,double setang)220     inline void SetToAngle(double *coordinates, double setang)
221     {
222       double /*dx,dy,dz,*/ sn,cs,t,ang,mag;
223       // compute the angle to rotate (radians)
224       ang = setang - CalcTorsion(coordinates);
225       // if the angle to rotate is too small, we're done
226       if (fabs(ang) < 1e-5)
227         return;
228 
229       // compute the bond length
230       mag = CalcBondLength(coordinates);
231       // compute some rotation matrix elements
232       sn = sin(ang);
233       cs = cos(ang);
234       t = 1 - cs;
235 
236       // perform rotation
237       Set(coordinates, sn, cs, t, 1.0 / mag);
238     }
239     /**
240      * Rotate the atoms in the specified @p coordinates. This function does not
241      * require any precomputation and will compute all needed information when
242      * needed.
243      * @param coordinates The coordinates for the molecules as pointer to double.
244      * @param next The index of the new rotor angle. This is an index for the
245      * GetTorsionValues() list.
246      * @param prev If specified, the torsion current torsion angle can be
247      * looked up and does not have to be calculated again.
248      */
249     void SetRotor(double *coordinates, int next, int prev = -1);
250     /**
251      * Rotate the specified @p coordinates by using the specified rotation matrix.
252      *
253      */
254     void Set(double *coordinates, double sine, double cosine, double translation, double invmag);
255     /**
256      * Precompute the reference angle and inverse bond length of this rotor for
257      * a single conformer. This function should be used in combination with
258      * Set(double *coordinates, int idx).
259      * @param coordinates The coordinates to use in the computation.
260      *
261      * @code
262      * OBMol mol;
263      * ...
264      *
265      * unsigned int numCoords = mol.NumAtoms() * 3;
266      * double *coords = mol.GetCoordinates();
267      * OBRotor rotor;
268      * rotor.SetBond(mol.GetBond(3));
269      *
270      * // set the possible torsion values
271      * std::vector<double> angles;
272      * angles.push_back(0.0);
273      * angles.push_back(3.1415);
274      * rotor.SetTorsionValues(angles);
275      *
276      * // precompute inverse bond length (i.e. the bond length of bond with index 3
277      * // using the specified coordinates) and reference angle (i.e. torsion angle
278      * //in coords)
279      * rotor.Precompute(coords);
280      *
281      * // copy coordinates to coords_1
282      * double *coords_1 = new double[numCoords];
283      * for (unsigned int i = 0; i < numCoords; ++i)
284      *   coords_1[i] = coords[i];
285      * // rotate the atoms in coords_1 to angle with index 0 (i.e. 0.0 degrees)
286      * // note: on input, the coordinates should be the same as the coordinates used
287      * //       to precompute the inverse bond length and reference angle (in other
288      * //       words, the inverse magnitude and reference angle in the specfied
289      * //       coordinates should be the same as the one used for Precompute)
290      * rotor.Set(coords_1, 0)
291      *
292      * // copy coordinates to coords_2
293      * double *coords_2 = new double[numCoords];
294      * for (unsigned int i = 0; i < numCoords; ++i)
295      *   coords_2[i] = coords[i];
296      * // rotate the atoms in coords_2 to angle with index 1 (i.e. 180.0 degrees)
297      * rotor.Set(coords_2, 1)
298      *
299      * delete coords_1;
300      * delete coords_2;
301      * @endcode
302      */
303     void Precompute(double *coordinates);
304     /**
305      * Rotate the @p coordinates to set the torsion angle of this rotor to the angle
306      * specified by the index @p idx. Make sure to call Precompute before calling
307      * this function.
308      * @param coordinates The coordinates to rotate.
309      * @param idx The index of the torsion angle in the GetTorsionValues() list.
310      */
311     void Set(double *coordinates, int idx);
312     /**
313      * Precompute the inverse bond lengths, rotation matrices for all
314      * specified conformers and all possible torsion values. This method is
315      * used in combination with Set(double *coordinates, int conformer, int idx).
316      * @param conformers The pointers to the conformer coordinates
317      */
318     void Precalc(std::vector<double*> &conformers);
319     /**
320      * Rotate the @p coordinates to set the torsion to the torsion value with the
321      * specified @p index. The coordinates should be the same as the conformer used
322      * for calling Precalc (i.e. conformers[conformer] == coordinates). Make sure
323      * to call Precalc before calling this method.
324      * @param coordinates The conformer coordinates.
325      * @param conformer The conformer index in the conformer list given to Precalc().
326      * @param idx The torsion value index in the GetTorsionValues() list.
327      */
Set(double * coordinates,int conformer,int idx)328     void Set(double *coordinates, int conformer, int idx)
329     {
330       Set(coordinates, _sn[conformer][idx], _cs[conformer][idx], _t[conformer][idx], _invmag[conformer]);
331     }
332     ///@}
333 
334 
335     ///@name Methods to retrieve information
336     ///@{
337     /**
338      * Get the OBBond object associated with this OBRotor.
339      */
GetBond()340     OBBond *GetBond()
341     {
342       return(_bond);
343     }
344     /**
345      * Get the number of possible torsion angles for this OBRotor. This
346      * is the length of the GetTorsionValues() list.
347      */
Size()348     size_t Size()
349     {
350       return _torsionAngles.size();
351     }
352     /**
353      * Get the index for this rotor (index in an OBRotorList).
354      */
GetIdx()355     int GetIdx() const
356     {
357       return _idx;
358     }
359     /**
360      * Get the dihedral atom indexes. These indexes start from 1.
361      */
GetDihedralAtoms(int ref[4])362     void GetDihedralAtoms(int ref[4])
363     {
364       for (int i = 0; i < 4; ++i)
365         ref[i] = _ref[i];
366     }
367     /**
368      * Get the dihedral atom indexes. These indexes start from 1.
369      */
GetDihedralAtoms()370     std::vector<int> &GetDihedralAtoms()
371       {
372         return _ref;
373       }
374     /**
375      * Get the atom indexes that will be displaced when this rotor changes
376      * torsion angle. These indexes start from 1.
377      */
GetRotAtoms()378     const std::vector<int>& GetRotAtoms() const
379     {
380       return _rotatoms;
381     }
382     /**
383      * Get the possible torsion angles for this OBRotor.
384      */
GetTorsionValues()385     const std::vector<double> &GetTorsionValues() const
386     {
387       return _torsionAngles;
388     }
389     /**
390      * Get an OBBitVec objects with bits set for all bonds that are fixed.
391      * Bonds are indexed from 0.
392      */
GetFixedBonds()393     OBBitVec &GetFixedBonds()
394       {
395         return _fixedbonds;
396       }
397     /**
398      * Calculate the torsion for this OBRotor using the specified coordinates.
399      * @param coordinates The coordinates (e.g. OBMol::GetCoordinates()).
400      * @return The torsion angle in radians.
401      */
402     double CalcTorsion(double *coordinates);
403     /**
404      * Calculate the bond length for this OBRotor using the specified coordinates.
405      * @param coordinates The coordinates (e.g. OBMol::GetCoordinates()).
406      */
407     double CalcBondLength(double *coordinates);
408     ///@}
409 
410 
411     ///@name Iterator methods
412     ///@{
BeginTorIncrement()413     std::vector<double>::iterator BeginTorIncrement()
414       {
415         return _torsionAngles.begin();
416       }
EndTorIncrement()417     std::vector<double>::iterator EndTorIncrement()
418       {
419         return _torsionAngles.end();
420       }
421     ///@}
422 
423     void RemoveSymTorsionValues(int);
424 
425     ///@name Deprecated
426     ///@{
427     /** @deprecated Has no effect. */
SetDelta(double UNUSED (d))428     void SetDelta(double UNUSED(d)) {}
429     /** @deprecated Has no effect. */
GetDelta()430     double GetDelta() { return 10.0; }
431     /** @deprecated */
GetFixedAtoms()432     OBBitVec &GetFixedAtoms() { return _fixedatoms; }
433     /** @deprecated See SetFixedBonds */
SetFixedAtoms(OBBitVec & bv)434     void SetFixedAtoms(OBBitVec &bv) { _fixedatoms = bv; }
435     /** @deprecated */
GetEvalAtoms()436     OBBitVec &GetEvalAtoms() { return _evalatoms; }
437     /** @deprecated */
SetEvalAtoms(OBBitVec & bv)438     void SetEvalAtoms(OBBitVec &bv) { _evalatoms = bv; }
439     /** @deprecated */
GetRotAtoms()440     void* GetRotAtoms() { return &_rotatoms; }
441     /** @deprecated Bad name, see GetTorsionValues() */
GetResolution()442     std::vector<double> &GetResolution() { return _torsionAngles; }
443     /** @deprecated */
SetNumCoords(int UNUSED (nc))444     void SetNumCoords(int UNUSED(nc)) {}
445     ///@}
446 
447   };
448 
449 
450   //! A standard iterator over a vector of rotors
451   typedef std::vector<OBRotor*>::iterator OBRotorIterator;
452 
453   /**
454    * @class OBRotorList rotor.h <openbabel/rotor.h>
455    * @brief Given an OBMol, set up a list of possibly rotatable torsions,
456    */
457   class OBAPI OBRotorList
458   {
459     bool _quiet;                    //!< Control debugging output
460     bool _removesym;                //!< Control removal of symmetric rotations
461     bool _ringRotors;               //!< Are there ring rotors
462     OBBitVec _fixedatoms, _fixedbonds; //!< Bit vector of fixed (i.e., invariant) atoms
463     OBRotorRules _rr;               //!< Database of rotatable bonds and dihedral angles to test
464     std::vector<int> _dffv;         //!< Distance from fixed
465     std::vector<OBRotor*> _rotor;   //!< List of individual OBRotor torsions
466     //!
467     std::vector<std::pair<OBSmartsPattern*,std::pair<int,int> > > _vsym2;
468     //!
469     std::vector<std::pair<OBSmartsPattern*,std::pair<int,int> > > _vsym3;
470   public:
471     /**
472      * Constructor.
473      */
474     OBRotorList();
475     /**
476      * Destructor.
477      */
478     ~OBRotorList();
479 
480     /**
481      * Clear the internal list of rotors and reset.
482      */
483     void Clear();
484     /**
485      * @return the number of rotors in this list
486      */
Size()487     size_t Size()
488     {
489       return _rotor.size();
490     }
491     /**
492      * When no atoms/bonds are fixed or when bonds are fixed, this function will
493      * return true if the bond is fixed. When using the deprecated fixed atoms,
494      * this function will return true if the bond and at least one neighboring
495      * bond has fixed atoms.
496      */
497     bool IsFixedBond(OBBond*);
498     /**
499      * @return True if this rotor list has any fixed bonds.
500      */
HasFixedBonds()501     bool HasFixedBonds()
502     {
503       return !_fixedbonds.IsEmpty();
504     }
505     //! Rotates each bond to zero and 180 degrees and tests
506     //! if the 2 conformers are duplicates.  if so - the symmetric torsion
507     //! values are removed from consideration during a search
508     void RemoveSymVals(OBMol&);
509 
510     /**
511      * @return True if this rotor list has any ring bonds.
512      * @since version 2.4
513      */
HasRingRotors()514     bool HasRingRotors()
515     { return _ringRotors; }
516 
517     ///@name Setup
518     /**
519      * Setup this rotor list for the supplied molecule. This method calls
520      * FindRotors(), SetEvalAtoms(), and AssignTorVals().
521      * @param mol The molecule.
522      * @param sampleRings Whether to sample ring conformers - default = false
523      * @return True if rotatable bonds were found.
524      */
525     bool Setup(OBMol &mol, bool sampleRings = false);
526     /**
527      * Set the bonds that will be fixed.
528      */
SetFixedBonds(OBBitVec & fix)529     void SetFixedBonds(OBBitVec &fix)
530     {
531       _fixedbonds = fix;
532       _fixedatoms.Clear();
533     }
534     /**
535      * Initialize the private OBRotorRules database from a specific file.
536      */
Init(std::string & fname)537     void Init(std::string &fname)
538     {
539       _rr.SetFilename(fname);
540       _rr.Init();
541     }
542     /**
543      * Turn off debugging output.
544      */
SetQuiet()545     void SetQuiet() {
546       _quiet=true;
547       _rr.Quiet();
548     }
549     //! \brief Set the atoms to rotate from the dihedral atoms for each rotor
550     //! Uses OBRotor->GetDihedralAtoms() to call OBRotor->SetRotAtoms()
551     //! and standarizes the dihedral angles via OBRotor->SetDihedralAtoms()
552     //! \return True
553     bool SetRotAtoms(OBMol&);
554     /**
555      * Find all potentially rotatable bonds in the molecule. This method uses
556      * OBBond::IsRotor() for initial evaluation which depends on ring perception
557      * (i.e. ring bonds are considered rotatable). Fixed bonds, specified using the
558      * deprecated fixed atoms or the new fixed bonds methods are not added to
559      * the list. All rotatable bonds will be sorted by their graph theoretical
560      * distance (GTD) score (see OBMol::GetGTDVector()). This results in the
561      * the rotors going from the inside to the outside of the mol.
562      * @param mol The molecule.
563      * @param sampleRingBonds whether to sample ring bonds from analysis (default = false)
564      * @return True.
565      */
566     bool FindRotors(OBMol &mol, bool sampleRingBonds = false);
567     //! Determines which atoms should be used to calculate the internal energy
568     //! if the dihedral angle of the rotor is modified
569     //! \return True
570     bool SetEvalAtoms(OBMol&);
571     /**
572      * Using the OBRotorRules database, set the torsion values (and delta)
573      * to be evaluated and tested. This method also sets the rotatable atoms
574      * for the rotors to the smallest possible set. For each bond there are
575      * two candidate sets, one on either side. The smallest of these is used
576      * and the torsion indexes for the rotor are inverted if needed
577      * (i.e. a-b-c-d -> d-c-b-a).
578      */
579     bool AssignTorVals(OBMol &);
580     ///@}
581 
582     //! \name Iterator methods
583     //@{
584     /**
585      * Initialize the @p i iterator and get a pointer to the first OBRotor.
586      * @param i OBRotorIterator object.
587      */
BeginRotor(OBRotorIterator & i)588     OBRotor *BeginRotor(OBRotorIterator &i)
589     {
590       i = _rotor.begin();
591       return((i ==_rotor.end()) ? nullptr:*i);
592     }
593     /**
594      * Get a pointer to the next iterator.
595      * @param i OBRotorIterator object.
596      */
NextRotor(OBRotorIterator & i)597     OBRotor *NextRotor(OBRotorIterator &i)
598     {
599       ++i;
600       return((i ==_rotor.end()) ? nullptr:*i);
601     }
602     /**
603      * Get the rotor list begin iterator.
604      */
BeginRotors()605     OBRotorIterator BeginRotors()   { return(_rotor.begin()); }
606     /**
607      * Get the rotor list end iterator.
608      */
EndRotors()609     OBRotorIterator EndRotors()     { return(_rotor.end());   }
610     //@}
611 
612     ///@name Deprecated
613     ///@{
614     // Not declared
615     //! \deprecated Not declared. Use Setup() for top-level functionality
IdentifyEvalAtoms(OBMol & mol)616     bool   IdentifyEvalAtoms(OBMol &mol) { return SetEvalAtoms(mol); }
617     /**
618      * Set the list of fixed (invariant) atoms to the supplied OBBitVec
619      * @deprecated See SetFixedBonds()
620      */
SetFixAtoms(OBBitVec & fix)621     void SetFixAtoms(OBBitVec &fix)
622     {
623       _fixedatoms = fix;
624       _fixedbonds.Clear();
625     }
626     /**
627      * @return whether this rotor list has any fixed (invariant) atoms
628      * @deprecated See HasFixedBonds()
629      */
HasFixedAtoms()630     bool HasFixedAtoms()
631     {
632       return(!_fixedatoms.IsEmpty());
633     }
634     //! Has no effect
635     //! \deprecated Currently has no effect
IgnoreSymmetryRemoval()636     void IgnoreSymmetryRemoval()    { _removesym = false;}
637     //! \brief Set the atoms to rotate from the dihedral atoms for each rotor
638     //! Insures the fixed atoms are respected, but otherwise functions like
639     //! SetRotAtoms()
640     void SetRotAtomsByFix(OBMol&);
641     ///@}
642 
643   };
644 
645   /// @cond DEV
646   class rotor_digit {
647   public:
rotor_digit(unsigned int rs)648     rotor_digit(unsigned int rs)
649       {
650         resolution_size = rs;
651         state = 0;
652       }
653 
rotor_digit()654     rotor_digit()
655       {
656         resolution_size = 0;
657         state = 0;
658       }
659 
set_size(unsigned int rs)660     void set_size(unsigned int rs)
661     {
662       resolution_size = rs;
663       state = 0;
664     }
665 
set_state(int st)666     void set_state(int st)
667     {
668       state = st;
669     }
670 
get_state()671     int get_state()
672     {
673       return state;
674     }
675 
size()676     unsigned int size()
677     {
678       return resolution_size;
679     }
680 
next()681     bool next()
682     {
683       if (state < static_cast<int>(resolution_size - 1)) {
684         ++state;
685         return false;
686       } else
687         state = 0;
688 
689       return true;
690     }
691   private:
692     unsigned int resolution_size;
693     int state;
694 #ifndef SWIG
695   } typedef rotor_digit;
696 #else
697 };
698 #endif
699   /// @endcond
700 
701   //! \class OBRotorKeys rotor.h <openbabel/rotor.h>
702   //! \brief A class to generate all possible rotorKeys
703   class OBAPI OBRotorKeys
704   {
705       /**
706       \brief A class to generate all possible rotorKeys
707 
708       This class can generate all possible rotor keys for a set of OBRotors
709       which can all have their own resolution. Thanks to Yongjin Xu for this
710       patch.
711 
712       the code blow is taken from  OBForceField::SystematicRotorSearch():
713       \code
714       #include <openbabel/rotor.h>
715       #include <openbabel/mol.h>
716 
717       // See OBConversion class to fill the mol object.
718       OBMol mol;
719       OBRotorList rl;
720       OBRotamerList rotamers;
721 
722       rl.Setup(_mol);
723       rotamers.SetBaseCoordinateSets(_mol);
724       rotamers.Setup(_mol, rl);
725 
726       cout << "number of rotatable bonds: " <<  rl.Size() << endl;
727 
728       if (!rl.Size()) { // only one conformer
729         cout << "generated only one conformer" << endl;
730         // exit here
731       }
732 
733       OBRotorKeys rotorKeys;
734       OBRotorIterator ri;
735       OBRotor *rotor = rl.BeginRotor(ri);
736       for (int i = 1; i < rl.Size() + 1; ++i, rotor = rl.NextRotor(ri)) { // foreach rotor
737         rotorKeys.AddRotor(rotor->GetResolution().size());
738       }
739 
740       while (rotorKeys.Next()) {
741         std::vector<int> rotorKey = rotorKeys.GetKey();
742         cout << "rotorKey = " << rotorKey[1] << " " << rotorKey[2] << endl;
743         rotamers.AddRotamer(rotorKey);
744       }
745 
746       rotamers.ExpandConformerList(_mol, _mol.GetConformers());
747       \endcode
748       **/
749 
750     public:
751       //! Constructor
OBRotorKeys()752       OBRotorKeys()
753       {
754         _vr.clear();
755       }
756 
757       //! Clear all rotors
Clear()758       void Clear(){
759         _vr.clear();
760       }
761 
762       //! Number of rotor keys (= number of possible conformers)
NumKeys()763       unsigned int NumKeys()
764       {
765         unsigned int numKeys = 0;
766 
767         while (Next())
768           numKeys++;
769 
770         return numKeys;
771       }
772 
773       //! Add a rotor
774       //! \param size the rotor resolution
AddRotor(unsigned int size)775       void AddRotor(unsigned int size)
776       {
777         rotor_digit rd(size);
778         _vr.push_back(rd);
779       }
780 
781       //! Select the next rotor key
782       //! \return true if there are more rotor keys
Next()783       bool Next()
784       {
785         if(_vr.size() == 0)
786           return false;
787 
788         bool carry = _vr[0].next();
789         unsigned int i = 1;
790         while (carry) {
791           if(i == _vr.size())
792             return false;
793 
794           carry = _vr[i].next();
795           i++;
796         }
797         return true;
798       }
799 
800       //! Get the currently selected rotor key
801       //! \return current rotor key
GetKey()802       std::vector<int> GetKey()
803       {
804         std::vector<int> rt;
805         rt.clear();
806         rt.push_back(0);
807         for(unsigned int i = 0; i < _vr.size(); i++){
808           rt.push_back(_vr[i].get_state());
809         }
810 
811         return rt;
812       }
813 
814     private:
815       std::vector<rotor_digit> _vr;
816   };
817 
818 
819 } // end namespace OpenBabel
820 
821 #endif // OB_ROTOR_H
822 
823 //! \file rotor.h
824 //! \brief Rotate torsional according to rotor rules.
825