1 /**********************************************************************
2 generic.cpp - Handle OBGenericData classes.
3 
4 Copyright (C) 1998-2001 by OpenEye Scientific Software, Inc.
5 Some portions Copyright (C) 2001-2006 by Geoffrey R. Hutchison
6 Some portions Copyright (C) 2010 by David Lonie
7 
8 This file is part of the Open Babel project.
9 For more information, see <http://openbabel.org/>
10 
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation version 2 of the License.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
19 ***********************************************************************/
20 #include <openbabel/babelconfig.h>
21 
22 #include <string>
23 #include <set>
24 
25 #include <openbabel/mol.h>
26 #include <openbabel/atom.h>
27 #include <openbabel/bond.h>
28 #include <openbabel/ring.h>
29 #include <openbabel/obiter.h>
30 #include <openbabel/generic.h>
31 #include <openbabel/math/matrix3x3.h>
32 #include <openbabel/elements.h>
33 
34 // needed for msvc to have at least one reference to AtomClass, AliasData in openbabel library
35 #include <openbabel/alias.h>
36 
37 using namespace std;
38 
39 namespace OpenBabel
40 {
41 
42   /** \class OBGenericData generic.h <openbabel/generic.h>
43 
44       OBGenericData is an abstract base class which defines an interface for
45       storage, retrieval, and indexing of arbitrary generic data.
46       Subclasses of OBGenericData can be used to store custom data
47       on a per-atom, per-bond, per-molecule, or per-residue basis.
48       Open Babel currently supports a small subset of chemical functionality
49       as OBGenericData types, which will expand over time to support additional
50       interconversion (e.g., spectroscopy, dynamics, surfaces...)
51 
52       For more information on currently supported types, please see
53       the developer wiki:
54       http://openbabel.org/wiki/Generic_Data
55 
56       For your own custom data, either define a custom subclass using
57       an id from the OBGenericDataType::CustomData0 to
58       OBGenericDataType::CustomData15 slots,
59       or store your data as a string and use OBPairData for key/value access.
60       The latter is <strong>highly</strong> recommended for various text
61       descriptors
62       e.g., in QSAR, atom or bond labels, or other textual data.
63 
64       <strong>New in Open Babel, version 2.1</strong>
65       is the template-based OBPairTemplate,
66       which can be used to store arbitrary data types. There are predefined
67       types OBPairInteger and OBPairFloatingPoint for storing integers and
68       floating-point values without converting to a string representation.
69 
70       Also <strong>new</strong> is the "source" or "origin" of a data
71       entry, enumerated by DataOrigin. This can be accessed by
72       SetOrigin() and GetOrigin(), as well as via "filtering" methods
73       in OBBase, allowing you to separate data read in from a file,
74       added by a user, or assigned by Open Babel internally.
75 
76       While the library and import routines will set DataOrigin correctly,
77       you should try to annotate data added by your code. Typically this would
78       either be userAdded or external. The former refers to something the
79       user requested as an annotation, while the latter refers to annotations
80       your code adds automatically.
81 
82       Example code using OBGenericData:
83 
84       @code
85       if (mol.HasData(OBGenericDataType::UnitCell))
86       {
87          uc = (OBUnitCell*)mol.GetData(OBGenericDataType::UnitCell);
88          sprintf(buffer,
89             "%10.5f%10.5f%10.5f%10.5f%10.5f%10.5f",
90             uc->GetA(), uc->GetB(), uc->GetC(),
91             uc->GetAlpha() , uc->GetBeta(), uc->GetGamma());
92          ofs << buffer << endl;
93       }
94 
95       ...
96 
97       vector<OBGenericData*>::iterator k;
98       vector<OBGenericData*> vdata = mol.GetData();
99       for (k = vdata.begin();k != vdata.end();++k)
100          if ((*k)->GetDataType() == OBGenericDataType::PairData)
101          {
102             ofs << ">  <" << (*k)->GetAttribute() << ">" << endl;
103             ofs << ((OBPairData*)(*k))->GetValue() << endl << endl;
104          }
105       @endcode
106 
107       Similar code also works for OBGenericData stored in an OBAtom or
108       OBBond or OBResidue. These examples show use of DataOrigin outside
109       of the Open Babel library.
110 
111       @code
112       string atomLabel; // e.g., from the user adding annotation to an atom
113       if (!atom.HasData("UserLabel")) // stored textual data as an OBPairData
114       {
115          OBPairData *label = new OBPairData;
116          label->SetAttribute("UserLabel");
117          label->SetValue(atomLabel);
118          label->SetOrigin(userInput); // set by user, not by Open Babel
119 
120          atom.SetData(label);
121       }
122 
123       ...
124 
125       if (bond.HasData("DisplayType")) // e.g. in a visualization tool
126       {
127          OBPairData *display = dynamic_cast<OBPairData *> bond.GetData("DisplayType");
128          if (display->GetValue() == "wireframe")
129          {
130             ... // display a wireframe view
131          }
132       }
133       @endcode
134 
135       When designing a class derived from OBGenericData you must add a
136       Clone() function. For classes used with OBMol this is used when
137       an OBMol object is copied. If your class member variables contain
138       pointers to atoms or bonds then it will be necessary to ensure
139       that these are updated in Clone() to refer to the new molecule. Without
140       these and similar pointers it is more likely that the very simple
141       clone function
142       @code
143       virtual OBGenericData* Clone(OBBase* parent) const
144          {return new MyNewClass(*this);}
145       @endcode
146       and the compiler generated copy constructor would be sufficient.
147 
148       It is recommended that, if possible, OBGenericData classes do not
149       store atom and bond pointers. Using atom and bond indices instead
150       would allow the simple version of Clone() above. See
151       OBRotameterData::Clone for an example of a more complicated version.
152       For classes which are not intended to support copying, Clone() can
153       return nullptr
154       @code
155       virtual OBGenericData* Clone(OBBase* parent) const
156          {return nullptr;}
157       @endcode
158       Clone() is a pure virtual function so that you need to decide what
159       kind of function you need and include it explicitly.
160   **/
161 
162   //
163   //member functions for OBGenericData class
164   //
165 
OBGenericData(const std::string attr,const unsigned int type,const DataOrigin source)166   OBGenericData::OBGenericData(const std::string attr, const unsigned int type,
167                                const DataOrigin  source):
168     _attr(attr), _type(type), _source(source)
169   { }
170 
171   /* Use default copy constructor and assignment operators
172      OBGenericData::OBGenericData(const OBGenericData &src)
173      {
174      _type = src.GetDataType();
175      _attr = src.GetAttribute();
176      }
177 
178 
179      OBGenericData& OBGenericData::operator = (const OBGenericData &src)
180      {
181      if(this == &src)
182      return(*this);
183 
184      _type = src._type;
185      _attr = src._attr;
186 
187      return(*this);
188      }
189   */
190 
191   //
192   //member functions for OBCommentData class
193   //
194 
OBCommentData()195   OBCommentData::OBCommentData():
196     OBGenericData("Comment", OBGenericDataType::CommentData)
197   { }
198 
OBCommentData(const OBCommentData & src)199   OBCommentData::OBCommentData(const OBCommentData &src) :
200     OBGenericData(src), _data(src._data)
201   {  }
202 
203   //
204   //member functions for OBExternalBond class
205   //
OBExternalBond(OBAtom * atom,OBBond * bond,int idx)206   OBExternalBond::OBExternalBond(OBAtom *atom,OBBond *bond,int idx):
207     _idx(idx), _atom(atom), _bond(bond)
208   {  }
209 
OBExternalBond(const OBExternalBond & src)210   OBExternalBond::OBExternalBond(const OBExternalBond &src):
211     _idx(src._idx), _atom(src._atom), _bond(src._bond)
212   { }
213 
214   //
215   //member functions for OBExternalBondData class
216   //
217 
OBExternalBondData()218   OBExternalBondData::OBExternalBondData():
219     OBGenericData("ExternalBondData", OBGenericDataType::ExternalBondData,
220                   perceived)
221   { }
222 
SetData(OBAtom * atom,OBBond * bond,int idx)223   void OBExternalBondData::SetData(OBAtom *atom,OBBond *bond,int idx)
224   {
225     OBExternalBond xb(atom,bond,idx);
226     _vexbnd.push_back(xb);
227   }
228 
229   //
230   //member functions for OBPairData class
231   //
232 
OBPairData()233   OBPairData::OBPairData() :
234     OBGenericData("PairData", OBGenericDataType::PairData)
235   { }
236 
237   //
238   //member functions for OBVirtualBond class
239   //
240 
OBVirtualBond()241   OBVirtualBond::OBVirtualBond():
242     OBGenericData("VirtualBondData", OBGenericDataType::VirtualBondData, perceived),
243     _bgn(0), _end(0), _ord(0), _stereo(0)
244   {  }
245 
OBVirtualBond(unsigned int bgn,unsigned int end,unsigned int ord,int stereo)246   OBVirtualBond::OBVirtualBond(unsigned int bgn, unsigned int end, unsigned int ord, int stereo):
247     OBGenericData("VirtualBondData", OBGenericDataType::VirtualBondData, perceived),
248     _bgn(bgn), _end(end), _ord(ord), _stereo(stereo)
249   {  }
250 
251   //
252   // member functions for OBUnitCell class
253   //
OBUnitCell()254   OBUnitCell::OBUnitCell():
255     OBGenericData("UnitCell", OBGenericDataType::UnitCell),
256     _mOrtho(matrix3x3()),
257     _mOrient(matrix3x3()),
258     _offset(vector3()),
259     _spaceGroupName(""), _spaceGroup(nullptr),
260     _lattice(Undefined)
261   {  }
262 
OBUnitCell(const OBUnitCell & src)263   OBUnitCell::OBUnitCell(const OBUnitCell &src) :
264     OBGenericData("UnitCell", OBGenericDataType::UnitCell),
265     _mOrtho(src._mOrtho),
266     _mOrient(src._mOrient),
267     _offset(src._offset),
268     _spaceGroupName(src._spaceGroupName), _spaceGroup(src._spaceGroup),
269     _lattice(src._lattice)
270   {  }
271 
operator =(const OBUnitCell & src)272   OBUnitCell & OBUnitCell::operator=(const OBUnitCell &src)
273   {
274     if(this == &src)
275       return(*this);
276 
277     _mOrtho = src._mOrtho;
278     _mOrient = src._mOrient;
279     _offset = src._offset;
280 
281     _spaceGroup = src._spaceGroup;
282     _spaceGroupName = src._spaceGroupName;
283     _lattice = src._lattice;
284 
285     return(*this);
286   }
287 
288   //! Implements <a href="http://qsar.sourceforge.net/dicts/blue-obelisk/index.xhtml#calculateOrthogonalisationMatrix">blue-obelisk:calculateOrthogonalisationMatrix</a>
SetData(const double a,const double b,const double c,const double alpha,const double beta,const double gamma)289   void OBUnitCell::SetData(const double a, const double b, const double c,
290                            const double alpha, const double beta, const double gamma)
291   {
292     _mOrtho.FillOrth(alpha, beta, gamma, a, b, c);
293     _mOrient = matrix3x3(1);
294     _spaceGroup = nullptr;
295     _spaceGroupName = "";
296     _lattice = OBUnitCell::Undefined;
297   }
298 
299   //! Implements <a href="http://qsar.sourceforge.net/dicts/blue-obelisk/index.xhtml#calculateOrthogonalisationMatrix">blue-obelisk:calculateOrthogonalisationMatrix</a>
SetData(const vector3 v1,const vector3 v2,const vector3 v3)300   void OBUnitCell::SetData(const vector3 v1, const vector3 v2, const vector3 v3)
301   {
302     matrix3x3 m (v1, v2, v3);
303     _mOrtho.FillOrth(vectorAngle(v2,v3), // alpha
304                      vectorAngle(v1,v3), // beta
305                      vectorAngle(v1,v2), // gamma
306                      v1.length(),        // a
307                      v2.length(),        // b
308                      v3.length());       // c
309     _mOrient = m.transpose() * _mOrtho.inverse();
310     _spaceGroup = nullptr;
311     _spaceGroupName = "";
312     _lattice = OBUnitCell::Undefined;
313   }
314 
315   //! Implements <a href="http://qsar.sourceforge.net/dicts/blue-obelisk/index.xhtml#calculateOrthogonalisationMatrix">blue-obelisk:calculateOrthogonalisationMatrix</a>
SetData(const matrix3x3 m)316   void OBUnitCell::SetData(const matrix3x3 m)
317   {
318     SetData(m.GetRow(0), m.GetRow(1), m.GetRow(2));
319   }
320 
SetOffset(const vector3 v1)321   void OBUnitCell::SetOffset(const vector3 v1)
322   {
323     _offset = v1;
324   }
325 
326   //! Implements <a href="http://qsar.sourceforge.net/dicts/blue-obelisk/index.xhtml#convertNotionalIntoCartesianCoordinates">blue-obelisk:convertNotionalIntoCartesianCoordinates</a>
GetCellVectors() const327   vector<vector3> OBUnitCell::GetCellVectors() const
328   {
329     vector<vector3> v;
330     v.reserve(3);
331 
332     matrix3x3 m = GetCellMatrix();
333 
334     v.push_back(m.GetRow(0));
335     v.push_back(m.GetRow(1));
336     v.push_back(m.GetRow(2));
337 
338     return v;
339   }
340 
GetCellMatrix() const341   matrix3x3 OBUnitCell::GetCellMatrix() const
342   {
343     return (_mOrient * _mOrtho).transpose();
344   }
345 
GetOrthoMatrix() const346   matrix3x3 OBUnitCell::GetOrthoMatrix() const
347   {
348     return _mOrtho;
349   }
350 
GetOrientationMatrix() const351   matrix3x3 OBUnitCell::GetOrientationMatrix() const
352   {
353     return _mOrient;
354   }
355 
GetFractionalMatrix() const356   matrix3x3 OBUnitCell::GetFractionalMatrix() const
357   {
358     return _mOrtho.inverse();
359   }
360 
FractionalToCartesian(vector3 frac) const361   vector3 OBUnitCell::FractionalToCartesian(vector3 frac) const
362   {
363     return _mOrient * _mOrtho * frac + _offset;
364   }
365 
CartesianToFractional(vector3 cart) const366   vector3 OBUnitCell::CartesianToFractional(vector3 cart) const
367   {
368     return _mOrtho.inverse() * _mOrient.inverse() * (cart - _offset);
369   }
370 
WrapCartesianCoordinate(vector3 cart) const371   vector3 OBUnitCell::WrapCartesianCoordinate(vector3 cart) const
372   {
373     vector3 v = CartesianToFractional(cart);
374     v = WrapFractionalCoordinate(v);
375     return FractionalToCartesian(v);
376   }
377 
WrapFractionalCoordinate(vector3 frac) const378   vector3 OBUnitCell::WrapFractionalCoordinate(vector3 frac) const
379   {
380     double x = fmod(frac.x(), 1);
381     double y = fmod(frac.y(), 1);
382     double z = fmod(frac.z(), 1);
383     if (x < 0) x += 1;
384     if (y < 0) y += 1;
385     if (z < 0) z += 1;
386 
387 #define LIMIT 0.999999
388     if (x > LIMIT)
389       x -= 1;
390     if (y > LIMIT)
391       y -= 1;
392     if (z > LIMIT)
393       z -= 1;
394 #undef LIMIT
395 
396     // Fuzzy logic from Francois-Xavier
397 #define EPSILON 1.0e-6
398     if (x > 1 - EPSILON || x < EPSILON)
399       x = 0.0;
400     if (y > 1 - EPSILON || y < EPSILON)
401       y = 0.0;
402     if (z > 1 - EPSILON || z < EPSILON)
403       z = 0.0;
404 #undef EPSILON
405 
406     return vector3(x, y, z);
407   }
408 
UnwrapCartesianNear(vector3 new_loc,vector3 ref_loc) const409   vector3 OBUnitCell::UnwrapCartesianNear(vector3 new_loc, vector3 ref_loc) const
410   {
411     vector3 bond_dir = MinimumImageCartesian(new_loc - ref_loc);
412     return ref_loc + bond_dir;
413   }
414 
UnwrapFractionalNear(vector3 new_loc,vector3 ref_loc) const415   vector3 OBUnitCell::UnwrapFractionalNear(vector3 new_loc, vector3 ref_loc) const
416   {
417     vector3 bond_dir = MinimumImageFractional(new_loc - ref_loc);
418     return ref_loc + bond_dir;
419   }
420 
MinimumImageCartesian(vector3 cart) const421   vector3 OBUnitCell::MinimumImageCartesian(vector3 cart) const
422   {
423     vector3 frac = CartesianToFractional(cart);
424     frac = MinimumImageFractional(frac);
425     return FractionalToCartesian(frac);
426   }
427 
MinimumImageFractional(vector3 frac) const428   vector3 OBUnitCell::MinimumImageFractional(vector3 frac) const
429   {
430     double x = frac.x() - round(frac.x());
431     double y = frac.y() - round(frac.y());
432     double z = frac.z() - round(frac.z());
433     return vector3(x, y, z);
434   }
435 
GetLatticeType(int spacegroup) const436   OBUnitCell::LatticeType OBUnitCell::GetLatticeType( int spacegroup ) const
437   {
438     //	1-2 	Triclinic
439     //	3-15 	Monoclinic
440     //	16-74	Orthorhombic
441     //	75-142 	Tetragonal
442     //	143-167 Rhombohedral
443     //	168-194 Hexagonal
444     //	195-230 Cubic
445 
446     if ( spacegroup == 0  && _spaceGroup)
447       spacegroup = _spaceGroup->GetId();
448 
449     if ( spacegroup <= 0 )
450       return OBUnitCell::Undefined;
451 
452     else if ( spacegroup == 1 ||
453               spacegroup == 2 )
454       return OBUnitCell::Triclinic;
455 
456     else if ( spacegroup >= 3 &&
457               spacegroup <= 15 )
458       return OBUnitCell::Monoclinic;
459 
460     else if ( spacegroup >= 16 &&
461               spacegroup <= 74 )
462       return OBUnitCell::Orthorhombic;
463 
464     else if ( spacegroup >= 75 &&
465               spacegroup <= 142 )
466       return OBUnitCell::Tetragonal;
467 
468     else if ( spacegroup >= 143 &&
469               spacegroup <= 167 )
470       return OBUnitCell::Rhombohedral;
471 
472     else if ( spacegroup >= 168 &&
473               spacegroup <= 194 )
474       return OBUnitCell::Hexagonal;
475 
476     else if ( spacegroup >= 195 &&
477               spacegroup <= 230 )
478       return OBUnitCell::Cubic;
479 
480     //just to be extra sure
481     else // ( spacegroup > 230 )
482       return OBUnitCell::Undefined;
483   }
484 
GetLatticeType() const485   OBUnitCell::LatticeType OBUnitCell::GetLatticeType() const
486   {
487     if (_lattice != Undefined)
488       return _lattice;
489     else if (_spaceGroup != nullptr)
490       return GetLatticeType(_spaceGroup->GetId());
491 
492     double a = GetA();
493     double b = GetB();
494     double c = GetC();
495     double alpha = GetAlpha();
496     double beta  = GetBeta();
497     double gamma = GetGamma();
498 
499     unsigned int rightAngles = 0;
500     if (IsApprox(alpha, 90.0, 1.0e-3)) rightAngles++;
501     if (IsApprox(beta,  90.0, 1.0e-3)) rightAngles++;
502     if (IsApprox(gamma, 90.0, 1.0e-3)) rightAngles++;
503 
504     // recast cache member "_lattice" as mutable
505     OBUnitCell::LatticeType *lattice =
506       const_cast<OBUnitCell::LatticeType*>(&_lattice);
507 
508     switch (rightAngles)
509       {
510       case 3:
511         if (IsApprox(a, b, 1.0e-4) && IsApprox(b, c, 1.0e-4))
512           *lattice = Cubic;
513         else if (IsApprox(a, b, 1.0e-4) || IsApprox(b, c, 1.0e-4))
514           *lattice = Tetragonal;
515         else
516           *lattice = Orthorhombic;
517         break;
518       case 2:
519         if ( (IsApprox(alpha, 120.0, 1.0e-3)
520               || IsApprox(beta, 120.0, 1.0e-3)
521               || IsApprox(gamma, 120.0f, 1.0e-3))
522              && (IsApprox(a, b, 1.0e-4) || IsApprox(b, c, 1.0e-4)) )
523           *lattice = Hexagonal;
524         else
525           *lattice = Monoclinic;
526         break;
527       default:
528         if (IsApprox(a, b, 1.0e-4) && IsApprox(b, c, 1.0e-4))
529           *lattice = Rhombohedral;
530         else
531           *lattice = Triclinic;
532       }
533 
534     return *lattice;
535   }
536 
GetSpaceGroupNumber(std::string name) const537   int OBUnitCell::GetSpaceGroupNumber( std::string name) const
538   {
539     static const char * const spacegroups[] = {
540       "P1", "P-1", "P2", "P2(1)", "C2", "Pm", "Pc", "Cm", "Cc", "P2/m",
541       "P2(1)/m", "C2/m", "P2/c", "P2(1)/c", "C2/c", "P222", "P222(1)",
542       "P2(1)2(1)2", "P2(1)2(1)2(1)", "C222(1)", "C222", "F222", "I222",
543       "I2(1)2(1)2(1)", "Pmm2", "Pmc2(1)", "Pcc2", "Pma2", "Pca2(1)", "Pnc2",
544       "Pmn2(1)", "Pba2", "Pna2(1)", "Pnn2", "Cmm2", "Cmc2(1)", "Ccc2", "Amm2",
545       "Abm2", "Ama2", "Aba2", "Fmm2", "Fdd2", "Imm2", "Iba2", "Ima2", "Pmmm",
546       "Pnnn", "Pccm", "Pban", "Pmma", "Pnna", "Pmna", "Pcca", "Pbam", "Pccn",
547       "Pbcm", "Pnnm", "Pmmn", "Pbcn", "Pbca", "Pnma", "Cmcm", "Cmca", "Cmmm",
548       "Cccm", "Cmma", "Ccca", "Fmmm", "Fddd", "Immm", "Ibam", "Ibca", "Imma",
549       "P4", "P4(1)", "P4(2)", "P4(3)", "I4", "I4(1)", "P-4", "I-4", "P4/m",
550       "P4(2)/m", "P4/n", "P4(2)/n", "I4/m", "I4(1)/a", "P422", "P42(1)2",
551       "P4(1)22", "P4(1)2(1)2", "P4(2)22", "P4(2)2(1)2", "P4(3)22", "P4(3)2(1)2",
552       "I422", "I4(1)22", "P4mm", "P4bm", "P4(2)cm", "P4(2)nm", "P4cc", "P4nc",
553       "P4(2)mc", "P4(2)bc", "I4mm", "I4cm", "I4(1)md", "I4(1)cd", "P-42m",
554       "P-42c", "P-42(1)m", "P-42(1)c", "P-4m2", "P-4c2", "P-4b2", "P-4n2",
555       "I-4m2", "I-4c2", "I-42m", "I-42d", "P4/mmm", "P4/mcc", "P4/nbm",
556       "P4/nnc", "P4/mbm", "P4/mnc", "P4/nmm", "P4/ncc", "P4(2)/mmc",
557       "P4(2)/mcm", "P4(2)/nbc", "P4(2)/nnm", "P4(2)/mbc", "P4(2)/mnm",
558       "P4(2)/nmc", "P4(2)/ncm", "I4/mmm", "I4/mcm", "I4(1)/amd", "I4(1)/acd",
559       "P3", "P3(1)", "P3(2)", "R3", "P-3", "R-3", "P312", "P321", "P3(1)12",
560       "P3(1)21", "P3(2)12", "P3(2)21", "R32", "P3m1", "P31m", "P3c1", "P31c",
561       "R3m", "R3c", "P-31m", "P-31c", "P-3m1", "P-3c1", "R-3m", "R-3c", "P6",
562       "P6(1)", "P6(5)", "P6(2)", "P6(4)", "P6(3)", "P-6", "P6/m", "P6(3)/m",
563       "P622", "P6(1)22", "P6(5)22", "P6(2)22", "P6(4)22", "P6(3)22", "P6mm",
564       "P6cc", "P6(3)cm", "P6(3)mc", "P-6m2", "P-6c2", "P-62m", "P-62c",
565       "P6/mmm", "P6/mcc", "P6(3)/mcm", "P6(3)/mmc", "P23", "F23", "I23",
566       "P2(1)3", "I2(1)3", "Pm-3", "Pn-3", "Fm-3", "Fd-3", "Im-3", "Pa-3",
567       "Ia-3", "P432", "P4(2)32", "F432", "F4(1)32", "I432", "P4(3)32",
568       "P4(1)32", "I4(1)32", "P-43m", "F4-3m", "I-43m", "P-43n", "F-43c",
569       "I-43d", "Pm-3m", "Pn-3n", "Pm-3n", "Pn-3m", "Fm-3m", "Fm-3c",
570       "Fd-3m", "Fd-3c", "Im-3m", "Ia-3d"
571     };
572 
573     if (name.length () == 0)
574       {
575         if (_spaceGroup != nullptr)
576           return _spaceGroup->GetId();
577         else
578           name = _spaceGroupName;
579       }
580     static const int numStrings = sizeof( spacegroups ) / sizeof( spacegroups[0] );
581     for ( int i = 0; i < numStrings; ++i ) {
582       if (name == spacegroups[i] ) {
583         return i+1;
584       }
585     }
586     return 0; //presumably never reached
587   }
588 
589   // Whether two points (given in fractional coordinates) are close enough
590   // to be considered duplicates.
areDuplicateAtoms(vector3 v1,vector3 v2)591   bool areDuplicateAtoms (vector3 v1, vector3 v2)
592   {
593     vector3 dr = v2 - v1;
594     if (dr.x() < -0.5)
595       dr.SetX(dr.x() + 1);
596     if (dr.x() > 0.5)
597       dr.SetX(dr.x() - 1);
598     if (dr.y() < -0.5)
599       dr.SetY(dr.y() + 1);
600     if (dr.y() > 0.5)
601       dr.SetY(dr.y() - 1);
602     if (dr.z() < -0.5)
603       dr.SetZ(dr.z() + 1);
604     if (dr.z() > 0.5)
605       dr.SetZ(dr.z() - 1);
606 
607     return (dr.length_2() < 1e-6);
608   }
609 
FillUnitCell(OBMol * mol)610   void OBUnitCell::FillUnitCell(OBMol *mol)
611   {
612     const SpaceGroup *sg = GetSpaceGroup(); // the actual space group and transformations for this unit cell
613 
614     if (sg == nullptr)
615       return ;
616 
617     // For each atom, we loop through: convert the coords back to inverse space, apply the transformations and create new atoms
618     vector3 baseV, uniqueV, updatedCoordinate;
619     list<vector3> transformedVectors; // list of symmetry-defined copies of the atom
620     list<vector3>::iterator transformIter;
621     list<OBAtom*>::iterator deleteIter, atomIter;
622     OBAtom *newAtom;
623     list<OBAtom*> atoms, atomsToDelete;
624     char hash[22];
625     set<string> coordinateSet;
626 
627     // Check original mol for duplicates
628     FOR_ATOMS_OF_MOL(atom, *mol) {
629       baseV = atom->GetVector();
630       baseV = CartesianToFractional(baseV);
631       baseV = WrapFractionalCoordinate(baseV);
632       snprintf(hash, 22, "%03d,%.3f,%.3f,%.3f", atom->GetAtomicNum(), baseV.x(), baseV.y(), baseV.z());
633       if (coordinateSet.insert(hash).second) { // True if new entry
634         atoms.push_back(&(*atom));
635       } else {
636         atomsToDelete.push_back(&(*atom));
637       }
638     }
639     for (deleteIter = atomsToDelete.begin(); deleteIter != atomsToDelete.end(); ++deleteIter) {
640       mol->DeleteAtom(*deleteIter);
641     }
642 
643     // Cross-check all transformations for duplicity
644     for (atomIter = atoms.begin(); atomIter != atoms.end(); ++atomIter) {
645       uniqueV = (*atomIter)->GetVector();
646       uniqueV = CartesianToFractional(uniqueV);
647       uniqueV = WrapFractionalCoordinate(uniqueV);
648 
649       transformedVectors = sg->Transform(uniqueV);
650       for (transformIter = transformedVectors.begin();
651         transformIter != transformedVectors.end(); ++transformIter) {
652         updatedCoordinate = WrapFractionalCoordinate(*transformIter);
653 
654         // Check if the transformed coordinate is a duplicate of an atom
655         snprintf(hash, 22, "%03d,%.3f,%.3f,%.3f", (*atomIter)->GetAtomicNum(), updatedCoordinate.x(),
656                  updatedCoordinate.y(), updatedCoordinate.z());
657         if (coordinateSet.insert(hash).second) {
658           newAtom = mol->NewAtom();
659           newAtom->Duplicate(*atomIter);
660           newAtom->SetVector(FractionalToCartesian(updatedCoordinate));
661         }
662       } // end loop of transformed atoms
663     } // end loop of atoms
664     SetSpaceGroup(1); // We've now applied the symmetry, so we should act like a P1 unit cell
665   }
666 
667   /// @todo Remove nonconst overloads in OBUnitCell on next version bump.
668 #define OBUNITCELL_CALL_CONST_OVERLOAD(_type, _name) \
669   _type OBUnitCell::_name() \
670   { \
671     return const_cast<const OBUnitCell*>(this)->_name(); \
672   }
673 #define OBUNITCELL_CALL_CONST_OVERLOAD_ARG(_type, _name, _argsig) \
674   _type OBUnitCell::_name( _argsig arg1 ) \
675   { \
676     return const_cast<const OBUnitCell*>(this)->_name(arg1); \
677   }
678   OBUNITCELL_CALL_CONST_OVERLOAD(double, GetA);
679   OBUNITCELL_CALL_CONST_OVERLOAD(double, GetB);
680   OBUNITCELL_CALL_CONST_OVERLOAD(double, GetC);
681   OBUNITCELL_CALL_CONST_OVERLOAD(double, GetAlpha);
682   OBUNITCELL_CALL_CONST_OVERLOAD(double, GetBeta);
683   OBUNITCELL_CALL_CONST_OVERLOAD(double, GetGamma);
684   OBUNITCELL_CALL_CONST_OVERLOAD(vector3, GetOffset);
685   OBUNITCELL_CALL_CONST_OVERLOAD_ARG(OBUnitCell::LatticeType,
686                                      GetLatticeType, int);
687   OBUNITCELL_CALL_CONST_OVERLOAD(OBUnitCell::LatticeType, GetLatticeType);
688   OBUNITCELL_CALL_CONST_OVERLOAD(std::vector<vector3>, GetCellVectors);
689   OBUNITCELL_CALL_CONST_OVERLOAD(matrix3x3, GetCellMatrix );
690   OBUNITCELL_CALL_CONST_OVERLOAD(matrix3x3, GetOrthoMatrix );
691   OBUNITCELL_CALL_CONST_OVERLOAD(matrix3x3, GetOrientationMatrix );
692   OBUNITCELL_CALL_CONST_OVERLOAD(matrix3x3, GetFractionalMatrix );
693   OBUNITCELL_CALL_CONST_OVERLOAD_ARG(vector3, FractionalToCartesian, vector3);
694   OBUNITCELL_CALL_CONST_OVERLOAD_ARG(vector3, CartesianToFractional, vector3);
695   OBUNITCELL_CALL_CONST_OVERLOAD_ARG(vector3, WrapCartesianCoordinate, vector3);
696   OBUNITCELL_CALL_CONST_OVERLOAD_ARG(vector3, WrapFractionalCoordinate, vector3);
697   OBUNITCELL_CALL_CONST_OVERLOAD_ARG(vector3, MinimumImageCartesian, vector3);
698   OBUNITCELL_CALL_CONST_OVERLOAD_ARG(vector3, MinimumImageFractional, vector3);
699   OBUNITCELL_CALL_CONST_OVERLOAD_ARG(int, GetSpaceGroupNumber, std::string);
700   OBUNITCELL_CALL_CONST_OVERLOAD(double, GetCellVolume);
701   // Based on OBUNITCELL_CALL_CONST_OVERLOAD_ARG above
702 #define OBUNITCELL_CALL_CONST_OVERLOAD_ARG2(_type, _name, _argsig, _argsig2) \
703   _type OBUnitCell::_name( _argsig arg1, _argsig2 arg2 ) \
704   { \
705     return const_cast<const OBUnitCell*>(this)->_name(arg1, arg2); \
706   }
707   OBUNITCELL_CALL_CONST_OVERLOAD_ARG2(vector3, UnwrapCartesianNear, vector3, vector3);
708   OBUNITCELL_CALL_CONST_OVERLOAD_ARG2(vector3, UnwrapFractionalNear, vector3, vector3);
709 
GetA() const710   double OBUnitCell::GetA() const
711   {
712     return _mOrtho.GetColumn(0).length();
713   }
714 
GetB() const715   double OBUnitCell::GetB() const
716   {
717     return _mOrtho.GetColumn(1).length();
718   }
719 
GetC() const720   double OBUnitCell::GetC() const
721   {
722     return _mOrtho.GetColumn(2).length();
723   }
724 
GetAlpha() const725   double OBUnitCell::GetAlpha() const
726   {
727     return vectorAngle(_mOrtho.GetColumn(1), _mOrtho.GetColumn(2));
728   }
729 
GetBeta() const730   double OBUnitCell::GetBeta() const
731   {
732     return vectorAngle(_mOrtho.GetColumn(0), _mOrtho.GetColumn(2));
733   }
734 
GetGamma() const735   double OBUnitCell::GetGamma() const
736   {
737     return vectorAngle(_mOrtho.GetColumn(0), _mOrtho.GetColumn(1));
738   }
739 
GetOffset() const740   vector3 OBUnitCell::GetOffset() const
741   {
742     return _offset;
743   }
744 
GetCellVolume() const745   double OBUnitCell::GetCellVolume() const
746   {
747     return fabs(GetCellMatrix().determinant());
748   }
749 
750   //
751   // member functions for OBSymmetryData class
752   //
OBSymmetryData()753   OBSymmetryData::OBSymmetryData():
754     OBGenericData("Symmetry", OBGenericDataType::SymmetryData)
755   { }
756 
OBSymmetryData(const OBSymmetryData & src)757   OBSymmetryData::OBSymmetryData(const OBSymmetryData &src) :
758     OBGenericData(src._attr, src._type, src._source),
759     _spaceGroup(src._spaceGroup),
760     _pointGroup(src._pointGroup)
761   {  }
762 
operator =(const OBSymmetryData & src)763   OBSymmetryData & OBSymmetryData::operator=(const OBSymmetryData &src)
764   {
765     if(this == &src)
766       return(*this);
767 
768     _pointGroup = src._pointGroup;
769     _spaceGroup = src._spaceGroup;
770     _source = src._source;
771 
772     return(*this);
773   }
774 
OBConformerData()775   OBConformerData::OBConformerData() :
776     OBGenericData("Conformers", OBGenericDataType::ConformerData)
777   {  }
778 
OBConformerData(const OBConformerData & src)779   OBConformerData::OBConformerData(const OBConformerData &src) :
780     OBGenericData("Conformers", OBGenericDataType::ConformerData),
781     _vDimension(src._vDimension),
782     _vEnergies(src._vEnergies), _vForces(src._vForces),
783     _vVelocity(src._vVelocity), _vDisplace(src._vDisplace),
784     _vData(src._vData)
785   {  }
786 
operator =(const OBConformerData & src)787   OBConformerData & OBConformerData::operator=(const OBConformerData &src)
788   {
789     if(this == &src)
790       return(*this);
791 
792     _source = src._source;
793 
794     _vDimension = src._vDimension;
795     _vEnergies = src._vEnergies;
796     _vForces = src._vForces;
797     _vVelocity = src._vVelocity;
798     _vDisplace = src._vDisplace;
799     _vData = src._vData;
800 
801     return(*this);
802   }
803 
804   //
805   //member functions for OBRingData class
806   //
807 
OBRingData()808   OBRingData::OBRingData() :
809     OBGenericData("RingData", OBGenericDataType::RingData)
810   {
811     _vr.clear();
812   }
813 
814   /*!
815   **\brief OBRingData copy constructor
816   **\param src reference to original OBRingData object (rhs)
817   */
OBRingData(const OBRingData & src)818   OBRingData::OBRingData(const OBRingData &src)
819     :	OBGenericData(src),	//chain to base class
820       _vr(src._vr)				//chain to member classes
821   {
822     //no other memeber data
823     //memory management
824 
825     vector<OBRing*>::iterator ring;
826 
827     for(ring = _vr.begin();ring != _vr.end();++ring)
828       {
829         OBRing *newring = new OBRing;
830         (*newring) = (**ring);	//copy data to new object
831         (*ring)    = newring;	//repoint new pointer to new copy of data
832       }
833   }
834 
~OBRingData()835   OBRingData::~OBRingData()
836   {
837     vector<OBRing*>::iterator ring;
838     for (ring = _vr.begin();ring != _vr.end();++ring)
839       {
840         delete *ring;
841       }
842   }
843 
844   /*!
845   **\brief OBRingData assignment operator
846   **\param src reference to original OBRingData object (rhs)
847   **\return reference to changed OBRingData object (lhs)
848   */
operator =(const OBRingData & src)849   OBRingData& OBRingData::operator =(const OBRingData &src)
850   {
851     //on identity, return
852     if(this == &src)
853       return(*this);
854 
855     //chain to base class
856     OBGenericData::operator =(src);
857 
858     //member data
859 
860     //memory management
861     vector<OBRing*>::iterator ring;
862     for(ring = _vr.begin();ring != _vr.end();++ring)
863       {
864         delete &*ring;	//deallocate old rings to prevent memory leak
865       }
866 
867     _vr.clear();
868     _vr = src._vr;	//copy vector properties
869 
870     for(ring = _vr.begin();ring != _vr.end();++ring)
871       {
872         if(*ring == 0)
873           continue;
874 
875         //allocate and copy ring data
876         OBRing *newring = new OBRing;
877         (*newring) = (**ring);
878         (*ring) = newring;	//redirect pointer
879       }
880     return(*this);
881   }
882 
BeginRing(std::vector<OBRing * >::iterator & i)883   OBRing *OBRingData::BeginRing(std::vector<OBRing*>::iterator &i)
884   {
885     i = _vr.begin();
886     return i == _vr.end() ? nullptr : (OBRing*)*i;
887   }
888 
NextRing(std::vector<OBRing * >::iterator & i)889   OBRing *OBRingData::NextRing(std::vector<OBRing*>::iterator &i)
890   {
891     ++i;
892     return i == _vr.end() ? nullptr : (OBRing*)*i;
893   }
894 
895   //
896   //member functions for OBAngle class - stores all angles
897   //
898 
899   /*!
900   **\brief Angle default constructor
901   */
OBAngle()902   OBAngle::OBAngle():
903     _vertex(nullptr), _termini(nullptr, nullptr), _radians(0.0)
904   {  }
905 
906   /*!
907   **\brief Angle constructor
908   */
OBAngle(OBAtom * vertex,OBAtom * a,OBAtom * b)909   OBAngle::OBAngle(OBAtom *vertex,OBAtom *a,OBAtom *b):
910     _vertex(vertex), _termini(a, b)
911   {
912     SortByIndex();
913   }
914 
915   /*!
916   **\brief OBAngle copy constructor
917   */
OBAngle(const OBAngle & src)918   OBAngle::OBAngle(const OBAngle &src):
919     _vertex(src._vertex), _termini(src._termini), _radians(src._radians)
920   {  }
921 
922   /*!
923   **\brief OBAngle assignment operator
924   */
operator =(const OBAngle & src)925   OBAngle& OBAngle::operator = (const OBAngle &src)
926   {
927     if (this == &src)
928       return(*this);
929 
930     _vertex         = src._vertex;
931     _termini.first  = src._termini.first;
932     _termini.second = src._termini.second;
933     _radians        = src._radians;
934 
935     return(*this);
936   }
937 
938   /*!
939   **\brief Return OBAngle to its original state
940   */
Clear()941   void OBAngle::Clear()
942   {
943     _vertex         = nullptr;
944     _termini.first  = 0;
945     _termini.second = 0;
946     _radians        = 0.0;
947     return;
948   }
949 
950   /*!
951   **\brief Sets the 3 atoms in the angle
952   ** Parameters are pointers to each OBAtom
953   */
SetAtoms(OBAtom * vertex,OBAtom * a,OBAtom * b)954   void OBAngle::SetAtoms(OBAtom *vertex,OBAtom *a,OBAtom *b)
955   {
956     _vertex         = vertex;
957     _termini.first  = a;
958     _termini.second = b;
959     SortByIndex();
960     return;
961   }
962 
963   /*!
964   **\brief Sets the 3 atoms in the angle
965   **\param atoms a triple of OBAtom pointers, the first must be the vertex
966   */
SetAtoms(triple<OBAtom *,OBAtom *,OBAtom * > & atoms)967   void OBAngle::SetAtoms(triple<OBAtom*,OBAtom*,OBAtom*> &atoms)
968   {
969     _vertex         = atoms.first;
970     _termini.first  = atoms.second;
971     _termini.second = atoms.third;
972     SortByIndex();
973     return;
974   }
975 
976   /*!
977   **\brief Retrieves the 3 atom pointer for the angle (vertex first)
978   **\return triple of OBAtom pointers
979   */
GetAtoms()980   triple<OBAtom*,OBAtom*,OBAtom*> OBAngle::GetAtoms()
981   {
982     triple<OBAtom*,OBAtom*,OBAtom*> atoms;
983     atoms.first  = _vertex;
984     atoms.second = _termini.first;
985     atoms.third  = _termini.second;
986     return(atoms);
987   }
988 
989   /*!
990   **\brief sorts atoms in angle by order of indices
991   */
SortByIndex()992   void OBAngle::SortByIndex()
993   {
994     OBAtom *tmp;
995 
996     if(_termini.first->GetIdx() > _termini.second->GetIdx())
997       {
998         tmp             = _termini.first;
999         _termini.first  = _termini.second;
1000         _termini.second = tmp;
1001       }
1002   }
1003 
1004   /*!
1005   **\brief OBAngle equality operator, is same angle, NOT same value
1006   **\return boolean equality
1007   */
operator ==(const OBAngle & other)1008   bool OBAngle::operator ==(const OBAngle &other)
1009   {
1010     return ((_vertex         == other._vertex)        &&
1011             (_termini.first  == other._termini.first) &&
1012             (_termini.second == other._termini.second));
1013   }
1014 
1015   //
1016   //member functions for OBAngleData class - stores OBAngle set
1017   //
1018 
1019   /*!
1020   **\brief OBAngleData constructor
1021   */
OBAngleData()1022   OBAngleData::OBAngleData()
1023     :	OBGenericData("AngleData", OBGenericDataType::AngleData)
1024   {  }
1025 
1026   /*!
1027   **\brief OBAngleData copy constructor
1028   */
OBAngleData(const OBAngleData & src)1029   OBAngleData::OBAngleData(const OBAngleData &src)
1030     :	OBGenericData(src), _angles(src._angles)
1031   {  }
1032 
1033   /*!
1034   **\brief OBAngleData assignment operator
1035   */
operator =(const OBAngleData & src)1036   OBAngleData& OBAngleData::operator =(const OBAngleData &src)
1037   {
1038     if (this == &src)
1039       return(*this);
1040 
1041     _source = src._source;
1042     _angles = src._angles;
1043 
1044     return(*this);
1045   }
1046 
1047   /*!
1048   **\brief sets OBAngleData to its original state
1049   */
Clear()1050   void OBAngleData::Clear()
1051   {
1052     _angles.clear();
1053     return;
1054   }
1055 
1056   /*!
1057   **\brief Adds a new angle to OBAngleData
1058   */
SetData(OBAngle & angle)1059   void OBAngleData::SetData(OBAngle &angle)
1060   {
1061     _angles.push_back(angle);
1062     return;
1063   }
1064 
1065   /*!
1066   **\brief Fills an array with the indices of the atoms in the angle (vertex first)
1067   **\param angles pointer to the pointer to an array of angles atom indices
1068   **\return True if successful
1069   */
FillAngleArray(std::vector<std::vector<unsigned int>> & angles)1070   bool OBAngleData::FillAngleArray(std::vector<std::vector<unsigned int> > &angles)
1071   {
1072     if(_angles.empty())
1073       return(false);
1074 
1075     vector<OBAngle>::iterator angle;
1076 
1077     angles.clear();
1078     angles.resize(_angles.size());
1079 
1080     unsigned int ct = 0;
1081 
1082     for( angle=_angles.begin(); angle!=_angles.end(); ++angle,ct++)
1083       {
1084         angles[ct].resize(3);
1085         angles[ct][0] = angle->_vertex->GetIdx() - 1;
1086         angles[ct][1] = angle->_termini.first->GetIdx() - 1;
1087         angles[ct][2] = angle->_termini.second->GetIdx() - 1;
1088       }
1089 
1090     return(true);
1091   }
1092 
1093   /*!
1094   **\brief Fills an array with the indices of the atoms in the angle (vertex first)
1095   **\param angles pointer to the pointer to an array of angles atom indices
1096   **\param size the current number of rows in the array
1097   **\return int The number of angles
1098   */
FillAngleArray(int ** angles,unsigned int & size)1099   unsigned int OBAngleData::FillAngleArray(int **angles, unsigned int &size)
1100   {
1101     if(_angles.size() > size)
1102       {
1103         delete [] *angles;
1104         *angles = new int[_angles.size()*3];
1105         size    = (unsigned int)_angles.size();
1106       }
1107 
1108     vector<OBAngle>::iterator angle;
1109     int angleIdx = 0;
1110     for( angle=_angles.begin(); angle!=_angles.end(); ++angle)
1111       {
1112         *angles[angleIdx++] = angle->_vertex->GetIdx();
1113         *angles[angleIdx++] = angle->_termini.first->GetIdx();
1114         *angles[angleIdx++] = angle->_termini.second->GetIdx();
1115       }
1116     return (unsigned int)_angles.size();
1117   }
1118 
1119   //
1120   //member functions for OBAngleData class - stores OBAngle set
1121   //
1122 
1123   /*!
1124   **\brief OBTorsion constructor
1125   */
OBTorsion(OBAtom * a,OBAtom * b,OBAtom * c,OBAtom * d)1126   OBTorsion::OBTorsion(OBAtom *a,OBAtom *b, OBAtom *c,OBAtom *d)
1127   {
1128     triple<OBAtom*,OBAtom*,double> ad(a,d,0.0);
1129     _ads.push_back(ad);
1130 
1131     _bc.first  = b;
1132     _bc.second = c;
1133   }
1134 
1135   /*!
1136   **\brief OBTorsion copy constructor
1137   */
OBTorsion(const OBTorsion & src)1138   OBTorsion::OBTorsion(const OBTorsion &src)
1139     :	_bc(src._bc), _ads(src._ads)
1140   {}
1141 
1142   /*!
1143   **\brief Returns all the 4 atom sets in OBTorsion
1144   */
GetTorsions()1145   vector<quad<OBAtom*,OBAtom*,OBAtom*,OBAtom*> > OBTorsion::GetTorsions()
1146   {
1147     quad<OBAtom*,OBAtom*,OBAtom*,OBAtom*> abcd;
1148 
1149     abcd.second = _bc.first;
1150     abcd.third  = _bc.second;
1151 
1152     vector<quad<OBAtom*,OBAtom*,OBAtom*,OBAtom*> > torsions;
1153     vector<triple<OBAtom*,OBAtom*,double> >::iterator ad;
1154 
1155     for(ad = _ads.begin();ad != _ads.end();++ad)
1156       {
1157         abcd.first = ad->first;
1158         abcd.fourth = ad->second;
1159         torsions.push_back(abcd);
1160       }
1161 
1162     return(torsions);
1163   }
1164 
1165   /*!
1166   **\brief OBTorsion assignment operator
1167   */
operator =(const OBTorsion & src)1168   OBTorsion& OBTorsion::operator =(const OBTorsion &src)
1169   {
1170     if (this == &src)
1171       return(*this);
1172 
1173     _bc  = src._bc;
1174     _ads = src._ads;
1175 
1176     return(*this);
1177   }
1178 
1179   /*!
1180   **\brief Returns the OBTorsion to its original state
1181   */
Clear()1182   void OBTorsion::Clear()
1183   {
1184     _bc.first  = 0;
1185     _bc.second = 0;
1186     _ads.erase(_ads.begin(),_ads.end());
1187   }
1188 
1189   /*!
1190   **\brief Sets the angle of a torsion in OBTorsion
1191   **\param radians the value to assign to the torsion
1192   **\param index the index into the torsion of the OBTorsion
1193   **\return boolean success
1194   */
SetAngle(double radians,unsigned int index)1195   bool OBTorsion::SetAngle(double radians,unsigned int index)
1196   {
1197     if(index >= _ads.size())
1198       return(false);
1199 
1200     _ads[index].third = radians;
1201 
1202     return(true);
1203   }
1204 
1205   /*!
1206   **\brief Obtains the angle of a torsion in OBTorsion
1207   **\param radians the value of the angle is set here
1208   **\param index the index into the torsion of the OBTorsion
1209   **\return boolean success
1210   */
GetAngle(double & radians,unsigned int index)1211   bool OBTorsion::GetAngle(double &radians, unsigned int index)
1212   {
1213     if(index >= _ads.size())
1214       return false;
1215     radians = _ads[index].third;
1216     return true;
1217   }
1218 
GetBondIdx()1219   unsigned int OBTorsion::GetBondIdx()
1220   {
1221     return(_bc.first->GetBond(_bc.second)->GetIdx());
1222   }
1223 
1224   /*!
1225   **\brief determines if torsion has only protons on either the a or d end
1226   **\return boolean
1227   */
IsProtonRotor()1228   bool OBTorsion::IsProtonRotor()
1229   {
1230     bool Aprotor = true;
1231     bool Dprotor = true;
1232     vector<triple<OBAtom*,OBAtom*,double> >::iterator ad;
1233     for(ad = _ads.begin();ad != _ads.end() && (Aprotor || Dprotor);++ad)
1234       {
1235         if (ad->first->GetAtomicNum() != OBElements::Hydrogen)
1236           Aprotor = false;
1237         if (ad->second->GetAtomicNum() != OBElements::Hydrogen)
1238           Dprotor = false;
1239       }
1240     return (Aprotor || Dprotor);
1241   }
1242 
1243   /*!
1244   **\brief adds a new torsion to the OBTorsion object
1245   */
AddTorsion(OBAtom * a,OBAtom * b,OBAtom * c,OBAtom * d)1246   bool OBTorsion::AddTorsion(OBAtom *a,OBAtom *b, OBAtom *c,OBAtom *d)
1247   {
1248     if(!Empty() && (b != _bc.first || c != _bc.second))
1249       return(false);
1250 
1251     if(Empty())
1252       {
1253         _bc.first  = b;
1254         _bc.second = c;
1255       }
1256 
1257     triple<OBAtom*,OBAtom*,double> ad(a,d,0.0);
1258     _ads.push_back(ad);
1259 
1260     return(true);
1261   }
1262 
1263   /*!
1264   **\brief adds a new torsion to the OBTorsion object
1265   */
AddTorsion(quad<OBAtom *,OBAtom *,OBAtom *,OBAtom * > & atoms)1266   bool OBTorsion::AddTorsion(quad<OBAtom*,OBAtom*,OBAtom*,OBAtom*> &atoms)
1267   {
1268     if(!Empty() && (atoms.second != _bc.first || atoms.third != _bc.second))
1269       return(false);
1270 
1271     if(Empty())
1272       {
1273         _bc.first  = atoms.second;
1274         _bc.second = atoms.third;
1275       }
1276 
1277     triple<OBAtom*,OBAtom*,double> ad(atoms.first,atoms.fourth,0.0);
1278     _ads.push_back(ad);
1279 
1280     return(true);
1281   }
1282 
1283   //\!brief OBTorsionData ctor
OBTorsionData()1284   OBTorsionData::OBTorsionData()
1285     : OBGenericData("TorsionData", OBGenericDataType::TorsionData)
1286   {  }
1287 
1288   //
1289   //member functions for OBTorsionData class - stores OBTorsion set
1290   //
OBTorsionData(const OBTorsionData & src)1291   OBTorsionData::OBTorsionData(const OBTorsionData &src)
1292     :	OBGenericData(src), _torsions(src._torsions)
1293   {  }
1294 
operator =(const OBTorsionData & src)1295   OBTorsionData& OBTorsionData::operator =(const OBTorsionData &src)
1296   {
1297     if (this == &src)
1298       return(*this);
1299 
1300     OBGenericData::operator =(src);
1301 
1302     _source = src._source;
1303     _torsions = src._torsions;
1304 
1305     return(*this);
1306   }
1307 
Clear()1308   void OBTorsionData::Clear()
1309   {
1310     _torsions.clear();
1311   }
1312 
SetData(OBTorsion & torsion)1313   void OBTorsionData::SetData(OBTorsion &torsion)
1314   {
1315     _torsions.push_back(torsion);
1316   }
1317 
1318   /*!
1319   **\brief Fills a vector with the indices of the atoms in torsions (ordered abcd)
1320   **\param torsions reference to the vector of abcd atom sets
1321   **\return boolean success
1322   */
FillTorsionArray(std::vector<std::vector<unsigned int>> & torsions)1323   bool OBTorsionData::FillTorsionArray(std::vector<std::vector<unsigned int> > &torsions)
1324   {
1325     if(_torsions.empty())
1326       return(false);
1327 
1328     vector<quad<OBAtom*,OBAtom*,OBAtom*,OBAtom*> > tmpquads,quads;
1329     vector<quad<OBAtom*,OBAtom*,OBAtom*,OBAtom*> >::iterator thisQuad;
1330     vector<OBTorsion>::iterator torsion;
1331 
1332     //generate set of all 4 atom abcd's from torsion structure
1333     for (torsion = _torsions.begin();torsion != _torsions.end();++torsion)
1334       {
1335         tmpquads = torsion->GetTorsions();
1336         for(thisQuad = tmpquads.begin();thisQuad != tmpquads.end();++thisQuad)
1337           quads.push_back(*thisQuad);
1338       }
1339 
1340     //fill array of torsion atoms
1341 
1342     torsions.clear();
1343     torsions.resize(quads.size());
1344 
1345     unsigned int ct = 0;
1346 
1347     for (thisQuad = quads.begin();thisQuad != quads.end();++thisQuad,++ct)
1348       {
1349         torsions[ct].resize(4);
1350         torsions[ct][0] = thisQuad->first->GetIdx()-1;
1351         torsions[ct][1] = thisQuad->second->GetIdx()-1;
1352         torsions[ct][2] = thisQuad->third->GetIdx()-1;
1353         torsions[ct][3] = thisQuad->fourth->GetIdx()-1;
1354       }
1355 
1356     return(true);
1357   }
1358 
1359 
1360 //
1361 //member functions for OBDOSData class
1362 //
1363 
1364 /*!
1365 **\brief Assign the data
1366 **\param fermi The Fermi energy in eV
1367 **\param vEnergies Energy levels in eV
1368 **\param vDensities Density of states in (number of states) / (unit cell)
1369 **\param vIntegration Integrated DOS
1370 */
SetData(double fermi,const std::vector<double> & vEnergies,const std::vector<double> & vDensities,const std::vector<double> & vIntegration)1371 void OBDOSData::SetData(double fermi,
1372                         const std::vector<double> & vEnergies,
1373                         const std::vector<double> & vDensities,
1374                         const std::vector<double> & vIntegration)
1375 {
1376   this->_fermi = fermi;
1377   this->_vEnergies = vEnergies;
1378   this->_vIntegration = vIntegration;
1379   this->_vDensities = vDensities;
1380 }
1381 
1382   // member functions for OBOrbitalData
1383 
LoadClosedShellOrbitals(std::vector<double> energies,std::vector<std::string> symmetries,unsigned int alphaHOMO)1384   void OBOrbitalData::LoadClosedShellOrbitals(std::vector<double> energies, std::vector<std::string> symmetries, unsigned int alphaHOMO)
1385   {
1386     if (energies.size() < symmetries.size())
1387       return; // something is very weird -- it's OK to pass no symmetries (we'll assume "A")
1388     if (energies.size() == 0)
1389       return;
1390     if (alphaHOMO > energies.size())
1391       return;
1392 
1393     _alphaHOMO = alphaHOMO;
1394     _alphaOrbitals.clear();
1395     _betaHOMO = 0;
1396     _betaOrbitals.clear();
1397     _openShell = false;
1398 
1399     if (symmetries.size() < energies.size()) // pad with "A" symmetry
1400       for (unsigned int i = symmetries.size(); i < energies.size(); ++i)
1401         symmetries.push_back("A");
1402 
1403     OBOrbital currentOrbital;
1404     for (unsigned int i = 0; i < energies.size(); ++i)
1405       {
1406         if (i < alphaHOMO)
1407           currentOrbital.SetData(energies[i], 2.0, symmetries[i]);
1408         else
1409           currentOrbital.SetData(energies[i], 0.0, symmetries[i]);
1410 
1411         _alphaOrbitals.push_back(currentOrbital);
1412       }
1413   }
1414 
LoadAlphaOrbitals(std::vector<double> energies,std::vector<std::string> symmetries,unsigned int alphaHOMO)1415   void OBOrbitalData::LoadAlphaOrbitals(std::vector<double> energies, std::vector<std::string> symmetries, unsigned int alphaHOMO)
1416   {
1417     if (energies.size() < symmetries.size())
1418       return; // something is very weird -- it's OK to pass no symmetries (we'll assume "A")
1419     if (energies.size() == 0)
1420       return;
1421     if (alphaHOMO > energies.size())
1422       return;
1423 
1424     _alphaHOMO = alphaHOMO;
1425     _alphaOrbitals.clear();
1426     _openShell = true;
1427 
1428     if (symmetries.size() < energies.size()) // pad with "A" symmetry
1429       for (unsigned int i = symmetries.size(); i < energies.size(); ++i)
1430         symmetries.push_back("A");
1431 
1432     OBOrbital currentOrbital;
1433     for (unsigned int i = 0; i < energies.size(); ++i)
1434       {
1435         if (i < alphaHOMO)
1436           currentOrbital.SetData(energies[i], 2.0, symmetries[i]);
1437         else
1438           currentOrbital.SetData(energies[i], 0.0, symmetries[i]);
1439 
1440         _alphaOrbitals.push_back(currentOrbital);
1441       }
1442   }
1443 
LoadBetaOrbitals(std::vector<double> energies,std::vector<std::string> symmetries,unsigned int betaHOMO)1444   void OBOrbitalData::LoadBetaOrbitals(std::vector<double> energies, std::vector<std::string> symmetries, unsigned int betaHOMO)
1445   {
1446     if (energies.size() < symmetries.size())
1447       return; // something is very weird -- it's OK to pass no symmetries (we'll assume "A")
1448     if (energies.size() == 0)
1449       return;
1450     if (betaHOMO > energies.size())
1451       return;
1452 
1453     _betaHOMO = betaHOMO;
1454     _betaOrbitals.clear();
1455     _openShell = true;
1456 
1457     if (symmetries.size() < energies.size()) // pad with "A" symmetry
1458       for (unsigned int i = symmetries.size(); i < energies.size(); ++i)
1459         symmetries.push_back("A");
1460 
1461     OBOrbital currentOrbital;
1462     for (unsigned int i = 0; i < energies.size(); ++i)
1463       {
1464         if (i < betaHOMO)
1465           currentOrbital.SetData(energies[i], 2.0, symmetries[i]);
1466         else
1467           currentOrbital.SetData(energies[i], 0.0, symmetries[i]);
1468 
1469         _betaOrbitals.push_back(currentOrbital);
1470       }
1471   }
1472 
1473 //
1474 //member functions for OBElectronicTransitionData class
1475 //
1476 
1477 /*!
1478 **\brief Assign the basic excitation data
1479 **\param vWavelengths Wavelengths in nm
1480 **\param vForces Oscillator strengths
1481 */
SetData(const std::vector<double> & vWavelengths,const std::vector<double> & vForces)1482 void OBElectronicTransitionData::SetData(const std::vector<double> & vWavelengths,
1483                                   const std::vector<double> & vForces)
1484 {
1485   this->_vWavelengths = vWavelengths;
1486   this->_vForces = vForces;
1487 }
1488 
1489 /*!
1490 **\brief Assign the electronic dipole strengths
1491 **\param vEDipole Electronic dipole moment strength
1492 */
SetEDipole(const std::vector<double> & vEDipole)1493 void OBElectronicTransitionData::SetEDipole(const std::vector<double> & vEDipole)
1494 {
1495   this->_vEDipole = vEDipole;
1496 }
1497 
1498 /*!
1499 **\brief Assign the rotatory strengths (velocity)
1500 **\param vRotatoryStrengthsVelocity Vector containing the rotatory strengths
1501 */
SetRotatoryStrengthsVelocity(const std::vector<double> & vRotatoryStrengthsVelocity)1502 void OBElectronicTransitionData::SetRotatoryStrengthsVelocity(const std::vector<double> & vRotatoryStrengthsVelocity)
1503 {
1504   this->_vRotatoryStrengthsVelocity = vRotatoryStrengthsVelocity;
1505 }
1506 
1507 /*!
1508 **\brief Assign the rotatory strengths (length)
1509 **\param vRotatoryStrengthsLength Vector containing the rotatory strengths
1510 */
SetRotatoryStrengthsLength(const std::vector<double> & vRotatoryStrengthsLength)1511 void OBElectronicTransitionData::SetRotatoryStrengthsLength(const std::vector<double> & vRotatoryStrengthsLength)
1512 {
1513   this->_vRotatoryStrengthsLength = vRotatoryStrengthsLength;
1514 }
1515 
1516 //
1517 //member functions for OBVibrationData class
1518 //
1519 
1520 /*!
1521 **\brief Assign the data
1522 **\param vLx Normal modes in 1/sqrt(a.u.)
1523 **\param vFrequencies Harmonic frequencies in inverse centimeters
1524 **\param vIntensities Infrared absorption intensities in KM/Mole
1525 */
SetData(const std::vector<std::vector<vector3>> & vLx,const std::vector<double> & vFrequencies,const std::vector<double> & vIntensities)1526 void OBVibrationData::SetData(const std::vector< std::vector< vector3 > > & vLx,
1527                               const std::vector<double> & vFrequencies,
1528                               const std::vector<double> & vIntensities)
1529 {
1530   this->_vLx = vLx;
1531   this->_vFrequencies = vFrequencies;
1532   this->_vIntensities = vIntensities;
1533 }
1534 
1535 /*!
1536 **\brief Assign the data
1537 **\param vLx Normal modes in 1/sqrt(a.u.)
1538 **\param vFrequencies Harmonic frequencies in inverse centimeters
1539 **\param vIntensities Infrared absorption intensities in KM/Mole
1540 **\param vRamanActivities Raman activities
1541 */
SetData(const std::vector<std::vector<vector3>> & vLx,const std::vector<double> & vFrequencies,const std::vector<double> & vIntensities,const std::vector<double> & vRamanActivities)1542 void OBVibrationData::SetData(const std::vector< std::vector< vector3 > > & vLx,
1543                               const std::vector<double> & vFrequencies,
1544                               const std::vector<double> & vIntensities,
1545                               const std::vector<double> & vRamanActivities)
1546 {
1547   this->_vLx = vLx;
1548   this->_vFrequencies = vFrequencies;
1549   this->_vIntensities = vIntensities;
1550   this->_vRamanActivities = vRamanActivities;
1551 }
1552 
1553 
1554 /*!
1555 **\brief Get the number of frequencies
1556 */
GetNumberOfFrequencies() const1557 unsigned int OBVibrationData::GetNumberOfFrequencies() const
1558 {
1559   return this->_vFrequencies.size();
1560 }
1561 
Clear()1562 void OBFreeGrid::Clear()
1563 {
1564   _points.clear();
1565 }
1566 
1567 } //end namespace OpenBabel
1568 
1569 //! \file generic.cpp
1570 //! \brief Handle OBGenericData classes. Custom data for atoms, bonds, etc.
1571