source: pcp/src/mymath.c@ 64ca279

Last change on this file since 64ca279 was a0bcf1, checked in by Frederik Heber <heber@…>, 17 years ago

-initial commit
-Minimum set of files needed from ESPACK SVN repository
-Switch to three tantamount package parts instead of all relating to pcp (as at some time Ralf's might find inclusion as well)

  • Property mode set to 100644
File size: 13.9 KB
Line 
1/** \file mymath.c
2 * Linear algebra mathematical routines.
3 * Small library of often needed mathematical routines such as hard-coded
4 * vector VP3(), scalar SP(), matrix products RMat33Vec3(), RMatMat33(), RVec3Mat33(),
5 * multiplication with scalar SM(), euclidian distance Dist(),inverse RMatReci3(),
6 * transposed RTranspose3(), modulo Rest(), nullifying NV(), SetArrayToDouble0(),
7 * gamma function gammln(), gaussian error function derf(), integration via
8 * Simpsons Rule Simps().\n
9 * Also for printing matrixes PrintCMat330(), PrintRMat330() and vectors
10 * PrintCVec30(), PrintRVec30() to screen.\n
11 * All specialized for 3x3 real or complex ones.\n
12 * Rather specialized is RotateToAlign() which is needed in transforming the whole coordinate
13 * system in order to align a certain vector.
14 *
15 Project: ParallelCarParrinello
16 \author Jan Hamaekers
17 \date 2000
18
19 File: helpers.c
20 $Id: mymath.c,v 1.25 2007-03-29 13:38:30 foo Exp $
21*/
22
23#include<stdlib.h>
24#include<stdio.h>
25#include<stddef.h>
26#include<math.h>
27#include<string.h>
28#include"mymath.h"
29
30// use double precision fft when we have it
31#ifdef HAVE_CONFIG_H
32#include <config.h>
33#endif
34
35#ifdef HAVE_DFFTW_H
36#include "dfftw.h"
37#else
38#include "fftw.h"
39#endif
40
41#ifdef HAVE_GSL_GSL_SF_ERF_H
42#include "gsl/gsl_sf_erf.h"
43#endif
44
45
46/** efficiently compute x^n
47 * \param x argument
48 * \param n potency
49 * \return \f$x^n\f$
50 */
51inline double tpow(double x, int n)
52{
53 double y = 1;
54 int neg = (n < 0);
55
56 if (neg) n = -n;
57
58 while (n) {
59 if (n & 1) y *= x;
60 x *= x;
61 n >>= 1;
62 }
63 return neg ? 1.0/y : y;
64}
65
66
67/** Modulo function.
68 * Normal modulo operation, yet return value is >=0
69 * \param n denominator
70 * \param m divisor
71 * \return modulo >=0
72 */
73inline int Rest(int n, int m) /* normale modulo-Funktion, Ausgabe>=0 */
74{
75 int q = n%m;
76 if (q >= 0) return (q);
77 return ((q) + m);
78}
79
80/* Rechnungen */
81
82/** Real 3x3 inverse of matrix.
83 * Calculates the inverse of a matrix by b_ij = A_ij/det(A), where
84 * is A_ij is the matrix with row j and column i removed.
85 * \param B inverse matrix array (set by function)
86 * \param A matrix array to be inverted
87 * \return 0 - error: det A == 0, 1 - success
88 */
89inline int RMatReci3(double B[NDIM_NDIM], const double A[NDIM_NDIM])
90{
91 double detA = RDET3(A);
92 double detAReci;
93 if (detA == 0.0) return 1; // RDET3(A) yields precisely zero if A irregular
94 detAReci = 1./detA;
95 B[0] = detAReci*RDET2(A[4],A[5],A[7],A[8]); // A_11
96 B[1] = -detAReci*RDET2(A[1],A[2],A[7],A[8]); // A_12
97 B[2] = detAReci*RDET2(A[1],A[2],A[4],A[5]); // A_13
98 B[3] = -detAReci*RDET2(A[3],A[5],A[6],A[8]); // A_21
99 B[4] = detAReci*RDET2(A[0],A[2],A[6],A[8]); // A_22
100 B[5] = -detAReci*RDET2(A[0],A[2],A[3],A[5]); // A_23
101 B[6] = detAReci*RDET2(A[3],A[4],A[6],A[7]); // A_31
102 B[7] = -detAReci*RDET2(A[0],A[1],A[6],A[7]); // A_32
103 B[8] = detAReci*RDET2(A[0],A[1],A[3],A[4]); // A_33
104 return 0;
105}
106
107/** Real 3x3 Matrix multiplication.
108 * Hard-coded falk scheme for multiplication of matrix1 * matrix2
109 * \param C product matrix
110 * \param A matrix1 array
111 * \param B matrix2 array
112 */
113inline void RMatMat33(double C[NDIM*NDIM], const double A[NDIM*NDIM], const double B[NDIM*NDIM])
114{
115 C[0] = A[0]*B[0]+A[3]*B[1]+A[6]*B[2];
116 C[1] = A[1]*B[0]+A[4]*B[1]+A[7]*B[2];
117 C[2] = A[2]*B[0]+A[5]*B[1]+A[8]*B[2];
118 C[3] = A[0]*B[3]+A[3]*B[4]+A[6]*B[5];
119 C[4] = A[1]*B[3]+A[4]*B[4]+A[7]*B[5];
120 C[5] = A[2]*B[3]+A[5]*B[4]+A[8]*B[5];
121 C[6] = A[0]*B[6]+A[3]*B[7]+A[6]*B[8];
122 C[7] = A[1]*B[6]+A[4]*B[7]+A[7]*B[8];
123 C[8] = A[2]*B[6]+A[5]*B[7]+A[8]*B[8];
124}
125
126/** Real 3x3 Matrix vector multiplication.
127 * hard-coded falk scheme for multiplication of matrix * vector
128 * \param C resulting vector
129 * \param M matrix array
130 * \param V vector array
131 */
132inline void RMat33Vec3(double C[NDIM], const double M[NDIM*NDIM], const double V[NDIM])
133{
134 C[0] = M[0]*V[0]+M[3]*V[1]+M[6]*V[2];
135 C[1] = M[1]*V[0]+M[4]*V[1]+M[7]*V[2];
136 C[2] = M[2]*V[0]+M[5]*V[1]+M[8]*V[2];
137}
138
139/** Real 3x3 vector Matrix multiplication.
140 * hard-coded falk scheme for multiplication of vector * matrix
141 * \param C resulting vector
142 * \param V vector array
143 * \param M matrix array
144 */
145inline void RVec3Mat33(double C[NDIM], const double V[NDIM], const double M[NDIM*NDIM])
146{
147 C[0] = V[0]*M[0]+V[1]*M[1]+V[2]*M[2];
148 C[1] = V[0]*M[3]+V[1]*M[4]+V[2]*M[5];
149 C[2] = V[0]*M[6]+V[1]*M[7]+V[2]*M[8];
150}
151
152/** Real 3x3 vector product.
153 * vector product of vector1 x vector 2
154 * \param V resulting orthogonal vector
155 * \param A vector1 array
156 * \param B vector2 array
157 */
158inline void VP3(double V[NDIM], double A[NDIM], double B[NDIM])
159{
160 V[0] = A[1]*B[2]-A[2]*B[1];
161 V[1] = A[2]*B[0]-A[0]*B[2];
162 V[2] = A[0]*B[1]-A[1]*B[0];
163}
164
165/** Real transposition of 3x3 Matrix.
166 * \param *A Matrix
167 */
168#ifdef HAVE_INLINE
169inline void RTranspose3(double *A) {
170#else
171void RTranspose3(double *A) {
172#endif
173 double dummy = A[1];
174 A[1] = A[3];
175 A[3] = dummy;
176 dummy = A[2];
177 A[2] = A[6];
178 A[6] = dummy;
179 dummy = A[5];
180 A[5] = A[7];
181 A[7] = dummy;
182}
183
184/** Scalar product.
185 * \param *a first vector
186 * \param *b second vector
187 * \param n dimension
188 * \return scalar product of a with b
189 */
190#ifdef HAVE_INLINE
191inline double SP(const double *a, const double *b, const int n) {
192#else
193double SP(const double *a, const double *b, const int n) {
194#endif
195 int i;
196 double dummySP;
197 dummySP = 0;
198 for (i = 0; i < n; i++) {
199 dummySP += ((a[i]) * (b[i]));
200 }
201 return dummySP;
202}
203
204/** Euclidian distance.
205 * \param *a first vector
206 * \param *b second vector
207 * \param n dimension
208 * \return sqrt(a-b)
209 */
210inline double Dist(const double *a, const double *b, const int n){
211 int i;
212 double dummyDist = 0;
213 for (i = 0; i < n; i++) {
214 dummyDist += (a[i]-b[i])*(a[i]-b[i]);
215 }
216 return (sqrt(dummyDist));
217}
218
219
220/** Multiplication with real scalar.
221 * \param *a vector (changed)
222 * \param c scalar
223 * \param n dimension
224 */
225inline void SM(double *a, const double c, const int n)
226{
227 int i;
228 for (i = 0; i < n; i++) a[i] *= c;
229}
230
231/** nullify vector.
232 * sets all components of vector /a a to zero.
233 * \param *a vector (changed)
234 * \param n dimension
235 */
236#ifdef HAVE_INLINE
237inline void NV(double *a, const int n) {
238#else
239void NV(double *a, const int n) {
240#endif
241 int i;
242 for (i = 0; i < n; i++) a[i] = 0;
243}
244
245/** Differential step sum.
246 * Sums up entries from array *dx, taking each \a incx of it, \a n times.
247 * \param n number of steps
248 * \param *dx incremental value array
249 * \param incx step width
250 * \return sum_i+=incx dx[i]
251 * \sa Simps
252 */
253#ifdef HAVE_INLINE
254inline double dSum(int n, double *dx, int incx) {
255#else
256double dSum(int n, double *dx, int incx) {
257#endif
258 int i;
259 double res;
260 if (n <= 0) return(0.0);
261 res = dx[0];
262 for(i = incx+1; i <= n*incx; i +=incx)
263 res += dx[i-1];
264 return (res);
265}
266
267/** Simpson formula for integration.
268 * \a f is replaced by a polynomial of 2nd degree in order
269 * to approximate the integral
270 * \param n number of sampling points
271 * \param *f function value array
272 * \param h half the width of the integration interval
273 * \return \f$\int_a^b f(x) dx = \frac{h}{3} (y_0 + 4 y_1 + 2 y_2 + 4 y_3 + ... + 2 y_{n-2} + 4 y_{n-1} + y_n)\f$
274 * \sa dSum() - used by this function.
275 */
276#ifdef HAVE_INLINE
277inline double Simps(int n, double *f, double h) {
278#else
279double Simps(int n, double *f, double h) {
280#endif
281 double res;
282 int nm12=(n-1)/2;
283 if (nm12*2 != n-1) {
284 fprintf(stderr,"Simps: wrong n in Simps");
285 }
286 res = 4.*dSum(nm12,&f[1],2)+2.*dSum(nm12-1,&f[2],2)+f[0]+f[n-1];
287 return(res*h/3.);
288}
289
290/* derf */
291
292#ifndef HAVE_GSL_GSL_SF_ERF_H
293/** Logarithm of Gamma function.
294 * \param xx x-value for function
295 * \return ln(gamma(xx))
296 * \note formula and coefficients are taken from "Numerical Receipes in C"
297 */
298static double gammln(double xx) {
299 int j;
300 double x,tmp,ser;
301 double stp = 2.50662827465;
302 double cof[6] = { 76.18009173,-86.50532033,24.01409822,-1.231739516,.120858003e-2,-.536382e-5 };
303 x = xx -1.;
304 tmp = x+5.5;
305 tmp = (x+0.5)*log(tmp)-tmp;
306 ser = 1.;
307 for(j=0;j<6;j++) {
308 x+=1.0;
309 ser+=cof[j]/x;
310 }
311 return(tmp+log(stp*ser));
312}
313
314/** Series used by gammp().
315 * \param a
316 * \param x
317 * \bug when x equals 0 is 0 returned?
318 * \note formula and coefficients are taken from "Numerical Receipes in C"
319 * \warning maximum precision 1e-7
320 */
321static double gser(double a, double x) {
322 double gln = gammln(a);
323 double ap,sum,del;
324 int n;
325 if (x <= 0.) {
326 if (x < 0.) {
327 return(0.0);
328 }
329 }
330 ap=a;
331 sum=1./a;
332 del=sum;
333 for (n=1;n<=100;n++) {
334 ap += 1.;
335 del *=x/ap;
336 sum += del;
337 if(fabs(del) < fabs(sum)*1.e-7) {
338 return(sum*exp(-x+a*log(x)-gln));
339 }
340 }
341 return(sum*exp(-x+a*log(x)-gln));
342}
343
344/** Continued fraction used by gammp().
345 * \param a
346 * \param x
347 * \note formula and coefficients are taken from "Numerical Receipes in C"
348 */
349static double gcf(double a, double x) {
350 double gln = gammln(a);
351 double gold = 0.0;
352 double a0 = 1.;
353 double a1 = x;
354 double b0 = 0.;
355 double b1 = 1.;
356 double fac = 1.;
357 double an,ana,anf,g=0.0;
358 int n;
359 for (n=1; n <= 100; n++) {
360 an = n;
361 ana = an-a;
362 a0=(a1+a0*ana)*fac;
363 b0=(b1+b0*ana)*fac;
364 anf=an*fac;
365 a1=x*a0+anf*a1;
366 b1=x*b0+anf*b1;
367 if(a1 != 0.) {
368 fac=1./a1;
369 g=b1*fac;
370 if (fabs((g-gold)/g)<1.e-7) {
371 return(exp(-x+a*log(x)-gln)*g);
372 }
373 }
374 }
375 return(exp(-x+a*log(x)-gln)*g);
376}
377
378/** Incomplete gamma function.
379 * Either calculated via series gser() or via continued fraction gcf()
380 * Needed by derf()
381 * \f[
382 * gammp(a,x) = \frac{1}{\gamma(a)} \int_x^\infty t^{a-1} \exp(-t) dt
383 * \f]
384 * \param a
385 * \param x
386 * \return f(a,x) = (x < 1+a) ? gser(a,x) : 1-gcf(a,x)
387 * \note formula and coefficients are taken from "Numerical Receipes in C"
388 */
389static double gammp(double a, double x) {
390 double res;
391 if (x < a+1.) {
392 res = gser(a,x);
393 } else {
394 res = 1.-gcf(a,x);
395 }
396 return(res);
397}
398#endif
399
400/** Error function of integrated normal distribution.
401 * Either realized via GSL function gsl_sf_erf or via gammp()
402 * \f[
403 erf(x) = \frac{2}{\sqrt{\pi}} \int^x_0 \exp(-t^2) dt
404 = \pi^{-1/2} \gamma(\frac{1}{2},x^2)
405 * \f]
406 * \param x
407 * \return f(x) = sign(x) * gammp(0.5,x^2)
408 * \sa gammp
409 */
410#ifdef HAVE_INLINE
411inline double derf(double x) {
412#else
413double derf(double x) {
414#endif
415 double res;
416 #ifdef HAVE_GSL_GSL_SF_ERF_H
417 // call gsl instead of numerical recipes routines
418 res = gsl_sf_erf(x);
419 #else
420 if (x < 0) {
421 res = -gammp(0.5,x*x);
422 } else {
423 res = gammp(0.5,x*x);
424 }
425 #endif
426 return(res);
427}
428
429/** Sets array to zero.
430 * \param *a pointer to the double array
431 * \param n number of array elements
432 */
433inline void SetArrayToDouble0(double *a, int n)
434{
435 int i;
436 for(i=0;i<n;i++) a[i] = 0.0;
437}
438
439/** Print complex 3x3 matrix.
440 * Checks if matrix has only zero entries, if not print each to screen: (re, im) ...
441 * \param M matrix array
442 */
443void PrintCMat330(fftw_complex M[NDIM_NDIM])
444{
445 int i,p=0;
446 for (i=0;i<NDIM_NDIM;i++)
447 if (M[i].re != 0.0 || M[i].im != 0.0) p++;
448 if (p) {
449 for (i=0;i<NDIM_NDIM;i++) fprintf(stderr," (%f %f)", M[i].re, M[i].im);
450 fprintf(stderr,"\n");
451 }
452}
453
454/** Print real 3x3 matrix.
455 * Checks if matrix has only zero entries, if not print each to screen: re ...
456 * \param M matrix array
457 */
458void PrintRMat330(fftw_real M[NDIM_NDIM])
459{
460 int i,p=0;
461 for (i=0;i<NDIM_NDIM;i++)
462 if (M[i] != 0.0) p++;
463 if (p) {
464 for (i=0;i<NDIM_NDIM;i++) fprintf(stderr," %f", M[i]);
465 fprintf(stderr,"\n");
466 }
467}
468
469/** Print complex 3-dim vector.
470 * Checks if vector has only zero entries, if not print each to screen: (re, im) ...
471 * \param M vector array
472 */
473void PrintCVec30(fftw_complex M[NDIM])
474{
475 int i,p=0;
476 for (i=0;i<NDIM;i++)
477 if (M[i].re != 0.0 || M[i].im != 0.0) p++;
478 if (p) {
479 for (i=0;i<NDIM;i++) fprintf(stderr," (%f %f)", M[i].re, M[i].im);
480 fprintf(stderr,"\n");
481 }
482}
483
484/** Print real 3-dim vector.
485 * Checks if vector has only zero entries, if not print each to screen: re ...
486 * \param M matrix array
487 */
488void PrintRVec30(fftw_real M[NDIM])
489{
490 int i,p=0;
491 for (i=0;i<NDIM;i++)
492 if (M[i] != 0.0) p++;
493 if (p) {
494 for (i=0;i<NDIM;i++) fprintf(stderr," %f", M[i]);
495 fprintf(stderr,"\n");
496 }
497}
498
499/** Rotates \a matrix, such that simultaneously given \a vector is aligned with z axis.
500 * Is used to rotate the unit cell in case of an external magnetic field. This field
501 * is rotated so that it aligns with z axis in order to simplify necessary perturbation
502 * calculations (only one component of each perturbed wave function necessary then).
503 * \param vector which is aligned with z axis by rotation \a Q
504 * \param Q return rotation matrix
505 * \param matrix which is transformed under the above rotation \a Q
506 */
507void RotateToAlign(fftw_real Q[NDIM_NDIM], fftw_real matrix[NDIM_NDIM], fftw_real vector[NDIM]) {
508 double tmp[NDIM_NDIM], Q1[NDIM_NDIM], Qtmp[NDIM_NDIM];
509 double alpha, beta, new_y;
510 int i,j ;
511
512 // calculate rotation angles
513 if (vector[0] < MYEPSILON) {
514 alpha = 0;
515 } else if (vector[1] > MYEPSILON) {
516 alpha = atan(-vector[0]/vector[1]);
517 } else alpha = PI/2;
518 new_y = -sin(alpha)*vector[0]+cos(alpha)*vector[1];
519 if (new_y < MYEPSILON) {
520 beta = 0;
521 } else if (vector[2] > MYEPSILON) {
522 beta = atan(-new_y/vector[2]);//asin(-vector[1]/vector[2]);
523 } else beta = PI/2;
524
525 // create temporary matrix copy
526 // set Q to identity
527 for (i=0;i<NDIM;i++)
528 for (j=0;j<NDIM;j++) {
529 Q[i*NDIM+j] = (i == j) ? 1 : 0;
530 tmp[i*NDIM+j] = matrix[i*NDIM+j];
531 }
532
533 // construct rotation matrices
534 Q1[0] = cos(alpha);
535 Q1[1] = sin(alpha);
536 Q1[2] = 0;
537 Q1[3] = -sin(alpha);
538 Q1[4] = cos(alpha);
539 Q1[5] = 0;
540 Q1[6] = 0;
541 Q1[7] = 0;
542 Q1[8] = 1;
543 // apply rotation and store
544 RMatMat33(tmp,Q1,matrix);
545 RMatMat33(Qtmp,Q1,Q);
546
547 Q1[0] = 1;
548 Q1[1] = 0;
549 Q1[2] = 0;
550 Q1[3] = 0;
551 Q1[4] = cos(beta);
552 Q1[5] = sin(beta);
553 Q1[6] = 0;
554 Q1[7] = -sin(beta);
555 Q1[8] = cos(beta);
556 // apply rotation and store
557 RMatMat33(matrix,Q1,tmp);
558 RMatMat33(Q,Q1,Qtmp);
559
560 // in order to avoid unncessary calculations, set everything below epsilon to zero
561 for (i=0;i<NDIM_NDIM;i++) {
562 matrix[i] = (fabs(matrix[i]) > MYEPSILON) ? matrix[i] : 0;
563 Q[i] = (fabs(Q[i]) > MYEPSILON) ? Q[i] : 0;
564 }
565}
Note: See TracBrowser for help on using the repository browser.