Coverage Report

Created: 2025-08-26 06:55

/src/openbabel/src/generic.cpp
Line
Count
Source (jump to first uncovered line)
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
166
  OBGenericData::OBGenericData(const std::string attr, const unsigned int type,
167
                               const DataOrigin  source):
168
14.4M
    _attr(attr), _type(type), _source(source)
169
14.4M
  { }
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
195
  OBCommentData::OBCommentData():
196
0
    OBGenericData("Comment", OBGenericDataType::CommentData)
197
0
  { }
198
199
  OBCommentData::OBCommentData(const OBCommentData &src) :
200
0
    OBGenericData(src), _data(src._data)
201
0
  {  }
202
203
  //
204
  //member functions for OBExternalBond class
205
  //
206
  OBExternalBond::OBExternalBond(OBAtom *atom,OBBond *bond,int idx):
207
237
    _idx(idx), _atom(atom), _bond(bond)
208
237
  {  }
209
210
  OBExternalBond::OBExternalBond(const OBExternalBond &src):
211
402
    _idx(src._idx), _atom(src._atom), _bond(src._bond)
212
402
  { }
213
214
  //
215
  //member functions for OBExternalBondData class
216
  //
217
218
  OBExternalBondData::OBExternalBondData():
219
110
    OBGenericData("ExternalBondData", OBGenericDataType::ExternalBondData,
220
110
                  perceived)
221
110
  { }
222
223
  void OBExternalBondData::SetData(OBAtom *atom,OBBond *bond,int idx)
224
237
  {
225
237
    OBExternalBond xb(atom,bond,idx);
226
237
    _vexbnd.push_back(xb);
227
237
  }
228
229
  //
230
  //member functions for OBPairData class
231
  //
232
233
  OBPairData::OBPairData() :
234
0
    OBGenericData("PairData", OBGenericDataType::PairData)
235
0
  { }
236
237
  //
238
  //member functions for OBVirtualBond class
239
  //
240
241
  OBVirtualBond::OBVirtualBond():
242
0
    OBGenericData("VirtualBondData", OBGenericDataType::VirtualBondData, perceived),
243
0
    _bgn(0), _end(0), _ord(0), _stereo(0)
244
0
  {  }
245
246
  OBVirtualBond::OBVirtualBond(unsigned int bgn, unsigned int end, unsigned int ord, int stereo):
247
0
    OBGenericData("VirtualBondData", OBGenericDataType::VirtualBondData, perceived),
248
0
    _bgn(bgn), _end(end), _ord(ord), _stereo(stereo)
249
0
  {  }
250
251
  //
252
  // member functions for OBUnitCell class
253
  //
254
  OBUnitCell::OBUnitCell():
255
0
    OBGenericData("UnitCell", OBGenericDataType::UnitCell),
256
0
    _mOrtho(matrix3x3()),
257
0
    _mOrient(matrix3x3()),
258
0
    _offset(vector3()),
259
0
    _spaceGroupName(""), _spaceGroup(nullptr),
260
0
    _lattice(Undefined)
261
0
  {  }
262
263
  OBUnitCell::OBUnitCell(const OBUnitCell &src) :
264
0
    OBGenericData("UnitCell", OBGenericDataType::UnitCell),
265
0
    _mOrtho(src._mOrtho),
266
0
    _mOrient(src._mOrient),
267
0
    _offset(src._offset),
268
0
    _spaceGroupName(src._spaceGroupName), _spaceGroup(src._spaceGroup),
269
0
    _lattice(src._lattice)
270
0
  {  }
271
272
  OBUnitCell & OBUnitCell::operator=(const OBUnitCell &src)
273
0
  {
274
0
    if(this == &src)
275
0
      return(*this);
276
277
0
    _mOrtho = src._mOrtho;
278
0
    _mOrient = src._mOrient;
279
0
    _offset = src._offset;
280
281
0
    _spaceGroup = src._spaceGroup;
282
0
    _spaceGroupName = src._spaceGroupName;
283
0
    _lattice = src._lattice;
284
285
0
    return(*this);
286
0
  }
287
288
  //! Implements <a href="http://qsar.sourceforge.net/dicts/blue-obelisk/index.xhtml#calculateOrthogonalisationMatrix">blue-obelisk:calculateOrthogonalisationMatrix</a>
289
  void OBUnitCell::SetData(const double a, const double b, const double c,
290
                           const double alpha, const double beta, const double gamma)
291
0
  {
292
0
    _mOrtho.FillOrth(alpha, beta, gamma, a, b, c);
293
0
    _mOrient = matrix3x3(1);
294
0
    _spaceGroup = nullptr;
295
0
    _spaceGroupName = "";
296
0
    _lattice = OBUnitCell::Undefined;
297
0
  }
298
299
  //! Implements <a href="http://qsar.sourceforge.net/dicts/blue-obelisk/index.xhtml#calculateOrthogonalisationMatrix">blue-obelisk:calculateOrthogonalisationMatrix</a>
300
  void OBUnitCell::SetData(const vector3 v1, const vector3 v2, const vector3 v3)
301
0
  {
302
0
    matrix3x3 m (v1, v2, v3);
303
0
    _mOrtho.FillOrth(vectorAngle(v2,v3), // alpha
304
0
                     vectorAngle(v1,v3), // beta
305
0
                     vectorAngle(v1,v2), // gamma
306
0
                     v1.length(),        // a
307
0
                     v2.length(),        // b
308
0
                     v3.length());       // c
309
0
    _mOrient = m.transpose() * _mOrtho.inverse();
310
0
    _spaceGroup = nullptr;
311
0
    _spaceGroupName = "";
312
0
    _lattice = OBUnitCell::Undefined;
313
0
  }
314
315
  //! Implements <a href="http://qsar.sourceforge.net/dicts/blue-obelisk/index.xhtml#calculateOrthogonalisationMatrix">blue-obelisk:calculateOrthogonalisationMatrix</a>
316
  void OBUnitCell::SetData(const matrix3x3 m)
317
0
  {
318
0
    SetData(m.GetRow(0), m.GetRow(1), m.GetRow(2));
319
0
  }
320
321
  void OBUnitCell::SetOffset(const vector3 v1)
322
0
  {
323
0
    _offset = v1;
324
0
  }
325
326
  //! Implements <a href="http://qsar.sourceforge.net/dicts/blue-obelisk/index.xhtml#convertNotionalIntoCartesianCoordinates">blue-obelisk:convertNotionalIntoCartesianCoordinates</a>
327
  vector<vector3> OBUnitCell::GetCellVectors() const
328
0
  {
329
0
    vector<vector3> v;
330
0
    v.reserve(3);
331
332
0
    matrix3x3 m = GetCellMatrix();
333
334
0
    v.push_back(m.GetRow(0));
335
0
    v.push_back(m.GetRow(1));
336
0
    v.push_back(m.GetRow(2));
337
338
0
    return v;
339
0
  }
340
341
  matrix3x3 OBUnitCell::GetCellMatrix() const
342
0
  {
343
0
    return (_mOrient * _mOrtho).transpose();
344
0
  }
