Coverage Report

Created: 2025-08-03 06:29

/src/openbabel/src/obutil.cpp
Line
Count
Source (jump to first uncovered line)
1
/**********************************************************************
2
obutil.cpp - Various utility methods.
3
4
Copyright (C) 1998-2001 by OpenEye Scientific Software, Inc.
5
Some portions Copyright (C) 2001-2006 by Geoffrey R. Hutchison
6
7
This file is part of the Open Babel project.
8
For more information, see <http://openbabel.org/>
9
10
This program is free software; you can redistribute it and/or modify
11
it under the terms of the GNU General Public License as published by
12
the Free Software Foundation version 2 of the License.
13
14
This program is distributed in the hope that it will be useful,
15
but WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
GNU General Public License for more details.
18
***********************************************************************/
19
20
#include <openbabel/babelconfig.h>
21
#include <openbabel/math/matrix3x3.h>
22
#include <openbabel/mol.h>
23
#include <openbabel/atom.h>
24
#include <openbabel/obiter.h>
25
#include <openbabel/obutil.h>
26
#include <openbabel/internalcoord.h>
27
28
#include <cstring>
29
30
#ifdef HAVE_CONIO_H
31
#include <conio.h>
32
#endif
33
34
using namespace std;
35
namespace OpenBabel
36
{
37
38
  /*! \class OBStopwatch obutil.h <openbabel/obutil.h>
39
    \brief Stopwatch class used for timing length of execution
40
41
    The OBStopwatch class makes timing the execution of blocks of
42
    code to microsecond accuracy very simple. The class effectively
43
    has two functions, Start() and Elapsed(). The usage of the
44
    OBStopwatch class is demonstrated by the following code:
45
    \code
46
    OBStopwatch sw;
47
    sw.Start();
48
    //insert code here
49
    cout << "Elapsed time = " << sw.Elapsed() << endl;
50
    \endcode
51
  */
52
53
  //! Deprecated: use the OBMessageHandler class instead
54
  //! \deprecated Throw an error through the OpenBabel::OBMessageHandler class
55
  OB_DEPRECATED_MSG("use the OBMessageHandler class instead")
56
  void ThrowError(char *str)
57
0
  {
58
0
    obErrorLog.ThrowError("", str, obInfo);
59
0
  }
60
61
  //! Deprecated: use the OBMessageHandler class instead
62
  //! \deprecated Throw an error through the OpenBabel::OBMessageHandler class
63
  OB_DEPRECATED_MSG("use the OBMessageHandler class instead")
64
  void ThrowError(std::string &str)
65
0
  {
66
0
    obErrorLog.ThrowError("", str, obInfo);
67
0
  }
68
69
  // returns True if a < b, False otherwise.
70
  bool OBCompareInt(const int &a,const int &b)
71
0
  {
72
0
    return(a<b);
73
0
  }
74
75
  // Comparison function (for sorting unsigned ints) returns a < b
76
  bool OBCompareUnsigned(const unsigned int &a,const unsigned int &b)
77
0
  {
78
0
    return(a<b);
79
0
  }
80
81
  //! Comparison for doubles: returns fabs(a - b) < epsilon
82
  bool IsNear(const double &a, const double &b, const double epsilon)
83
0
  {
84
0
    return (fabs(a - b) < epsilon);
85
0
  }
86
87
  //! Comparison for doubles: returns fabs(a) < epsilon
88
  bool IsNearZero(const double &a, const double epsilon)
89
0
  {
90
0
    return (fabs(a) < epsilon);
91
0
  }
92
93
  //! Comparison for nan (not a number)
94
  bool IsNan(const double &a)
95
0
  {
96
0
    return ((a) != (a));
97
0
  }
98
99
  //! Tests whether its argument can be squared without triggering an overflow or
100
  //! underflow.
101
  bool CanBeSquared(const double &a)
102
0
  {
103
0
    if( a == 0 ) return true;
104
0
    const double max_squarable_double = 1e150;
105
0
    const double min_squarable_double = 1e-150;
106
0
    double abs_a = fabs(a);
107
0
    return(abs_a < max_squarable_double && abs_a > min_squarable_double);
108
0
  }
109
110
  //! Utility function: replace the last extension in string &src with new extension char *ext.
111
  string NewExtension(string &src,char *ext)
112
0
  {
113
0
    string::size_type pos = (unsigned int)src.find_last_of(".");
114
0
    string dst;
115
116
0
    if (pos != string::npos)
117
0
      dst = src.substr(0,pos+1);
118
0
    else
119
0
      {
120
0
        dst = src;
121
0
        dst += ".";
122
0
      }
123
124
0
    dst += ext;
125
0
    return(dst);
126
0
  }
127
128
  //! \return the geometric centroid to an array of coordinates in double* format
129
  //!  and center the coordinates to the origin. Operates on the first "size"
130
  //!  coordinates in the array.
131
  vector3 center_coords(double *c, unsigned int size)
132
0
  {
133
0
    if (size == 0)
134
0
      {
135
0
        return(VZero);
136
0
      }
137
0
    unsigned int i;
138
0
    double x=0.0, y=0.0, z=0.0;
139
0
    for (i = 0;i < size;++i)
140
0
      {
141
0
        x += c[i*3];
142
0
        y += c[i*3+1];
143
0
        z += c[i*3+2];
144
0
      }
145
0
    x /= (double) size;
146
0
    y /= (double) size;
147
0
    z /= (double) size;
148
0
    for (i = 0;i < size;++i)
149
0
      {
150
0
        c[i*3]   -= x;
151
0
        c[i*3+1] -= y;
152
0
        c[i*3+2] -= z;
153
0
      }
154
0
    vector3 v(x,y,z);
155
0
    return(v);
156
0
  }
157
158
  //! Rotates the coordinate set *c by the transformation matrix m[3][3]
159
  //!  Operates on the first "size" coordinates in the array.
160
  void rotate_coords(double *c,double m[3][3],unsigned int size)
161
0
  {
162
0
    double x,y,z;
163
0
    for (unsigned int i = 0;i < size;++i)
164
0
      {
165
0
        x = c[i*3]*m[0][0] + c[i*3+1]*m[0][1] + c[i*3+2]*m[0][2];
166
0
        y = c[i*3]*m[1][0] + c[i*3+1]*m[1][1] + c[i*3+2]*m[1][2];
167
0
        z = c[i*3]*m[2][0] + c[i*3+1]*m[2][1] + c[i*3+2]*m[2][2];
168
0
        c[i*3] = x;
169
0
        c[i*3+1] = y;
170
0
        c[i*3+2] = z;
171
0
      }
172
0
  }
173
174
  //! Calculate the RMS deviation between the first N coordinates of *r and *f
175
  double calc_rms(double *r,double *f, unsigned int N)
176
0
  {
177
0
    if (N == 0)
178
0
      return 0.0; // no RMS deviation between two empty sets
179
180
0
    double d2=0.0;
181
0
    for (unsigned int i = 0;i < N;++i)
182
0
      {
183
0
        d2 += SQUARE(r[i*3] - f[i*3]) +
184
0
          SQUARE(r[i*3+1] - f[i*3+1]) +
185
0
          SQUARE(r[i*3+2] - f[i*3+2]);
186
0
      }
187
188
0
    d2 /= (double) N;
189
0
    return(sqrt(d2));
190
0
  }
191
192
  //! Rotate the coordinates of 'atoms'
193
  //! such that tor == ang - atoms in 'tor' should be ordered such
194
  //! that the 3rd atom is the pivot around which atoms rotate
195
  void SetRotorToAngle(double *c,vector<int> &tor,double ang,vector<int> &atoms)
196
0
  {
197
0
    double v1x,v1y,v1z,v2x,v2y,v2z,v3x,v3y,v3z;
198
0
    double c1x,c1y,c1z,c2x,c2y,c2z,c3x,c3y,c3z;
199
0
    double c1mag,c2mag,radang,costheta,m[9];
200
0
    double x,y,z,mag,rotang,sn,cs,t,tx,ty,tz;
201
202
    //
203
    //calculate the torsion angle
204
    //
205
0
    v1x = c[tor[0]]   - c[tor[1]];
206
0
    v2x = c[tor[1]]   - c[tor[2]];
207
0
    v1y = c[tor[0]+1] - c[tor[1]+1];
208
0
    v2y = c[tor[1]+1] - c[tor[2]+1];
209
0
    v1z = c[tor[0]+2] - c[tor[1]+2];
210
0
    v2z = c[tor[1]+2] - c[tor[2]+2];
211
0
    v3x = c[tor[2]]   - c[tor[3]];
212
0
    v3y = c[tor[2]+1] - c[tor[3]+1];
213
0
    v3z = c[tor[2]+2] - c[tor[3]+2];
214
215
0
    c1x = v1y*v2z - v1z*v2y;
216
0
    c2x = v2y*v3z - v2z*v3y;
217
0
    c1y = -v1x*v2z + v1z*v2x;
218
0
    c2y = -v2x*v3z + v2z*v3x;
219
0
    c1z = v1x*v2y - v1y*v2x;
220
0
    c2z = v2x*v3y - v2y*v3x;
221
0
    c3x = c1y*c2z - c1z*c2y;
222
0
    c3y = -c1x*c2z + c1z*c2x;
223
0
    c3z = c1x*c2y - c1y*c2x;
224
225
0
    c1mag = SQUARE(c1x)+SQUARE(c1y)+SQUARE(c1z);
226
0
    c2mag = SQUARE(c2x)+SQUARE(c2y)+SQUARE(c2z);
227
0
    if (c1mag*c2mag < 0.01)
228
0
      costheta = 1.0; //avoid div by zero error
229
0
    else
230
0
      costheta = (c1x*c2x + c1y*c2y + c1z*c2z)/(sqrt(c1mag*c2mag));
231
232
0
    if (costheta < -0.999999)
233
0
      costheta = -0.999999;
234
0
    if (costheta >  0.999999)
235
0
      costheta =  0.999999;
236
237
0
    if ((v2x*c3x + v2y*c3y + v2z*c3z) > 0.0)
238
0
      radang = -acos(costheta);
239
0
    else
240
0
      radang = acos(costheta);
241
242
    //
243
    // now we have the torsion angle (radang) - set up the rot matrix
244
    //
245
246
    //find the difference between current and requested
247
0
    rotang = ang - radang;
248
249
0
    sn = sin(rotang);
250
0
    cs = cos(rotang);
251
0
    t = 1 - cs;
252
    //normalize the rotation vector
253
0
    mag = sqrt(SQUARE(v2x)+SQUARE(v2y)+SQUARE(v2z));
254
0
    x = v2x/mag;
255
0
    y = v2y/mag;
256
0
    z = v2z/mag;
257
258
    //set up the rotation matrix
259
0
    m[0]= t*x*x + cs;
260
0
    m[1] = t*x*y + sn*z;
261
0
    m[2] = t*x*z - sn*y;
262
0
    m[3] = t*x*y - sn*z;
263
0
    m[4] = t*y*y + cs;
264
0
    m[5] = t*y*z + sn*x;
265
0
    m[6] = t*x*z + sn*y;
266
0
    m[7] = t*y*z - sn*x;
267
0
    m[8] = t*z*z + cs;
268
269
    //
270
    //now the matrix is set - time to rotate the atoms
271
    //
272
0
    tx = c[tor[1]];
273
0
    ty = c[tor[1]+1];
274
0
    tz = c[tor[1]+2];
275
0
    vector<int>::iterator i;
276
0
    int j;
277
0
    for (i = atoms.begin();i != atoms.end();++i)
278
0
      {
279
0
        j = *i;
280
0
        c[j] -= tx;
281
0
        c[j+1] -= ty;
282
0
        c[j+2]-= tz;
283
0
        x = c[j]*m[0] + c[j+1]*m[1] + c[j+2]*m[2];
284
0
        y = c[j]*m[3] + c[j+1]*m[4] + c[j+2]*m[5];
285
0
        z = c[j]*m[6] + c[j+1]*m[7] + c[j+2]*m[8];
286
0
        c[j] = x;
287
0
        c[j+1] = y;
288
0
        c[j+2] = z;
289
0
        c[j] += tx;
290
0
        c[j+1] += ty;
291
0
        c[j+2] += tz;
292
0
      }
293
0
  }
294
295
  //! Safely open the supplied filename and return an ifstream, throwing an error
296
  //! to the default OBMessageHandler error log if it fails.
297
  bool SafeOpen(std::ifstream &fs, const char *filename)
298
0
  {
299
#ifdef WIN32
300
    string s(filename);
301
    if (s.find(".bin") != string::npos)
302
      fs.open(filename,ios::binary);
303
    else
304
#endif
305
306
0
      fs.open(filename);
307
308
0
    if (!fs)
309
0
      {
310
0
        string error = "Unable to open file \'";
311
0
        error += filename;
312
0
        error += "\' in read mode";
313
0
        obErrorLog.ThrowError(__FUNCTION__, error, obError);
314
0
        return(false);
315
0
      }
316
317
0
    return(true);
318
0
  }
319
320
321
  //! Safely open the supplied filename and return an ofstream, throwing an error
322
  //! to the default OBMessageHandler error log if it fails.
323
  bool SafeOpen(std::ofstream &fs, const char *filename)
324
0
  {
325
#ifdef WIN32
326
    string s(filename);
327
    if (s.find(".bin") != string::npos)
328
      fs.open(filename,ios::binary);
329
    else
330
#endif
331
332
0
      fs.open(filename);
333
334
0
    if (!fs)
335
0
      {
336
0
        string error = "Unable to open file \'";
337
0
        error += filename;
338
0
        error += "\' in write mode";
339
0
        obErrorLog.ThrowError(__FUNCTION__, error, obError);
340
0
        return(false);
341
0
      }
342
343
0
    return(true);
344
0
  }
345
346
  //! Safely open the supplied filename and return an ifstream, throwing an error
347
  //! to the default OBMessageHandler error log if it fails.
348
  bool SafeOpen(std::ifstream &fs, const string &filename)
349
0
  {
350
0
    return(SafeOpen(fs, filename.c_str()));
351
0
  }
352
353
  //! Safely open the supplied filename and return an ofstream, throwing an error
354
  //! to the default OBMessageHandler error log if it fails.
355
  bool SafeOpen(std::ofstream &fs, const string &filename)
356
0
  {
357
0
    return(SafeOpen(fs, filename.c_str()));
358
0
  }
359
360
  //! Shift the supplied string to uppercase
361
  void ToUpper(std::string &s)
362
0
  {
363
0
    if (s.empty())
364
0
      return;
365
0
    unsigned int i;
366
0
    for (i = 0;i < s.size();++i)
367
0
      if (isalpha(s[i]) && !isdigit(s[i]))
368
0
        s[i] = toupper(s[i]);
369
0
  }
370
371
  //! Shift the supplied char* to uppercase
372
  void ToUpper(char *cptr)
373
0
  {
374
0
    char *c;
375
0
    for (c = cptr;*c != '\0';++c)
376
0
      if (isalpha(*c) && !isdigit(*c))
377
0
        *c = toupper(*c);
378
0
  }
379
380
  //! Shift the supplied string to lowercase
381
  void ToLower(std::string &s)
382
0
  {
383
0
    if (s.empty())
384
0
      return;
385
0
    unsigned int i;
386
0
    for (i = 0;i < s.size();++i)
387
0
      if (isalpha(s[i]) && !isdigit(s[i]))
388
0
        s[i] = tolower(s[i]);
389
0
  }
390
391
  //! Shift the supplied char* to lowercase
392
  void ToLower(char *cptr)
393
0
  {
394
0
    char *c;
395
0
    for (c = cptr;*c != '\0';++c)
396
0
      if (isalpha(*c) && !isdigit(*c))
397
0
        *c = tolower(*c);
398
0
  }
399
400
  //! Shift the supplied string: lowercase to upper, and upper to lower
401
  //! \param s - The string to switch case
402
  //! \param start - The position to start inverting case
403
  void InvertCase(std::string &s, unsigned int start)
404
0
  {
405
0
    if (start >= s.size())
406
0
      return;
407
0
    unsigned int i;
408
0
    for (i = start; i < s.size();++i)
409
0
      if (isalpha(s[i]) && !isdigit(s[i])) {
410
0
        if (isupper(s[i])) s[i] = tolower(s[i]);
411
0
        else s[i] = toupper(s[i]);
412
0
      }
413
0
  }
414
415
  //! Shift the supplied char*: lowercase to upper, and upper to lower
416
  void InvertCase(char *cptr)
417
0
  {
418
0
    char *c;
419
0
    for (c = cptr;*c != '\0';++c)
420
0
      if (isalpha(*c) && !isdigit(*c)) {
421
0
        if (isupper(*c)) *c = tolower(*c);
422
0
        else *c = toupper(*c);
423
0
      }
424
0
  }
425
426
  //! "Clean" the supplied atom type, shifting the first character to uppercase,
427
  //! the second character (if it's a letter) to lowercase, and terminating with a NULL
428
  //! to strip off any trailing characters
429
  void CleanAtomType(char *id)
430
0
  {
431
0
    id[0] = toupper(id[0]);
432
0
    if (isalpha(id[1]) == 0)
433
0
      id[1] = '\0';
434
0
    else
435
0
      {
436
0
        id[1] = tolower(id[1]);
437
0
        id[2] = '\0';
438
0
      }
439
0
  }
440
441
  //! Transform the supplied vector<OBInternalCoord*> into cartesian and update
442
  //! the OBMol accordingly. The size of supplied internal coordinate vector
443
  //! has to be the same as the number of atoms in molecule (+ NULL in the
444
  //! beginning).
445
  //! Implements <a href="http://qsar.sourceforge.net/dicts/blue-obelisk/index.xhtml#zmatrixCoordinatesIntoCartesianCoordinates">blue-obelisk:zmatrixCoordinatesIntoCartesianCoordinates</a>
446
  void InternalToCartesian(std::vector<OBInternalCoord*> &vic,OBMol &mol)
447
0
  {
448
0
    vector3 n,nn,v1,v2,v3,avec,bvec,cvec;
449
0
    double dst = 0.0, ang = 0.0, tor = 0.0;
450
0
    OBAtom *atom;
451
0
    vector<OBAtom*>::iterator i;
452
0
    unsigned int index;
453
454
0
    if (vic.empty())
455
0
      return;
456
457
0
    if (vic[0] != nullptr) {
458
0
      std::vector<OBInternalCoord*>::iterator it = vic.begin();
459
0
      vic.insert(it, nullptr);
460
0
    }
461
462
0
    if (vic.size() != mol.NumAtoms() + 1) {
463
0
      string error = "Number of internal coordinates is not the same as";
464
0
      error += " the number of atoms in molecule";
465
0
      obErrorLog.ThrowError(__FUNCTION__, error, obError);
466
0
      return;
467
0
    }
468
469
0
    obErrorLog.ThrowError(__FUNCTION__,
470
0
                          "Ran OpenBabel::InternalToCartesian", obAuditMsg);
471
472
0
    for (atom = mol.BeginAtom(i);atom;atom = mol.NextAtom(i))
473
0
      {
474
0
        index = atom->GetIdx();
475
476
        // make sure we always have valid pointers
477
0
        if (index >= vic.size() || !vic[index])
478
0
          return;
479
480
0
        if (vic[index]->_a) // make sure we have a valid ptr
481
0
          {
482
0
            avec = vic[index]->_a->GetVector();
483
0
            dst = vic[index]->_dst;
484
0
          }
485
0
        else
486
0
          {
487
            // atom 1
488
0
            atom->SetVector(0.0, 0.0, 0.0);
489
0
            continue;
490
0
          }
491
492
0
        if (vic[index]->_b)
493
0
          {
494
0
            bvec = vic[index]->_b->GetVector();
495
0
            ang = vic[index]->_ang * DEG_TO_RAD;
496
0
          }
497
0
        else
498
0
          {
499
            // atom 2
500
0
            atom->SetVector(dst, 0.0, 0.0);
501
0
            continue;
502
0
          }
503
504
0
        if (vic[index]->_c)
505
0
          {
506
0
            cvec = vic[index]->_c->GetVector();
507
0
            tor = vic[index]->_tor * DEG_TO_RAD;
508
0
          }
509
0
        else
510
0
          {
511
            // atom 3
512
0
            cvec = VY;
513
0
            tor = 90. * DEG_TO_RAD;
514
0
          }
515
516
0
        v1 = avec - bvec;
517
0
        v2 = avec - cvec;
518
0
        n = cross(v1,v2);
519
0
        nn = cross(v1,n);
520
0
        n.normalize();
521
0
        nn.normalize();
522
523
0
        n  *= -sin(tor);
524
0
        nn *= cos(tor);
525
0
        v3 = n + nn;
526
0
        v3.normalize();
527
0
        v3 *= dst * sin(ang);
528
0
        v1.normalize();
529
0
        v1 *= dst * cos(ang);
530
0
        v2 = avec + v3 - v1;
531
532
0
        atom->SetVector(v2);
533
0
      }
534
535
    // Delete dummy atoms
536
0
    vector<OBAtom*> for_deletion;
537
0
    FOR_ATOMS_OF_MOL(a, mol)
538
0
      if (a->GetAtomicNum() == 0)
539
0
        for_deletion.push_back(&(*a));
540
0
    for(vector<OBAtom*>::iterator a_it=for_deletion.begin(); a_it!=for_deletion.end(); ++a_it)
541
0
      mol.DeleteAtom(*a_it);
542
543
0
  }
544
545
  //! Use the supplied OBMol and its Cartesian coordinates to generate
546
  //! a set of internal (z-matrix) coordinates as supplied in the
547
  //! vector<OBInternalCoord*> argument.
548
  //! Implements <a href="http://qsar.sourceforge.net/dicts/blue-obelisk/index.xhtml#cartesianCoordinatesIntoZmatrixCoordinates">blue-obelisk:cartesianCoordinatesIntoZmatrixCoordinates</a>.
549
  //! \todo Consider lengths, angles, and torsions for periodic systems
550
  void CartesianToInternal(std::vector<OBInternalCoord*> &vic,OBMol &mol)
551
0
  {
552
0
    double r,sum;
553
0
    OBAtom *atom,*nbr,*ref;
554
0
    vector<OBAtom*>::iterator i,j,m;
555
556
0
    obErrorLog.ThrowError(__FUNCTION__,
557
0
                          "Ran OpenBabel::CartesianToInternal", obAuditMsg);
558
559
    //set reference atoms
560
0
    for (atom = mol.BeginAtom(i);atom;atom = mol.NextAtom(i))
561
0
      {
562
0
        if      (atom->GetIdx() == 1)
563
0
          continue;
564
0
        else if (atom->GetIdx() == 2)
565
0
          {
566
0
            vic[atom->GetIdx()]->_a = mol.GetAtom(1);
567
0
            continue;
568
0
          }
569
0
        else if (atom->GetIdx() == 3)
570
0
          {
571
0
            if( (atom->GetVector()-mol.GetAtom(2)->GetVector()).length_2()
572
0
                <(atom->GetVector()-mol.GetAtom(1)->GetVector()).length_2())
573
0
              {
574
0
                vic[atom->GetIdx()]->_a = mol.GetAtom(2);
575
0
                vic[atom->GetIdx()]->_b = mol.GetAtom(1);
576
0
              }
577
0
            else
578
0
              {
579
0
                vic[atom->GetIdx()]->_a = mol.GetAtom(1);
580
0
                vic[atom->GetIdx()]->_b = mol.GetAtom(2);
581
0
              }
582
0
            continue;
583
0
          }
584
0
        sum=1.0E10;
585
0
        ref = mol.GetAtom(1);
586
0
        for(nbr = mol.BeginAtom(j);nbr && (i != j);nbr = mol.NextAtom(j))
587
0
          {
588
0
            r = (atom->GetVector()-nbr->GetVector()).length_2();
589
0
            if((r < sum) && (vic[nbr->GetIdx()]->_a != nbr) &&
590
0
               (vic[nbr->GetIdx()]->_b != nbr))
591
0
              {
592
0
                sum = r;
593
0
                ref = nbr;
594
0
              }
595
0
          }
596
597
0
        vic[atom->GetIdx()]->_a = ref;
598
0
        if (ref->GetIdx() >= 3)
599
0
          {
600
0
            vic[atom->GetIdx()]->_b = vic[ref->GetIdx()]->_a;
601
0
            vic[atom->GetIdx()]->_c = vic[ref->GetIdx()]->_b;
602
0
          }
603
0
        else
604
0
          {
605
0
            if(ref->GetIdx()== 1)
606
0
              {
607
0
                vic[atom->GetIdx()]->_b = mol.GetAtom(2);
608
0
                vic[atom->GetIdx()]->_c = mol.GetAtom(3);
609
0
              }
610
0
            else
611
0
              {//ref->GetIdx()== 2
612
0
                vic[atom->GetIdx()]->_b = mol.GetAtom(1);
613
0
                vic[atom->GetIdx()]->_c = mol.GetAtom(3);
614
0
              }
615
0
          }
616
0
      }
617
618
    //fill in geometries
619
0
    unsigned int k;
620
0
    vector3 v1,v2;
621
0
    OBAtom *a,*b,*c;
622
0
    for (k = 2;k <= mol.NumAtoms();++k)
623
0
      {
624
0
        atom = mol.GetAtom(k);
625
0
        a = vic[k]->_a;
626
0
        b = vic[k]->_b;
627
0
        c = vic[k]->_c;
628
0
        v1 = atom->GetVector() - a->GetVector();
629
0
        vic[k]->_dst = v1.length();
630
0
        if (k == 2)
631
0
          continue;
632
633
0
        v2 = b->GetVector()    - a->GetVector();
634
0
        vic[k]->_ang = vectorAngle(v1,v2);
635
0
        if (k == 3)
636
0
          continue;
637
638
0
        vic[k]->_tor = CalcTorsionAngle(atom->GetVector(),
639
0
                                        a->GetVector(),
640
0
                                        b->GetVector(),
641
0
                                        c->GetVector());
642
0
      }
643
644
    //check for linear geometries and try to correct if possible
645
0
    bool done;
646
0
    double ang;
647
0
    for (k = 2;k <= mol.NumAtoms();++k)
648
0
      {
649
0
        ang = fabs(vic[k]->_ang);
650
0
        if (ang > 5.0 && ang < 175.0)
651
0
          continue;
652
0
        atom = mol.GetAtom(k);
653
0
        done = false;
654
0
        for (a = mol.BeginAtom(i);a && a->GetIdx() < k && !done;a = mol.NextAtom(i))
655
0
          for (b=mol.BeginAtom(j);b && b->GetIdx()<a->GetIdx() && !done;b = mol.NextAtom(j))
656
0
            {
657
0
              v1 = atom->GetVector() - a->GetVector();
658
0
              v2 = b->GetVector() - a->GetVector();
659
0
              ang = fabs(vectorAngle(v1,v2));
660
0
              if (ang < 5.0 || ang > 175.0)
661
0
                continue;
662
663
              // Also check length considerations -- don't bother if the length > 10.0 Angstroms
664
0
              if (v1.length_2() > 99.999)
665
0
                continue;
666
667
0
              for (c = mol.BeginAtom(m);c && c->GetIdx() < atom->GetIdx();c = mol.NextAtom(m))
668
0
                if (c != atom && c != a && c != b)
669
0
                  break;
670
0
              if (!c)
671
0
                continue;
672
673
0
              vic[k]->_a = a;
674
0
              vic[k]->_b = b;
675
0
              vic[k]->_c = c;
676
0
              vic[k]->_dst = v1.length();
677
0
              vic[k]->_ang = vectorAngle(v1,v2);
678
0
              vic[k]->_tor = CalcTorsionAngle(atom->GetVector(),
679
0
                                              a->GetVector(),
680
0
                                              b->GetVector(),
681
0
                                              c->GetVector());
682
0
              if (!isfinite(vic[k]->_tor))
683
0
                vic[k]->_tor = 180.0;
684
0
              done = true;
685
0
            }
686
0
      }
687
0
  }
688
689
  void qtrfit (double *r,double *f,int size, double u[3][3])
690
0
  {
691
0
    int i;
692
0
    double xxyx, xxyy, xxyz;
693
0
    double xyyx, xyyy, xyyz;
694
0
    double xzyx, xzyy, xzyz;
695
0
    double d[4],q[4];
696
0
    double c[16],v[16];
697
0
    double rx,ry,rz,fx,fy,fz;
698
699
    /* generate the upper triangle of the quadratic form matrix */
700
701
0
    xxyx = 0.0;
702
0
    xxyy = 0.0;
703
0
    xxyz = 0.0;
704
0
    xyyx = 0.0;
705
0
    xyyy = 0.0;
706
0
    xyyz = 0.0;
707
0
    xzyx = 0.0;
708
0
    xzyy = 0.0;
709
0
    xzyz = 0.0;
710
711
0
    for (i = 0; i < size; ++i)
712
0
      {
713
0
        rx = r[i*3];
714
0
        ry = r[i*3+1];
715
0
        rz = r[i*3+2];
716
0
        fx = f[i*3];
717
0
        fy = f[i*3+1];
718
0
        fz = f[i*3+2];
719
720
0
        xxyx += fx * rx;
721
0
        xxyy += fx * ry;
722
0
        xxyz += fx * rz;
723
0
        xyyx += fy * rx;
724
0
        xyyy += fy * ry;
725
0
        xyyz += fy * rz;
726
0
        xzyx += fz * rx;
727
0
        xzyy += fz * ry;
728
0
        xzyz += fz * rz;
729
0
      }
730
731
0
    c[4*0+0] = xxyx + xyyy + xzyz;
732
733
0
    c[4*0+1] = xzyy - xyyz;
734
0
    c[4*1+1] = xxyx - xyyy - xzyz;
735
736
0
    c[4*0+2] = xxyz - xzyx;
737
0
    c[4*1+2] = xxyy + xyyx;
738
0
    c[4*2+2] = xyyy - xzyz - xxyx;
739
740
0
    c[4*0+3] = xyyx - xxyy;
741
0
    c[4*1+3] = xzyx + xxyz;
742
0
    c[4*2+3] = xyyz + xzyy;
743
0
    c[4*3+3] = xzyz - xxyx - xyyy;
744
745
    /* diagonalize c */
746
747
0
    matrix3x3::jacobi(4, c, d, v);
748
749
    /* extract the desired quaternion */
750
751
0
    q[0] = v[4*0+3];
752
0
    q[1] = v[4*1+3];
753
0
    q[2] = v[4*2+3];
754
0
    q[3] = v[4*3+3];
755
756
    /* generate the rotation matrix */
757
758
0
    u[0][0] = q[0]*q[0] + q[1]*q[1] - q[2]*q[2] - q[3]*q[3];
759
0
    u[1][0] = 2.0 * (q[1] * q[2] - q[0] * q[3]);
760
0
    u[2][0] = 2.0 * (q[1] * q[3] + q[0] * q[2]);
761
762
0
    u[0][1] = 2.0 * (q[2] * q[1] + q[0] * q[3]);
763
0
    u[1][1] = q[0]*q[0] - q[1]*q[1] + q[2]*q[2] - q[3]*q[3];
764
0
    u[2][1] = 2.0 * (q[2] * q[3] - q[0] * q[1]);
765
766
0
    u[0][2] = 2.0 * (q[3] * q[1] - q[0] * q[2]);
767
0
    u[1][2] = 2.0 * (q[3] * q[2] + q[0] * q[1]);
768
0
    u[2][2] = q[0]*q[0] - q[1]*q[1] - q[2]*q[2] + q[3]*q[3];
769
0
  }
770
771
772
773
  static double Roots[4];
774
775
0
#define ApproxZero 1E-7
776
0
#define IsZero(x)  ((double)fabs(x)<ApproxZero)
777
#ifndef PI
778
0
#define PI         3.14159265358979323846226433
779
#endif
780
0
#define OneThird      (1.0/3.0)
781
0
#define FourThirdsPI  (4.0*PI/3.0)
782
0
#define TwoThirdsPI   (2.0*PI/3.0)
783
784
  /*FUNCTION */
785
  /* receives: the co-efficients for a general
786
   *           equation of degree one.
787
   *           Ax + B = 0 !!
788
   */
789
  int SolveLinear(double A,double B)
790
0
  {
791
0
    if( IsZero(A) )
792
0
      return( 0 );
793
0
    Roots[0] = -B/A;
794
0
    return( 1 );
795
0
  }
796
797
  /*FUNCTION */
798
  /* receives: the co-efficients for a general
799
   *           linear equation of degree two.
800
   *           Ax^2 + Bx + C = 0 !!
801
   */
802
  int SolveQuadratic(double A,double B,double C)
803
0
  {
804
0
    double Descr, Temp, TwoA;
805
806
0
    if( IsZero(A) )
807
0
      return( SolveLinear(B,C) );
808
809
0
    TwoA = A+A;
810
0
    Temp = TwoA*C;
811
0
    Descr = B*B - (Temp+Temp);
812
0
    if( Descr<0.0 )
813
0
      return( 0 );
814
815
0
    if( Descr>0.0 )
816
0
      {
817
0
        Descr = sqrt(Descr);
818
        /* W. Press, B. Flannery, S. Teukolsky and W. Vetterling,
819
         * "Quadratic and Cubic Equations", Numerical Recipes in C,
820
         * Chapter 5, pp. 156-157, 1989.
821
         */
822
0
        Temp = (B<0.0)? -0.5*(B-Descr) : -0.5*(B+Descr);
823
0
        Roots[0] = Temp/A;
824
0
        Roots[1] = C/Temp;
825
826
0
        return( 2 );
827
0
      }
828
0
    Roots[0] = -B/TwoA;
829
0
    return( 1 );
830
0
  }
831
832
  /*FUNCTION */
833
  /* task: to return the cube root of the
834
   *       given value taking into account
835
   *       that it may be negative.
836
   */
837
  double CubeRoot(double X)
838
0
  {
839
0
    if( X>=0.0 )
840
0
      {
841
0
        return pow( X, OneThird );
842
0
      }
843
0
    else
844
0
      return -pow( -X, OneThird );
845
0
  }
846
847
  int SolveCubic(double A,double B,double C,double D)
848
0
  {
849
0
    double TwoA, ThreeA, BOver3A;
850
0
    double Temp, POver3, QOver2;
851
0
    double Desc, Rho, Psi;
852
853
854
0
    if( IsZero(A) )
855
0
      {
856
0
        return( SolveQuadratic(B,C,D) );
857
0
      }
858
859
0
    TwoA = A+A;
860
0
    ThreeA = TwoA+A;
861
0
    BOver3A = B/ThreeA;
862
0
    QOver2 = ((TwoA*BOver3A*BOver3A-C)*BOver3A+D)/TwoA;
863
0
    POver3 = (C-B*BOver3A)/ThreeA;
864
865
866
0
    Rho = POver3*POver3*POver3;
867
0
    Desc = QOver2*QOver2 + Rho;
868
869
0
    if( Desc<=0.0 )
870
0
      {
871
0
        Rho = sqrt( -Rho );
872
0
        Psi = OneThird*acos(-QOver2/Rho);
873
0
        Temp = CubeRoot( Rho );
874
0
        Temp = Temp+Temp;
875
876
0
        Roots[0] = Temp*cos( Psi )-BOver3A;
877
0
        Roots[1] = Temp*cos( Psi+TwoThirdsPI )-BOver3A;
878
0
        Roots[2] = Temp*cos( Psi+FourThirdsPI )-BOver3A;
879
0
        return( 3 );
880
0
      }
881
882
0
    if( Desc> 0.0 )
883
0
      {
884
0
        Temp = CubeRoot( -QOver2 );
885
0
        Roots[0] = Temp+Temp-BOver3A;
886
0
        Roots[1] = -Temp-BOver3A;
887
0
        return( 2 );
888
0
      }
889
890
0
    Desc = sqrt( Desc );
891
0
    Roots[0] = CubeRoot(Desc-QOver2)-CubeRoot(Desc+QOver2) - BOver3A;
892
893
0
    return( 1 );
894
0
  }
895
896
897
0
#define MAX_SWEEPS 50
898
899
  void ob_make_rmat(double a[3][3],double rmat[9])
900
0
  {
901
    /*
902
    onorm, dnorm - hold the sum of diagonals and off diagonals to check Jacobi completion
903
    d[3] - holds the diagonals of the input vector, which transofrm to become the Eigenvalues
904
    r1, r2 - hold 1st two Eigenvectors
905
    v1,v2,v3 - hold orthogonal unit vectors derived from Eigenvectors
906
    
907
    The junction performs a Jacobi Eigenvalue/vector determination 
908
    (https://en.wikipedia.org/wiki/Jacobi_eigenvalue_algorithm) on the supplied
909
    Inertial Tensor in a, and returns a unit transform matrix rmat as a row matrix.
910
    To work, a must be diagonally symmetric (i.e a[i][j] = a[j][i])
911
    v starts out holding the unit matrix (i.e. no transform in co-ordinate frame), 
912
    and undergoes the same rotations as applied to the Inertial Tensor in the Jacobi
913
    process to arrive at the new co-ordinate frame.
914
    Finally, the eigenvalues are sorted in order that the largest principal moment aligns to the 
915
    new x-axis
916
    */
917
0
    double onorm, dnorm; 
918
0
    double b, dma, q, t, c, s,d[3];
919
0
    double atemp, vtemp, dtemp,v[3][3];
920
0
    double r1[3],r2[3],v1[3],v2[3],v3[3];
921
0
    int i, j, k, l;
922
923
0
    memset((char*)d,'\0',sizeof(double)*3);
924
925
0
    for (j = 0; j < 3; ++j)
926
0
      {
927
0
        for (i = 0; i < 3; ++i)
928
0
          v[i][j] = 0.0;
929
930
0
        v[j][j] = 1.0;
931
0
        d[j] = a[j][j];
932
0
      }
933
934
0
    for (l = 1; l <= MAX_SWEEPS; ++l)
935
0
      {
936
0
        dnorm = 0.0;
937
0
        onorm = 0.0;
938
0
        for (j = 0; j < 3; ++j)
939
0
          {
940
0
            dnorm = dnorm + (double)fabs(d[j]);
941
0
            for (i = 0; i <= j - 1; ++i)
942
0
              {
943
0
                onorm = onorm + (double)fabs(a[i][j]);
944
0
              }
945
0
          }
946
947
0
        if((onorm/dnorm) <= 1.0e-12)
948
        /* Completion achieved (i.e. off-diagonals are all 0.0 within error)*/
949
0
          goto Exit_now;
950
0
        for (j = 1; j < 3; ++j)
951
0
          {
952
0
            for (i = 0; i <= j - 1; ++i)
953
0
              {
954
0
                b = a[i][j];
955
0
                if(fabs(b) > 0.0)
956
0
                  {
957
0
                    dma = d[j] - d[i];
958
0
                    if((fabs(dma) + fabs(b)) <=  fabs(dma))
959
0
                      t = b / dma;
960
0
                    else
961
0
                      {
962
0
                        q = 0.5 * dma / b;
963
0
                        t = 1.0/((double)fabs(q) + (double)sqrt(1.0+q*q));
964
0
                        if(q < 0.0)
965
0
                          t = -t;
966
0
                      }
967
0
                    c = 1.0/(double)sqrt(t * t + 1.0);
968
0
                    s = t * c;
969
0
                    a[i][j] = 0.0;
970
                    /* Perform a Jacobi rotation on the supplied matrix*/
971
0
                    for (k = 0; k <= i-1; ++k)
972
0
                      {
973
0
                        atemp = c * a[k][i] - s * a[k][j];
974
0
                        a[k][j] = s * a[k][i] + c * a[k][j];
975
0
                        a[k][i] = atemp;
976
0
                      }
977
0
                    for (k = i+1; k <= j-1; ++k)
978
0
                      {
979
0
                        atemp = c * a[i][k] - s * a[k][j];
980
0
                        a[k][j] = s * a[i][k] + c * a[k][j];
981
0
                        a[i][k] = atemp;
982
0
                      }
983
0
                    for (k = j+1; k < 3; ++k)
984
0
                      {
985
0
                        atemp = c * a[i][k] - s * a[j][k];
986
0
                        a[j][k] = s * a[i][k] + c * a[j][k];
987
0
                        a[i][k] = atemp;
988
0
                      }
989
                      /* Rotate the reference frame */
990
0
                    for (k = 0; k < 3; ++k)
991
0
                      {
992
0
                        vtemp = c * v[k][i] - s * v[k][j];
993
0
                        v[k][j] = s * v[k][i] + c * v[k][j];
994
0
                        v[k][i] = vtemp;
995
0
                      }
996
0
                    dtemp = c*c*d[i] + s*s*d[j] - 2.0*c*s*b;
997
0
                    d[j] = s*s*d[i] + c*c*d[j] +  2.0*c*s*b;
998
0
                    d[i] = dtemp;
999
0
                  }  /* end if */
1000
0
              } /* end for i */
1001
0
          } /* end for j */
1002
0
      } /* end for l */
1003
1004
0
  Exit_now:
1005
1006
    /* max_sweeps = l;*/
1007
1008
/* Now sort the eigenvalues and eigenvectors*/
1009
0
    for (j = 0; j < 3-1; ++j)
1010
0
      {
1011
0
        k = j;
1012
0
        dtemp = d[k];
1013
0
        for (i = j+1; i < 3; ++i)
1014
0
          if(d[i] < dtemp)
1015
0
            {
1016
0
              k = i;
1017
0
              dtemp = d[k];
1018
0
            }
1019
1020
0
        if(k > j)
1021
0
          {
1022
0
            d[k] = d[j];
1023
0
            d[j] = dtemp;
1024
0
            for (i = 0; i < 3 ; ++i)
1025
0
              {
1026
0
                dtemp = v[i][k];
1027
0
                v[i][k] = v[i][j];
1028
0
                v[i][j] = dtemp;
1029
0
              }
1030
0
          }
1031
0
      }
1032
1033
  /* Transfer the 1st two eigenvectors into r1 and r2*/
1034
0
    r1[0] = v[0][0];
1035
0
    r1[1] = v[1][0];
1036
0
    r1[2] = v[2][0];
1037
0
    r2[0] = v[0][1];
1038
0
    r2[1] = v[1][1];
1039
0
    r2[2] = v[2][1];
1040
1041
  /* Generate the 3rd unit vector for the new co-ordinate frame by cross product of r1 and r2*/
1042
0
    v3[0] =  r1[1]*r2[2] - r1[2]*r2[1];
1043
0
    v3[1] = -r1[0]*r2[2] + r1[2]*r2[0];
1044
0
    v3[2] =  r1[0]*r2[1] - r1[1]*r2[0];
1045
  /* Ensure it is normalised |v3|=1 */
1046
0
    s = (double)sqrt(v3[0]*v3[0] + v3[1]*v3[1] + v3[2]*v3[2]);
1047
0
    v3[0] /= s;
1048
0
    v3[1] /= s;
1049
0
    v3[2] /= s;
1050
1051
  /* Generate the 2nd unit vector for the new co-ordinate frame by cross product of v3 and r1*/
1052
0
    v2[0] =  v3[1]*r1[2] - v3[2]*r1[1];
1053
0
    v2[1] = -v3[0]*r1[2] + v3[2]*r1[0];
1054
0
    v2[2] =  v3[0]*r1[1] - v3[1]*r1[0];
1055
    /* Ensure it is normalised |v2|=1 */
1056
0
    s = (double)sqrt(v2[0]*v2[0] + v2[1]*v2[1] + v2[2]*v2[2]);
1057
0
    v2[0] /= s;
1058
0
    v2[1] /= s;
1059
0
    v2[2] /= s;
1060
1061
  /* Generate the 1st unit vector for the new co-ordinate frame by cross product of v2 and v3*/
1062
0
    v1[0] =  v2[1]*v3[2] - v2[2]*v3[1];
1063
0
    v1[1] = -v2[0]*v3[2] + v2[2]*v3[0];
1064
0
    v1[2] =  v2[0]*v3[1] - v2[1]*v3[0];
1065
     /* Ensure it is normalised |v1|=1 */
1066
0
    s = (double)sqrt(v1[0]*v1[0] + v1[1]*v1[1] + v1[2]*v1[2]);
1067
0
    v1[0] /= s;
1068
0
    v1[1] /= s;
1069
0
    v1[2] /= s;
1070
1071
  /* Transfer to the row matrix form for the result*/
1072
0
    rmat[0] = v1[0];
1073
0
    rmat[1] = v1[1];
1074
0
    rmat[2] = v1[2];
1075
0
    rmat[3] = v2[0];
1076
0
    rmat[4] = v2[1];
1077
0
    rmat[5] = v2[2];
1078
0
    rmat[6] = v3[0];
1079
0
    rmat[7] = v3[1];
1080
0
    rmat[8] = v3[2];
1081
0
  }
1082
1083
  static int get_roots_3_3(double mat[3][3], double roots[3])
1084
0
  {
1085
0
    double rmat[9];
1086
1087
0
    ob_make_rmat(mat,rmat);
1088
1089
0
    mat[0][0]=rmat[0];
1090
0
    mat[0][1]=rmat[3];
1091
0
    mat[0][2]=rmat[6];
1092
0
    mat[1][0]=rmat[1];
1093
0
    mat[1][1]=rmat[4];
1094
0
    mat[1][2]=rmat[7];
1095
0
    mat[2][0]=rmat[2];
1096
0
    mat[2][1]=rmat[5];
1097
0
    mat[2][2]=rmat[8];
1098
1099
0
    roots[0]=(double)Roots[0];
1100
0
    roots[1]=(double)Roots[1];
1101
0
    roots[2]=(double)Roots[2];
1102
1103
0
    return 1;
1104
0
  }
1105
1106
  double superimpose(double *r,double *f,int size)
1107
0
  {
1108
0
    int i,j;
1109
0
    double x,y,z,d2;
1110
0
    double mat[3][3],rmat[3][3],mat2[3][3],roots[3];
1111
1112
    /* make inertial cross tensor */
1113
0
    for(i=0;i<3;++i)
1114
0
      for(j=0;j<3;++j)
1115
0
        mat[i][j]=0.0;
1116
1117
0
    for(i=0;i < size;++i)
1118
0
      {
1119
0
        mat[0][0]+=r[3*i]  *f[3*i];
1120
0
        mat[1][0]+=r[3*i+1]*f[3*i];
1121
0
        mat[2][0]+=r[3*i+2]*f[3*i];
1122
0
        mat[0][1]+=r[3*i]  *f[3*i+1];
1123
0
        mat[1][1]+=r[3*i+1]*f[3*i+1];
1124
0
        mat[2][1]+=r[3*i+2]*f[3*i+1];
1125
0
        mat[0][2]+=r[3*i]  *f[3*i+2];
1126
0
        mat[1][2]+=r[3*i+1]*f[3*i+2];
1127
0
        mat[2][2]+=r[3*i+2]*f[3*i+2];
1128
0
      }
1129
1130
0
    d2=mat[0][0]*(mat[1][1]*mat[2][2]-mat[1][2]*mat[2][1])
1131
0
      -mat[0][1]*(mat[1][0]*mat[2][2]-mat[1][2]*mat[2][0])
1132
0
      +mat[0][2]*(mat[1][0]*mat[2][1]-mat[1][1]*mat[2][0]);
1133
1134
1135
    /* square matrix= ((mat transpose) * mat) */
1136
0
    for(i=0;i<3;++i)
1137
0
      for(j=0;j<3;++j)
1138
0
        {
1139
0
          x=mat[0][i]*mat[0][j]+mat[1][i]*mat[1][j]+mat[2][i]*mat[2][j];
1140
0
          mat2[i][j]=mat[i][j];
1141
0
          rmat[i][j]=x;
1142
0
        }
1143
0
    get_roots_3_3(rmat,roots);
1144
1145
0
    roots[0]=(roots[0]<0.0001) ? 0.0: (roots[0]);
1146
0
    roots[1]=(roots[1]<0.0001) ? 0.0: (roots[1]);
1147
0
    roots[2]=(roots[2]<0.0001) ? 0.0: (roots[2]);
1148
1149
    /* make sqrt of rmat, store in mat*/
1150
1151
0
    roots[0]=roots[0]<0.0001? 0.0: 1.0/(double)sqrt(roots[0]);
1152
0
    roots[1]=roots[1]<0.0001? 0.0: 1.0/(double)sqrt(roots[1]);
1153
0
    roots[2]=roots[2]<0.0001? 0.0: 1.0/(double)sqrt(roots[2]);
1154
1155
0
    if(d2<0.0)
1156
0
      {
1157
0
        if( (roots[0]>=roots[1]) && (roots[0]>=roots[2]) )
1158
0
          roots[0]*=-1.0;
1159
0
        if( (roots[1]>roots[0]) && (roots[1]>=roots[2]) )
1160
0
          roots[1]*=-1.0;
1161
0
        if( (roots[2]>roots[1]) && (roots[2]>roots[0]) )
1162
0
          roots[2]*=-1.0;
1163
0
      }
1164
1165
0
    for(i=0;i<3;++i)
1166
0
      for(j=0;j<3;++j)
1167
0
        mat[i][j]=roots[0]*rmat[i][0]*rmat[j][0]+
1168
0
          roots[1]*rmat[i][1]*rmat[j][1]+
1169
0
          roots[2]*rmat[i][2]*rmat[j][2];
1170
1171
    /* and multiply into original inertial cross matrix, mat2 */
1172
0
    for(i=0;i<3;++i)
1173
0
      for(j=0;j<3;++j)
1174
0
        rmat[i][j]=mat[0][j]*mat2[i][0]+
1175
0
          mat[1][j]*mat2[i][1]+
1176
0
          mat[2][j]*mat2[i][2];
1177
1178
    /* rotate all coordinates */
1179
0
    d2 = 0.0;
1180
0
    for(i=0;i<size;++i)
1181
0
      {
1182
0
        x=f[3*i]*rmat[0][0]+f[3*i+1]*rmat[0][1]+f[3*i+2]*rmat[0][2];
1183
0
        y=f[3*i]*rmat[1][0]+f[3*i+1]*rmat[1][1]+f[3*i+2]*rmat[1][2];
1184
0
        z=f[3*i]*rmat[2][0]+f[3*i+1]*rmat[2][1]+f[3*i+2]*rmat[2][2];
1185
0
        f[3*i  ]=x;
1186
0
        f[3*i+1]=y;
1187
0
        f[3*i+2]=z;
1188
1189
0
        x = r[i*3]   - f[i*3];
1190
0
        y = r[i*3+1] - f[i*3+1];
1191
0
        z = r[i*3+2] - f[i*3+2];
1192
0
        d2 += x*x+y*y+z*z;
1193
0
      }
1194
1195
0
    d2 /= (double) size;
1196
1197
0
    return((double)sqrt(d2));
1198
0
  }
1199
1200
  void get_rmat(double *rvec,double *r,double *f,int size)
1201
0
  {
1202
0
    int i,j;
1203
0
    double x,d2;
1204
0
    double mat[3][3],rmat[3][3],mat2[3][3],roots[3];
1205
1206
    /* make inertial cross tensor */
1207
0
    for(i=0;i<3;++i)
1208
0
      for(j=0;j<3;++j)
1209
0
        mat[i][j]=0.0;
1210
1211
0
    for(i=0;i < size;++i)
1212
0
      {
1213
0
        mat[0][0]+=r[3*i]  *f[3*i];
1214
0
        mat[1][0]+=r[3*i+1]*f[3*i];
1215
0
        mat[2][0]+=r[3*i+2]*f[3*i];
1216
0
        mat[0][1]+=r[3*i]  *f[3*i+1];
1217
0
        mat[1][1]+=r[3*i+1]*f[3*i+1];
1218
0
        mat[2][1]+=r[3*i+2]*f[3*i+1];
1219
0
        mat[0][2]+=r[3*i]  *f[3*i+2];
1220
0
        mat[1][2]+=r[3*i+1]*f[3*i+2];
1221
0
        mat[2][2]+=r[3*i+2]*f[3*i+2];
1222
0
      }
1223
1224
0
    d2=mat[0][0]*(mat[1][1]*mat[2][2]-mat[1][2]*mat[2][1])
1225
0
      -mat[0][1]*(mat[1][0]*mat[2][2]-mat[1][2]*mat[2][0])
1226
0
      +mat[0][2]*(mat[1][0]*mat[2][1]-mat[1][1]*mat[2][0]);
1227
1228
    /* square matrix= ((mat transpose) * mat) */
1229
0
    for(i=0;i<3;++i)
1230
0
      for(j=0;j<3;++j)
1231
0
        {
1232
0
          x=mat[0][i]*mat[0][j]+mat[1][i]*mat[1][j]+mat[2][i]*mat[2][j];
1233
0
          mat2[i][j]=mat[i][j];
1234
0
          rmat[i][j]=x;
1235
0
        }
1236
0
    get_roots_3_3(rmat,roots);
1237
1238
0
    roots[0]=(roots[0]<0.0001) ? 0.0: (roots[0]);
1239
0
    roots[1]=(roots[1]<0.0001) ? 0.0: (roots[1]);
1240
0
    roots[2]=(roots[2]<0.0001) ? 0.0: (roots[2]);
1241
1242
    /* make sqrt of rmat, store in mat*/
1243
1244
0
    roots[0]=(roots[0]<0.0001) ? 0.0: 1.0/(double)sqrt(roots[0]);
1245
0
    roots[1]=(roots[1]<0.0001) ? 0.0: 1.0/(double)sqrt(roots[1]);
1246
0
    roots[2]=(roots[2]<0.0001) ? 0.0: 1.0/(double)sqrt(roots[2]);
1247
1248
0
    if(d2<0.0)
1249
0
      {
1250
0
        if( (roots[0]>=roots[1]) && (roots[0]>=roots[2]) )
1251
0
          roots[0]*=-1.0;
1252
0
        if( (roots[1]>roots[0]) && (roots[1]>=roots[2]) )
1253
0
          roots[1]*=-1.0;
1254
0
        if( (roots[2]>roots[1]) && (roots[2]>roots[0]) )
1255
0
          roots[2]*=-1.0;
1256
0
      }
1257
1258
0
    for(i=0;i<3;++i)
1259
0
      for(j=0;j<3;++j)
1260
0
        mat[i][j]=roots[0]*rmat[i][0]*rmat[j][0]+
1261
0
          roots[1]*rmat[i][1]*rmat[j][1]+
1262
0
          roots[2]*rmat[i][2]*rmat[j][2];
1263
1264
    /* and multiply into original inertial cross matrix, mat2 */
1265
0
    for(i=0;i<3;++i)
1266
0
      for(j=0;j<3;++j)
1267
0
        rmat[i][j]=mat[0][j]*mat2[i][0]+
1268
0
          mat[1][j]*mat2[i][1]+
1269
0
          mat[2][j]*mat2[i][2];
1270
1271
0
    rvec[0] = rmat[0][0];
1272
0
    rvec[1] = rmat[0][1];
1273
0
    rvec[2] = rmat[0][2];
1274
0
    rvec[3] = rmat[1][0];
1275
0
    rvec[4] = rmat[1][1];
1276
0
    rvec[5] = rmat[1][2];
1277
0
    rvec[6] = rmat[2][0];
1278
0
    rvec[7] = rmat[2][1];
1279
0
    rvec[8] = rmat[2][2];
1280
0
  }
1281
1282
} // end namespace OpenBabel
1283
1284
//! \file obutil.cpp
1285
//! \brief Various utility methods.