ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/group/trunk/OOPSE-3.0/src/openbabel/obutil.cpp
Revision: 2450
Committed: Thu Nov 17 20:38:45 2005 UTC (18 years, 8 months ago) by gezelter
File size: 32215 byte(s)
Log Message:
Unifying config.h stuff and making sure the OpenBabel codes can find
our default (and environment variable) Force Field directories.

File Contents

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