345
346
  matrix3x3 OBUnitCell::GetOrthoMatrix() const
347
0
  {
348
0
    return _mOrtho;
349
0
  }
350
351
  matrix3x3 OBUnitCell::GetOrientationMatrix() const
352
0
  {
353
0
    return _mOrient;
354
0
  }
355
356
  matrix3x3 OBUnitCell::GetFractionalMatrix() const
357
0
  {
358
0
    return _mOrtho.inverse();
359
0
  }
360
361
  vector3 OBUnitCell::FractionalToCartesian(vector3 frac) const
362
0
  {
363
0
    return _mOrient * _mOrtho * frac + _offset;
364
0
  }
365
366
  vector3 OBUnitCell::CartesianToFractional(vector3 cart) const
367
0
  {
368
0
    return _mOrtho.inverse() * _mOrient.inverse() * (cart - _offset);
369
0
  }
370
371
  vector3 OBUnitCell::WrapCartesianCoordinate(vector3 cart) const
372
0
  {
373
0
    vector3 v = CartesianToFractional(cart);
374
0
    v = WrapFractionalCoordinate(v);
375
0
    return FractionalToCartesian(v);
376
0
  }
377
378
  vector3 OBUnitCell::WrapFractionalCoordinate(vector3 frac) const
379
0
  {
380
0
    double x = fmod(frac.x(), 1);
381
0
    double y = fmod(frac.y(), 1);
382
0
    double z = fmod(frac.z(), 1);
383
0
    if (x < 0) x += 1;
384
0
    if (y < 0) y += 1;
385
0
    if (z < 0) z += 1;
386
387
0
#define LIMIT 0.999999
388
0
    if (x > LIMIT)
389
0
      x -= 1;
390
0
    if (y > LIMIT)
391
0
      y -= 1;
392
0
    if (z > LIMIT)
393
0
      z -= 1;
394
0
#undef LIMIT
395
396
    // Fuzzy logic from Francois-Xavier
397
0
#define EPSILON 1.0e-6
398
0
    if (x > 1 - EPSILON || x < EPSILON)
399
0
      x = 0.0;
400
0
    if (y > 1 - EPSILON || y < EPSILON)
401
0
      y = 0.0;
402
0
    if (z > 1 - EPSILON || z < EPSILON)
403
0
      z = 0.0;
404
0
#undef EPSILON
405
406
0
    return vector3(x, y, z);
407
0
  }
408
409
  vector3 OBUnitCell::UnwrapCartesianNear(vector3 new_loc, vector3 ref_loc) const
410
0
  {
411
0
    vector3 bond_dir = MinimumImageCartesian(new_loc - ref_loc);
412
0
    return ref_loc + bond_dir;
413
0
  }
414
415
  vector3 OBUnitCell::UnwrapFractionalNear(vector3 new_loc, vector3 ref_loc) const
416
0
  {
417
0
    vector3 bond_dir = MinimumImageFractional(new_loc - ref_loc);
418
0
    return ref_loc + bond_dir;
419
0
  }
420
421
  vector3 OBUnitCell::MinimumImageCartesian(vector3 cart) const
422
0
  {
423
0
    vector3 frac = CartesianToFractional(cart);
424
0
    frac = MinimumImageFractional(frac);
425
0
    return FractionalToCartesian(frac);
426
0
  }
427
428
  vector3 OBUnitCell::MinimumImageFractional(vector3 frac) const
429
0
  {
430
0
    double x = frac.x() - round(frac.x());
431
0
    double y = frac.y() - round(frac.y());
432
0
    double z = frac.z() - round(frac.z());
433
0
    return vector3(x, y, z);
434
0
  }
435
436
  OBUnitCell::LatticeType OBUnitCell::GetLatticeType( int spacegroup ) const
437
0
  {
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
0
    if ( spacegroup == 0  && _spaceGroup)
447
0
      spacegroup = _spaceGroup->GetId();
448
449
0
    if ( spacegroup <= 0 )
450
0
      return OBUnitCell::Undefined;
451
452
0
    else if ( spacegroup == 1 ||
453
0
              spacegroup == 2 )
454
0
      return OBUnitCell::Triclinic;
455
456
0
    else if ( spacegroup >= 3 &&
457
0
              spacegroup <= 15 )
458
0
      return OBUnitCell::Monoclinic;
459
460
0
    else if ( spacegroup >= 16 &&
461
0
              spacegroup <= 74 )
462
0
      return OBUnitCell::Orthorhombic;
463
464
0
    else if ( spacegroup >= 75 &&
465
0
              spacegroup <= 142 )
466
0
      return OBUnitCell::Tetragonal;
467
468
0
    else if ( spacegroup >= 143 &&
469
0
              spacegroup <= 167 )
470
0
      return OBUnitCell::Rhombohedral;
471
472
0
    else if ( spacegroup >= 168 &&
473
0
              spacegroup <= 194 )
474
0
      return OBUnitCell::Hexagonal;
475
476
0
    else if ( spacegroup >= 195 &&
477
0
              spacegroup <= 230 )
478
0
      return OBUnitCell::Cubic;
479
480
    //just to be extra sure
481
0
    else // ( spacegroup > 230 )
482
0
      return OBUnitCell::Undefined;
483
0
  }
484
485
  OBUnitCell::LatticeType OBUnitCell::GetLatticeType() const
486
0
  {
487
0
    if (_lattice != Undefined)
488
0
      return _lattice;
489
0
    else if (_spaceGroup != nullptr)
490
0
      return GetLatticeType(_spaceGroup->GetId());
491
492
0
    double a = GetA();
493
0
    double b = GetB();
494
0
    double c = GetC();
495
0
    double alpha = GetAlpha();
496
0
    double beta  = GetBeta();
497
0
    double gamma = GetGamma();
498
499
0
    unsigned int rightAngles = 0;
500
0
    if (IsApprox(alpha, 90.0, 1.0e-3)) rightAngles++;
501
0
    if (IsApprox(beta,  90.0, 1.0e-3)) rightAngles++;
502
0
    if (IsApprox(gamma, 90.0, 1.0e-3)) rightAngles++;
503
504
    // recast cache member "_lattice" as mutable
505
0
    OBUnitCell::LatticeType *lattice =
506
0
      const_cast<OBUnitCell::LatticeType*>(&_lattice);
507
508
0
    switch (rightAngles)
509
0
      {
510
0
      case 3:
511
0
        if (IsApprox(a, b, 1.0e-4) && IsApprox(b, c, 1.0e-4))
512
0
          *lattice = Cubic;
513
0
        else if (IsApprox(a, b, 1.0e-4) || IsApprox(b, c, 1.0e-4))
514
0
          *lattice = Tetragonal;
515
0
        else
516
0
          *lattice = Orthorhombic;
517
0
        break;
518
0
      case 2:
519
0
        if ( (IsApprox(alpha, 120.0, 1.0e-3)
520
0
              || IsApprox(beta, 120.0, 1.0e-3)
521
0
              || IsApprox(gamma, 120.0f, 1.0e-3))
522
0
             && (IsApprox(a, b, 1.0e-4) || IsApprox(b, c, 1.0e-4)) )
523
0
          *lattice = Hexagonal;
524
0
        else
525
0
          *lattice = Monoclinic;
526
0
        break;
527
0
      default:
528
0
        if (IsApprox(a, b, 1.0e-4) && IsApprox(b, c, 1.0e-4))
529
0
          *lattice = Rhombohedral;
530
0
        else
531
0
          *lattice = Triclinic;
532
0
      }
533
534
0
    return *lattice;
535
0
  }
536
537
  int OBUnitCell::GetSpaceGroupNumber( std::string name) const
538
0
  {
539
0
    static const char * const spacegroups[] = {
540
0
      "P1", "P-1", "P2", "P2(1)", "C2", "Pm", "Pc", "Cm", "Cc", "P2/m",
541
0
      "P2(1)/m", "C2/m", "P2/c", "P2(1)/c", "C2/c", "P222", "P222(1)",
542
0
      "P2(1)2(1)2", "P2(1)2(1)2(1)", "C222(1)", "C222", "F222", "I222",
543
0
      "I2(1)2(1)2(1)", "Pmm2", "Pmc2(1)", "Pcc2", "Pma2", "Pca2(1)", "Pnc2",
544
0
      "Pmn2(1)", "Pba2", "Pna2(1)", "Pnn2", "Cmm2", "Cmc2(1)", "Ccc2", "Amm2",
545
0
      "Abm2", "Ama2", "Aba2", "Fmm2", "Fdd2", "Imm2", "Iba2", "Ima2", "Pmmm",
546
0
      "Pnnn", "Pccm", "Pban", "Pmma", "Pnna", "Pmna", "Pcca", "Pbam", "Pccn",
547
0
      "Pbcm", "Pnnm", "Pmmn", "Pbcn", "Pbca", "Pnma", "Cmcm", "Cmca", "Cmmm",
548
0
      "Cccm", "Cmma", "Ccca", "Fmmm", "Fddd", "Immm", "Ibam", "Ibca", "Imma",
549
0
      "P4", "P4(1)", "P4(2)", "P4(3)", "I4", "I4(1)", "P-4", "I-4", "P4/m",
550
0
      "P4(2)/m", "P4/n", "P4(2)/n", "I4/m", "I4(1)/a", "P422", "P42(1)2",
551
0
      "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
0
      "I422", "I4(1)22", "P4mm", "P4bm", "P4(2)cm", "P4(2)nm", "P4cc", "P4nc",
553
0
      "P4(2)mc", "P4(2)bc", "I4mm", "I4cm", "I4(1)md", "I4(1)cd", "P-42m",
554
0
      "P-42c", "P-42(1)m", "P-42(1)c", "P-4m2", "P-4c2", "P-4b2", "P-4n2",
555
0
      "I-4m2", "I-4c2", "I-42m", "I-42d", "P4/mmm", "P4/mcc", "P4/nbm",
556
0
      "P4/nnc", "P4/mbm", "P4/mnc", "P4/nmm", "P4/ncc", "P4(2)/mmc",
557
0
      "P4(2)/mcm", "P4(2)/nbc", "P4(2)/nnm", "P4(2)/mbc", "P4(2)/mnm",
558
0
      "P4(2)/nmc", "P4(2)/ncm", "I4/mmm", "I4/mcm", "I4(1)/amd", "I4(1)/acd",
559
0
      "P3", "P3(1)", "P3(2)", "R3", "P-3", "R-3", "P312", "P321", "P3(1)12",
560
0
      "P3(1)21", "P3(2)12", "P3(2)21", "R32", "P3m1", "P31m", "P3c1", "P31c",
561
0
      "R3m", "R3c", "P-31m", "P-31c", "P-3m1", "P-3c1", "R-3m", "R-3c", "P6",
562
0
      "P6(1)", "P6(5)", "P6(2)", "P6(4)", "P6(3)", "P-6", "P6/m", "P6(3)/m",
563
0
      "P622", "P6(1)22", "P6(5)22", "P6(2)22", "P6(4)22", "P6(3)22", "P6mm",
564
0
      "P6cc", "P6(3)cm", "P6(3)mc", "P-6m2", "P-6c2", "P-62m", "P-62c",
565
0
      "P6/mmm", "P6/mcc", "P6(3)/mcm", "P6(3)/mmc", "P23", "F23", "I23",
566
0
      "P2(1)3", "I2(1)3", "Pm-3", "Pn-3", "Fm-3", "Fd-3", "Im-3", "Pa-3",
567
0
      "Ia-3", "P432", "P4(2)32", "F432", "F4(1)32", "I432", "P4(3)32",
568
0
      "P4(1)32", "I4(1)32", "P-43m", "F4-3m", "I-43m", "P-43n", "F-43c",
569
0
      "I-43d", "Pm-3m", "Pn-3n", "Pm-3n", "Pn-3m", "Fm-3m", "Fm-3c",
570
0
      "Fd-3m", "Fd-3c", "Im-3m", "Ia-3d"
571
0
    };
572
573
0
    if (name.length () == 0)
574
0
      {
575
0
        if (_spaceGroup != nullptr)
576
0
          return _spaceGroup->GetId();
577
0
        else
578
0
          name = _spaceGroupName;
579
0
      }
580
0
    static const int numStrings = sizeof( spacegroups ) / sizeof( spacegroups[0] );
581
0
    for ( int i = 0; i < numStrings; ++i ) {
582
0
      if (name == spacegroups[i] ) {
583
0
        return i+1;
584
0
      }
585
0
    }
586
0
    return 0; //presumably never reached
587
0
  }
588
589
  // Whether two points (given in fractional coordinates) are close enough
590
  // to be considered duplicates.
591
  bool areDuplicateAtoms (vector3 v1, vector3 v2)
592
0
  {
593
0
    vector3 dr = v2 - v1;
594
0
    if (dr.x() < -0.5)
595
0
      dr.SetX(dr.x() + 1);
596
0
    if (dr.x() > 0.5)
597
0
      dr.SetX(dr.x() - 1);
598
0
    if (dr.y() < -0.5)
599
0
      dr.SetY(dr.y() + 1);
600
0
    if (dr.y() > 0.5)
601
0
      dr.SetY(dr.y() - 1);
602
0
    if (dr.z() < -0.5)
603
0
      dr.SetZ(dr.z() + 1);
604
0
    if (dr.z() > 0.5)
605
0
      dr.SetZ(dr.z() - 1);
606
607
0
    return (dr.length_2() < 1e-6);
608
0
  }
609
610
  void OBUnitCell::FillUnitCell(OBMol *mol)
611
0
  {
612
0
    const SpaceGroup *sg = GetSpaceGroup(); // the actual space group and transformations for this unit cell
613
614
0
    if (sg == nullptr)
615
0
      return ;
616
617
    // For each atom, we loop through: convert the coords back to inverse space, apply the transformations and create new atoms
618
0
    vector3 baseV, uniqueV, updatedCoordinate;
619
0
    list<vector3> transformedVectors; // list of symmetry-defined copies of the atom
620
0
    list<vector3>::iterator transformIter;
621
0
    list<OBAtom*>::iterator deleteIter, atomIter;
622
0
    OBAtom *newAtom;
623
0
    list<OBAtom*> atoms, atomsToDelete;
624
0
    char hash[22];
625
0
    set<string> coordinateSet;
626
627
    // Check original mol for duplicates
628
0
    FOR_ATOMS_OF_MOL(atom, *mol) {
629
0
      baseV = atom->GetVector();
630
0
      baseV = CartesianToFractional(baseV);
631
0
      baseV = WrapFractionalCoordinate(baseV);
632
0
      snprintf(hash, 22, "%03d,%.3f,%.3f,%.3f", atom->GetAtomicNum(), baseV.x(), baseV.y(), baseV.z());
633
0
      if (coordinateSet.insert(hash).second) { // True if new entry
634
0
        atoms.push_back(&(*atom));
635
0
      } else {
636
0
        atomsToDelete.push_back(&(*atom));
637
0
      }
638
0
    }
639
0
    for (deleteIter = atomsToDelete.begin(); deleteIter != atomsToDelete.end(); ++deleteIter) {
640
0
      mol->DeleteAtom(*deleteIter);
641
0
    }
642
643
    // Cross-check all transformations for duplicity
644
0
    for (atomIter = atoms.begin(); atomIter != atoms.end(); ++atomIter) {
645
0
      uniqueV = (*atomIter)->GetVector();
646
0
      uniqueV = CartesianToFractional(uniqueV);
647
0
      uniqueV = WrapFractionalCoordinate(uniqueV);
648
649
0
      transformedVectors = sg->Transform(uniqueV);
650
0
      for (transformIter = transformedVectors.begin();
651
0
        transformIter != transformedVectors.end(); ++transformIter) {
652
0
        updatedCoordinate = WrapFractionalCoordinate(*transformIter);
653
654
        // Check if the transformed coordinate is a duplicate of an atom
655
0
        snprintf(hash, 22, "%03d,%.3f,%.3f,%.3f", (*atomIter)->GetAtomicNum(), updatedCoordinate.x(),
656
0
                 updatedCoordinate.y(), updatedCoordinate.z());
657
0
        if (coordinateSet.insert(hash).second) {
658
0
          newAtom = mol->NewAtom();
659
0
          newAtom->Duplicate(*atomIter);
660
0
          newAtom->SetVector(FractionalToCartesian(updatedCoordinate));
661
0
        }
662
0
      } // end loop of transformed atoms
663
0
    } // end loop of atoms
664
0
    SetSpaceGroup(1); // We've now applied the symmetry, so we should act like a P1 unit cell
665
0
  }
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
0
  { \
671
0
    return const_cast<const OBUnitCell*>(this)->_name(); \
672
0
  }
Unexecuted instantiation: OpenBabel::OBUnitCell::GetA()
Unexecuted instantiation: OpenBabel::OBUnitCell::GetB()
Unexecuted instantiation: OpenBabel::OBUnitCell::GetC()
Unexecuted instantiation: OpenBabel::OBUnitCell::GetAlpha()
Unexecuted instantiation: OpenBabel::OBUnitCell::GetBeta()
Unexecuted instantiation: OpenBabel::OBUnitCell::GetGamma()
Unexecuted instantiation: OpenBabel::OBUnitCell::GetOffset()
Unexecuted instantiation: OpenBabel::OBUnitCell::GetLatticeType()
Unexecuted instantiation: OpenBabel::OBUnitCell::GetCellVectors()
Unexecuted instantiation: OpenBabel::OBUnitCell::GetCellMatrix()
Unexecuted instantiation: OpenBabel::OBUnitCell::GetOrthoMatrix()
Unexecuted instantiation: OpenBabel::OBUnitCell::GetOrientationMatrix()
Unexecuted instantiation: OpenBabel::OBUnitCell::GetFractionalMatrix()
Unexecuted instantiation: OpenBabel::OBUnitCell::GetCellVolume()
673
#define OBUNITCELL_CALL_CONST_OVERLOAD_ARG(_type, _name, _argsig) \
674
  _type OBUnitCell::_name( _argsig arg1 ) \
675
0
  { \
676
0
    return const_cast<const OBUnitCell*>(this)->_name(arg1); \
677
0
  }
Unexecuted instantiation: OpenBabel::OBUnitCell::GetLatticeType(int)
Unexecuted instantiation: OpenBabel::OBUnitCell::FractionalToCartesian(OpenBabel::vector3)
Unexecuted instantiation: OpenBabel::OBUnitCell::CartesianToFractional(OpenBabel::vector3)
Unexecuted instantiation: OpenBabel::OBUnitCell::WrapCartesianCoordinate(OpenBabel::vector3)
Unexecuted instantiation: OpenBabel::OBUnitCell::WrapFractionalCoordinate(OpenBabel::vector3)
Unexecuted instantiation: OpenBabel::OBUnitCell::MinimumImageCartesian(OpenBabel::vector3)
Unexecuted instantiation: OpenBabel::OBUnitCell::MinimumImageFractional(OpenBabel::vector3)
Unexecuted instantiation: OpenBabel::OBUnitCell::GetSpaceGroupNumber(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
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
0
  { \
705
0
    return const_cast<const OBUnitCell*>(this)->_name(arg1, arg2); \
706
0
  }
Unexecuted instantiation: OpenBabel::OBUnitCell::UnwrapCartesianNear(OpenBabel::vector3, OpenBabel::vector3)
Unexecuted instantiation: OpenBabel::OBUnitCell::UnwrapFractionalNear(OpenBabel::vector3, OpenBabel::vector3)
707
  OBUNITCELL_CALL_CONST_OVERLOAD_ARG2(vector3, UnwrapCartesianNear, vector3, vector3);
708
  OBUNITCELL_CALL_CONST_OVERLOAD_ARG2(vector3, UnwrapFractionalNear, vector3, vector3);
709
710
  double OBUnitCell::GetA() const
711
0
  {
712
0
    return _mOrtho.GetColumn(0).length();
713
0
  }
714
715
  double OBUnitCell::GetB() const
716
0
  {
717
0
    return _mOrtho.GetColumn(1).length();
718
0
  }
719
720
  double OBUnitCell::GetC() const
721
0
  {
722
0
    return _mOrtho.GetColumn(2).length();
723
0
  }
724
725
  double OBUnitCell::GetAlpha() const
726
0
  {
727
0
    return vectorAngle(_mOrtho.GetColumn(1), _mOrtho.GetColumn(2));
728
0
  }
729
730
  double OBUnitCell::GetBeta() const
731
0
  {
732
0
    return vectorAngle(_mOrtho.GetColumn(0), _mOrtho.GetColumn(2));
733
0
  }
734
735
  double OBUnitCell::GetGamma() const
736
0
  {
737
0
    return vectorAngle(_mOrtho.GetColumn(0), _mOrtho.GetColumn(1));
738
0
  }
739
740
  vector3 OBUnitCell::GetOffset() const
741
0
  {
742
0
    return _offset;
743
0
  }
744
745
  double OBUnitCell::GetCellVolume() const
746
0
  {
747
0
    return fabs(GetCellMatrix().determinant());
748
0
  }
749
750
  //
751
  // member functions for OBSymmetryData class
752
  //
753
  OBSymmetryData::OBSymmetryData():
754
0
    OBGenericData("Symmetry", OBGenericDataType::SymmetryData)
755
0
  { }
756
757
  OBSymmetryData::OBSymmetryData(const OBSymmetryData &src) :
758
0
    OBGenericData(src._attr, src._type, src._source),
759
0
    _spaceGroup(src._spaceGroup),
760
0
    _pointGroup(src._pointGroup)
761
0
  {  }
762
763
  OBSymmetryData & OBSymmetryData::operator=(const OBSymmetryData &src)
764
0
  {
765
0
    if(this == &src)
766
0
      return(*this);
767
768
0
    _pointGroup = src._pointGroup;
769
0
    _spaceGroup = src._spaceGroup;
770
0
    _source = src._source;
771
772
0
    return(*this);
773
0
  }
774
775
  OBConformerData::OBConformerData() :
776
0
    OBGenericData("Conformers", OBGenericDataType::ConformerData)
777
0
  {  }
778
779
  OBConformerData::OBConformerData(const OBConformerData &src) :
780
0
    OBGenericData("Conformers", OBGenericDataType::ConformerData),
781
0
    _vDimension(src._vDimension),
782
0
    _vEnergies(src._vEnergies), _vForces(src._vForces),
783
0
    _vVelocity(src._vVelocity), _vDisplace(src._vDisplace),
784
0
    _vData(src._vData)
785
0
  {  }
786
787
  OBConformerData & OBConformerData::operator=(const OBConformerData &src)
788
0
  {
789
0
    if(this == &src)
790
0
      return(*this);
791
792
0
    _source = src._source;
793
794
0
    _vDimension = src._vDimension;
795
0
    _vEnergies = src._vEnergies;
796
0
    _vForces = src._vForces;
797
0
    _vVelocity = src._vVelocity;
798
0
    _vDisplace = src._vDisplace;
799
0
    _vData = src._vData;
800
801
0
    return(*this);
802
0
  }
803
804
  //
805
  //member functions for OBRingData class
806
  //
807
808
  OBRingData::OBRingData() :
809
4.89k
    OBGenericData("RingData", OBGenericDataType::RingData)
810
4.89k
  {
811
4.89k
    _vr.clear();
812
4.89k
  }
813
814
  /*!
815
  **\brief OBRingData copy constructor
816
  **\param src reference to original OBRingData object (rhs)
817
  */
818
  OBRingData::OBRingData(const OBRingData &src)
819
0
    : OBGenericData(src), //chain to base class
820
0
      _vr(src._vr)        //chain to member classes
821
0
  {
822
    //no other memeber data
823
    //memory management
824
825
0
    vector<OBRing*>::iterator ring;
826
827
0
    for(ring = _vr.begin();ring != _vr.end();++ring)
828
0
      {
829
0
        OBRing *newring = new OBRing;
830
0
        (*newring) = (**ring);  //copy data to new object
831
0
        (*ring)    = newring; //repoint new pointer to new copy of data
832
0
      }
833
0
  }
834
835
  OBRingData::~OBRingData()
836
4.89k
  {
837
4.89k
    vector<OBRing*>::iterator ring;
838
154k
    for (ring = _vr.begin();ring != _vr.end();++ring)
839
149k
      {
840
149k
        delete *ring;
841
149k
      }
842
4.89k
  }
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
  */
849
  OBRingData& OBRingData::operator =(const OBRingData &src)
850
0
  {
851
    //on identity, return
852
0
    if(this == &src)
853
0
      return(*this);
854
855
    //chain to base class
856
0
    OBGenericData::operator =(src);
857
858
    //member data
859
860
    //memory management
861
0
    vector<OBRing*>::iterator ring;
862
0
    for(ring = _vr.begin();ring != _vr.end();++ring)
863
0
      {
864
0
        delete &*ring;  //deallocate old rings to prevent memory leak
865
0
      }
866
867
0
    _vr.clear();
868
0
    _vr = src._vr;  //copy vector properties
869
870
0
    for(ring = _vr.begin();ring != _vr.end();++ring)
871
0
      {
872
0
        if(*ring == 0)
873
0
          continue;
874
875
        //allocate and copy ring data
876
0
        OBRing *newring = new OBRing;
877
0
        (*newring) = (**ring);
878
0
        (*ring) = newring;  //redirect pointer
879
0
      }
880
0
    return(*this);
881
0
  }
882
883
  OBRing *OBRingData::BeginRing(std::vector<OBRing*>::iterator &i)
884
0
  {
885
0
    i = _vr.begin();
886
0
    return i == _vr.end() ? nullptr : (OBRing*)*i;
887
0
  }
888
889
  OBRing *OBRingData::NextRing(std::vector<OBRing*>::iterator &i)
890
0
  {
891
0
    ++i;
892
0
    return i == _vr.end() ? nullptr : (OBRing*)*i;
893
0
  }
894
895
  //
896
  //member functions for OBAngle class - stores all angles
897
  //
898
899
  /*!
900
  **\brief Angle default constructor
901
  */
902
  OBAngle::OBAngle():
903
0
    _vertex(nullptr), _termini(nullptr, nullptr), _radians(0.0)
904
0
  {  }
905
906
  /*!
907
  **\brief Angle constructor
908
  */
909
  OBAngle::OBAngle(OBAtom *vertex,OBAtom *a,OBAtom *b):
910
0
    _vertex(vertex), _termini(a, b)
911
0
  {
912
0
    SortByIndex();
913
0
  }
914
915
  /*!
916
  **\brief OBAngle copy constructor
917
  */
918
  OBAngle::OBAngle(const OBAngle &src):
919
0
    _vertex(src._vertex), _termini(src._termini), _radians(src._radians)
920
0
  {  }
921
922
  /*!
923
  **\brief OBAngle assignment operator
924
  */
925
  OBAngle& OBAngle::operator = (const OBAngle &src)
926
0
  {
927
0
    if (this == &src)
928
0
      return(*this);
929
930
0
    _vertex         = src._vertex;
931
0
    _termini.first  = src._termini.first;
932
0
    _termini.second = src._termini.second;
933
0
    _radians        = src._radians;
934
935
0
    return(*this);
936
0
  }
937
938
  /*!
939
  **\brief Return OBAngle to its original state
940
  */
941
  void OBAngle::Clear()
942
0
  {
943
0
    _vertex         = nullptr;
944
0
    _termini.first  = 0;
945
0
    _termini.second = 0;
946
0
    _radians        = 0.0;
947
0
    return;
948
0
  }
949
950
  /*!
951
  **\brief Sets the 3 atoms in the angle
952
  ** Parameters are pointers to each OBAtom
953
  */
954
  void OBAngle::SetAtoms(OBAtom *vertex,OBAtom *a,OBAtom *b)
955
0
  {
956
0
    _vertex         = vertex;
957
0
    _termini.first  = a;
958
0
    _termini.second = b;
959
0
    SortByIndex();
960
0
    return;
961
0
  }
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
  */
967
  void OBAngle::SetAtoms(triple<OBAtom*,OBAtom*,OBAtom*> &atoms)
968
0
  {
969
0
    _vertex         = atoms.first;
970
0
    _termini.first  = atoms.second;
971
0
    _termini.second = atoms.third;
972
0
    SortByIndex();
973
0
    return;
974
0
  }
975
976
  /*!
977
  **\brief Retrieves the 3 atom pointer for the angle (vertex first)
978
  **\return triple of OBAtom pointers
979
  */
980
  triple<OBAtom*,OBAtom*,OBAtom*> OBAngle::GetAtoms()
981
0
  {
982
0
    triple<OBAtom*,OBAtom*,OBAtom*> atoms;
983
0
    atoms.first  = _vertex;
984
0
    atoms.second = _termini.first;
985
0
    atoms.third  = _termini.second;
986
0
    return(atoms);
987
0
  }
988
989
  /*!
990
  **\brief sorts atoms in angle by order of indices
991
  */
992
  void OBAngle::SortByIndex()
993
0
  {
994
0
    OBAtom *tmp;
995
996
0
    if(_termini.first->GetIdx() > _termini.second->GetIdx())
997
0
      {
998
0
        tmp             = _termini.first;
999
0
        _termini.first  = _termini.second;
1000
0
        _termini.second = tmp;
1001
0
      }
1002
0
  }
1003
1004
  /*!
1005
  **\brief OBAngle equality operator, is same angle, NOT same value
1006
  **\return boolean equality
1007
  */
1008
  bool OBAngle::operator ==(const OBAngle &other)
1009
0
  {
1010
0
    return ((_vertex         == other._vertex)        &&
1011
0
            (_termini.first  == other._termini.first) &&
1012
0
            (_termini.second == other._termini.second));
1013
0
  }
1014
1015
  //
1016
  //member functions for OBAngleData class - stores OBAngle set
1017
  //
1018
1019
  /*!
1020
  **\brief OBAngleData constructor
1021
  */
1022
  OBAngleData::OBAngleData()
1023
0
    : OBGenericData("AngleData", OBGenericDataType::AngleData)
1024
0
  {  }
1025
1026
  /*!
1027
  **\brief OBAngleData copy constructor
1028
  */
1029
  OBAngleData::OBAngleData(const OBAngleData &src)
1030
0
    : OBGenericData(src), _angles(src._angles)
1031
0
  {  }
1032
1033
  /*!
1034
  **\brief OBAngleData assignment operator
1035
  */
1036
  OBAngleData& OBAngleData::operator =(const OBAngleData &src)
1037
0
  {
1038
0
    if (this == &src)
1039
0
      return(*this);
1040
1041
0
    _source = src._source;
1042
0
    _angles = src._angles;
1043
1044
0
    return(*this);
1045
0
  }
1046
1047
  /*!
1048
  **\brief sets OBAngleData to its original state
1049
  */
1050
  void OBAngleData::Clear()
1051
0
  {
1052
0
    _angles.clear();
1053
0
    return;
1054
0
  }
1055
1056
  /*!
1057
  **\brief Adds a new angle to OBAngleData
1058
  */
1059
  void OBAngleData::SetData(OBAngle &angle)
1060
0
  {
1061
0
    _angles.push_back(angle);
1062
0
    return;
1063
0
  }
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
  */
1070
  bool OBAngleData::FillAngleArray(std::vector<std::vector<unsigned int> > &angles)
1071
0
  {
1072
0
    if(_angles.empty())
1073
0
      return(false);
1074
1075
0
    vector<OBAngle>::iterator angle;
1076
1077
0
    angles.clear();
1078
0
    angles.resize(_angles.size());
1079
1080
0
    unsigned int ct = 0;
1081
1082
0
    for( angle=_angles.begin(); angle!=_angles.end(); ++angle,ct++)
1083
0
      {
1084
0
        angles[ct].resize(3);
1085
0
        angles[ct][0] = angle->_vertex->GetIdx() - 1;
1086
0
        angles[ct][1] = angle->_termini.first->GetIdx() - 1;
1087
0
        angles[ct][2] = angle->_termini.second->GetIdx() - 1;
1088
0
      }
1089
1090
0
    return(true);
1091
0
  }
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
  */
1099
  unsigned int OBAngleData::FillAngleArray(int **angles, unsigned int &size)
1100
0
  {
1101
0
    if(_angles.size() > size)
1102
0
      {
1103
0
        delete [] *angles;
1104
0
        *angles = new int[_angles.size()*3];
1105
0
        size    = (unsigned int)_angles.size();
1106
0
      }
1107
1108
0
    vector<OBAngle>::iterator angle;
1109
0
    int angleIdx = 0;
1110
0
    for( angle=_angles.begin(); angle!=_angles.end(); ++angle)
1111
0
      {
1112
0
        *angles[angleIdx++] = angle->_vertex->GetIdx();
1113
0
        *angles[angleIdx++] = angle->_termini.first->GetIdx();
1114
0
        *angles[angleIdx++] = angle->_termini.second->GetIdx();
1115
0
      }
1116
0
    return (unsigned int)_angles.size();
1117
0
  }
1118
1119
  //
1120
  //member functions for OBAngleData class - stores OBAngle set
1121
  //
1122
1123
  /*!
1124
  **\brief OBTorsion constructor
1125
  */
1126
  OBTorsion::OBTorsion(OBAtom *a,OBAtom *b, OBAtom *c,OBAtom *d)
1127
0
  {
1128
0
    triple<OBAtom*,OBAtom*,double> ad(a,d,0.0);
1129
0
    _ads.push_back(ad);
1130
1131
0
    _bc.first  = b;
1132
0
    _bc.second = c;
1133
0
  }
1134
1135
  /*!
1136
  **\brief OBTorsion copy constructor
1137
  */
1138
  OBTorsion::OBTorsion(const OBTorsion &src)
1139
0
    : _bc(src._bc), _ads(src._ads)
1140
0
  {}
1141
1142
  /*!
1143
  **\brief Returns all the 4 atom sets in OBTorsion
1144
  */
1145
  vector<quad<OBAtom*,OBAtom*,OBAtom*,OBAtom*> > OBTorsion::GetTorsions()
1146
0
  {
1147
0
    quad<OBAtom*,OBAtom*,OBAtom*,OBAtom*> abcd;
1148
1149
0
    abcd.second = _bc.first;
1150
0
    abcd.third  = _bc.second;
1151
1152
0
    vector<quad<OBAtom*,OBAtom*,OBAtom*,OBAtom*> > torsions;
1153
0
    vector<triple<OBAtom*,OBAtom*,double> >::iterator ad;
1154
1155
0
    for(ad = _ads.begin();ad != _ads.end();++ad)
1156
0
      {
1157
0
        abcd.first = ad->first;
1158
0
        abcd.fourth = ad->second;
1159
0
        torsions.push_back(abcd);
1160
0
      }
1161
1162
0
    return(torsions);
1163
0
  }
1164
1165
  /*!
1166
  **\brief OBTorsion assignment operator
1167
  */
1168
  OBTorsion& OBTorsion::operator =(const OBTorsion &src)
1169
0
  {
1170
0
    if (this == &src)
1171
0
      return(*this);
1172
1173
0
    _bc  = src._bc;
1174
0
    _ads = src._ads;
1175
1176
0
    return(*this);
1177
0
  }
1178
1179
  /*!
1180
  **\brief Returns the OBTorsion to its original state
1181
  */
1182
  void OBTorsion::Clear()
1183
0
  {
1184
0
    _bc.first  = 0;
1185
0
    _bc.second = 0;
1186
0
    _ads.erase(_ads.begin(),_ads.end());
1187
0
  }
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
  */
1195
  bool OBTorsion::SetAngle(double radians,unsigned int index)
1196
0
  {
1197
0
    if(index >= _ads.size())
1198
0
      return(false);
1199
1200
0
    _ads[index].third = radians;
1201
1202
0
    return(true);
1203
0
  }
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
  */
1211
  bool OBTorsion::GetAngle(double &radians, unsigned int index)
1212
0
  {
1213
0
    if(index >= _ads.size())
1214
0
      return false;
1215
0
    radians = _ads[index].third;
1216
0
    return true;
1217
0
  }
1218
1219
  unsigned int OBTorsion::GetBondIdx()
1220
0
  {
1221
0
    return(_bc.first->GetBond(_bc.second)->GetIdx());
1222
0
  }
1223
1224
  /*!
1225
  **\brief determines if torsion has only protons on either the a or d end
1226
  **\return boolean
1227
  */
1228
  bool OBTorsion::IsProtonRotor()
1229
0
  {
1230
0
    bool Aprotor = true;
1231
0
    bool Dprotor = true;
1232
0
    vector<triple<OBAtom*,OBAtom*,double> >::iterator ad;
1233
0
    for(ad = _ads.begin();ad != _ads.end() && (Aprotor || Dprotor);++ad)
1234
0
      {
1235
0
        if (ad->first->GetAtomicNum() != OBElements::Hydrogen)
1236
0
          Aprotor = false;
1237
0
        if (ad->second->GetAtomicNum() != OBElements::Hydrogen)
1238
0
          Dprotor = false;
1239
0
      }
1240
0
    return (Aprotor || Dprotor);
1241
0
  }
1242
1243
  /*!
1244
  **\brief adds a new torsion to the OBTorsion object
1245
  */
1246
  bool OBTorsion::AddTorsion(OBAtom *a,OBAtom *b, OBAtom *c,OBAtom *d)
1247
0
  {
1248
0
    if(!Empty() && (b != _bc.first || c != _bc.second))
1249
0
      return(false);
1250
1251
0
    if(Empty())
1252
0
      {
1253
0
        _bc.first  = b;
1254
0
        _bc.second = c;
1255
0
      }
1256
1257
0
    triple<OBAtom*,OBAtom*,double> ad(a,d,0.0);
1258
0
    _ads.push_back(ad);
1259
1260
0
    return(true);
1261
0
  }
1262
1263
  /*!
1264
  **\brief adds a new torsion to the OBTorsion object
1265
  */
1266
  bool OBTorsion::AddTorsion(quad<OBAtom*,OBAtom*,OBAtom*,OBAtom*> &atoms)
1267
0
  {
1268
0
    if(!Empty() && (atoms.second != _bc.first || atoms.third != _bc.second))
1269
0
      return(false);
1270
1271
0
    if(Empty())
1272
0
      {
1273
0
        _bc.first  = atoms.second;
1274
0
        _bc.second = atoms.third;
1275
0
      }
1276
1277
0
    triple<OBAtom*,OBAtom*,double> ad(atoms.first,atoms.fourth,0.0);
1278
0
    _ads.push_back(ad);
1279
1280
0
    return(true);
1281
0
  }
1282
1283
  //\!brief OBTorsionData ctor
1284
  OBTorsionData::OBTorsionData()
1285
0
    : OBGenericData("TorsionData", OBGenericDataType::TorsionData)
1286
0
  {  }
1287
1288
  //
1289
  //member functions for OBTorsionData class - stores OBTorsion set
1290
  //
1291
  OBTorsionData::OBTorsionData(const OBTorsionData &src)
1292
0
    : OBGenericData(src), _torsions(src._torsions)
1293
0
  {  }
1294
1295
  OBTorsionData& OBTorsionData::operator =(const OBTorsionData &src)
1296
0
  {
1297
0
    if (this == &src)
1298
0
      return(*this);
1299
1300
0
    OBGenericData::operator =(src);
1301
1302
0
    _source = src._source;
1303
0
    _torsions = src._torsions;
1304
1305
0
    return(*this);
1306
0
  }
1307
1308
  void OBTorsionData::Clear()
1309
0
  {
1310
0
    _torsions.clear();
1311
0
  }
1312
1313
  void OBTorsionData::SetData(OBTorsion &torsion)
1314
0
  {
1315
0
    _torsions.push_back(torsion);
1316
0
  }
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
  */
1323
  bool OBTorsionData::FillTorsionArray(std::vector<std::vector<unsigned int> > &torsions)
1324
0
  {
1325
0
    if(_torsions.empty())
1326
0
      return(false);
1327
1328
0
    vector<quad<OBAtom*,OBAtom*,OBAtom*,OBAtom*> > tmpquads,quads;
1329
0
    vector<quad<OBAtom*,OBAtom*,OBAtom*,OBAtom*> >::iterator thisQuad;
1330
0
    vector<OBTorsion>::iterator torsion;
1331
1332
    //generate set of all 4 atom abcd's from torsion structure
1333
0
    for (torsion = _torsions.begin();torsion != _torsions.end();++torsion)
1334
0
      {
1335
0
        tmpquads = torsion->GetTorsions();
1336
0
        for(thisQuad = tmpquads.begin();thisQuad != tmpquads.end();++thisQuad)
1337
0
          quads.push_back(*thisQuad);
1338
0
      }
1339
1340
    //fill array of torsion atoms
1341
1342
0
    torsions.clear();
1343
0
    torsions.resize(quads.size());
1344
1345
0
    unsigned int ct = 0;
1346
1347
0
    for (thisQuad = quads.begin();thisQuad != quads.end();++thisQuad,++ct)
1348
0
      {
1349
0
        torsions[ct].resize(4);
1350
0
        torsions[ct][0] = thisQuad->first->GetIdx()-1;
1351
0
        torsions[ct][1] = thisQuad->second->GetIdx()-1;
1352
0
        torsions[ct][2] = thisQuad->third->GetIdx()-1;
1353
0
        torsions[ct][3] = thisQuad->fourth->GetIdx()-1;
1354
0
      }
1355
1356
0
    return(true);
1357
0
  }
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
*/
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
0
{
1376
0
  this->_fermi = fermi;
1377
0
  this->_vEnergies = vEnergies;
1378
0
  this->_vIntegration = vIntegration;
1379
0
  this->_vDensities = vDensities;
1380
0
}
1381
1382
  // member functions for OBOrbitalData
1383
1384
  void OBOrbitalData::LoadClosedShellOrbitals(std::vector<double> energies, std::vector<std::string> symmetries, unsigned int alphaHOMO)
1385
0
  {
1386
0
    if (energies.size() < symmetries.size())
1387
0
      return; // something is very weird -- it's OK to pass no symmetries (we'll assume "A")
1388
0
    if (energies.size() == 0)
1389
0
      return;
1390
0
    if (alphaHOMO > energies.size())
1391
0
      return;
1392
1393
0
    _alphaHOMO = alphaHOMO;
1394
0
    _alphaOrbitals.clear();
1395
0
    _betaHOMO = 0;
1396
0
    _betaOrbitals.clear();
1397
0
    _openShell = false;
1398
1399
0
    if (symmetries.size() < energies.size()) // pad with "A" symmetry
1400
0
      for (unsigned int i = symmetries.size(); i < energies.size(); ++i)
1401
0
        symmetries.push_back("A");
1402
1403
0
    OBOrbital currentOrbital;
1404
0
    for (unsigned int i = 0; i < energies.size(); ++i)
1405
0
      {
1406
0
        if (i < alphaHOMO)
1407
0
          currentOrbital.SetData(energies[i], 2.0, symmetries[i]);
1408
0
        else
1409
0
          currentOrbital.SetData(energies[i], 0.0, symmetries[i]);
1410
1411
0
        _alphaOrbitals.push_back(currentOrbital);
1412
0
      }
1413
0
  }
1414
1415
  void OBOrbitalData::LoadAlphaOrbitals(std::vector<double> energies, std::vector<std::string> symmetries, unsigned int alphaHOMO)
1416
0
  {
1417
0
    if (energies.size() < symmetries.size())
1418
0
      return; // something is very weird -- it's OK to pass no symmetries (we'll assume "A")
1419
0
    if (energies.size() == 0)
1420
0
      return;
1421
0
    if (alphaHOMO > energies.size())
1422
0
      return;
1423
1424
0
    _alphaHOMO = alphaHOMO;
1425
0
    _alphaOrbitals.clear();
1426
0
    _openShell = true;
1427
1428
0
    if (symmetries.size() < energies.size()) // pad with "A" symmetry
1429
0
      for (unsigned int i = symmetries.size(); i < energies.size(); ++i)
1430
0
        symmetries.push_back("A");
1431
1432
0
    OBOrbital currentOrbital;
1433
0
    for (unsigned int i = 0; i < energies.size(); ++i)
1434
0
      {
1435
0
        if (i < alphaHOMO)
1436
0
          currentOrbital.SetData(energies[i], 2.0, symmetries[i]);
1437
0
        else
1438
0
          currentOrbital.SetData(energies[i], 0.0, symmetries[i]);
1439
1440
0
        _alphaOrbitals.push_back(currentOrbital);
1441
0
      }
1442
0
  }
1443
1444
  void OBOrbitalData::LoadBetaOrbitals(std::vector<double> energies, std::vector<std::string> symmetries, unsigned int betaHOMO)
1445
0
  {
1446
0
    if (energies.size() < symmetries.size())
1447
0
      return; // something is very weird -- it's OK to pass no symmetries (we'll assume "A")
1448
0
    if (energies.size() == 0)
1449
0
      return;
1450
0
    if (betaHOMO > energies.size())
1451
0
      return;
1452
1453
0
    _betaHOMO = betaHOMO;
1454
0
    _betaOrbitals.clear();
1455
0
    _openShell = true;
1456
1457
0
    if (symmetries.size() < energies.size()) // pad with "A" symmetry
1458
0
      for (unsigned int i = symmetries.size(); i < energies.size(); ++i)
1459
0
        symmetries.push_back("A");
1460
1461
0
    OBOrbital currentOrbital;
1462
0
    for (unsigned int i = 0; i < energies.size(); ++i)
1463
0
      {
1464
0
        if (i < betaHOMO)
1465
0
          currentOrbital.SetData(energies[i], 2.0, symmetries[i]);
1466
0
        else
1467
0
          currentOrbital.SetData(energies[i], 0.0, symmetries[i]);
1468
1469
0
        _betaOrbitals.push_back(currentOrbital);
1470
0
      }
1471
0
  }
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
*/
1482
void OBElectronicTransitionData::SetData(const std::vector<double> & vWavelengths,
1483
                                  const std::vector<double> & vForces)
1484
0
{
1485
0
  this->_vWavelengths = vWavelengths;
1486
0
  this->_vForces = vForces;
1487
0
}
1488
1489
/*!
1490
**\brief Assign the electronic dipole strengths
1491
**\param vEDipole Electronic dipole moment strength
1492
*/
1493
void OBElectronicTransitionData::SetEDipole(const std::vector<double> & vEDipole)
1494
0
{
1495
0
  this->_vEDipole = vEDipole;
1496
0
}
1497
1498
/*!
1499
**\brief Assign the rotatory strengths (velocity)
1500
**\param vRotatoryStrengthsVelocity Vector containing the rotatory strengths
1501
*/
1502
void OBElectronicTransitionData::SetRotatoryStrengthsVelocity(const std::vector<double> & vRotatoryStrengthsVelocity)
1503
0
{
1504
0
  this->_vRotatoryStrengthsVelocity = vRotatoryStrengthsVelocity;
1505
0
}
1506
1507
/*!
1508
**\brief Assign the rotatory strengths (length)
1509
**\param vRotatoryStrengthsLength Vector containing the rotatory strengths
1510
*/
1511
void OBElectronicTransitionData::SetRotatoryStrengthsLength(const std::vector<double> & vRotatoryStrengthsLength)
1512
0
{
1513
0
  this->_vRotatoryStrengthsLength = vRotatoryStrengthsLength;
1514
0
}
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
*/
1526
void OBVibrationData::SetData(const std::vector< std::vector< vector3 > > & vLx,
1527
                              const std::vector<double> & vFrequencies,
1528
                              const std::vector<double> & vIntensities)
1529
0
{
1530
0
  this->_vLx = vLx;
1531
0
  this->_vFrequencies = vFrequencies;
1532
0
  this->_vIntensities = vIntensities;
1533
0
}
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
*/
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
0
{
1547
0
  this->_vLx = vLx;
1548
0
  this->_vFrequencies = vFrequencies;
1549
0
  this->_vIntensities = vIntensities;
1550
0
  this->_vRamanActivities = vRamanActivities;
1551
0
}
1552
1553
1554
/*!
1555
**\brief Get the number of frequencies
1556
*/
1557
unsigned int OBVibrationData::GetNumberOfFrequencies() const
1558
0
{
1559
0
  return this->_vFrequencies.size();
1560
0
}
1561
1562
void OBFreeGrid::Clear()
1563
0
{
1564
0
  _points.clear();
1565
0
}
1566
1567
} //end namespace OpenBabel
1568
1569
//! \file generic.cpp
1570
//! \brief Handle OBGenericData classes. Custom data for atoms, bonds, etc.