OpenMD 3.0
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
Wigner3jm.cpp
1/*
2 * Matpack Wigner3jm special function imported and modified for use in
3 * OpenMD
4 *
5 * Matpack Library Release 1.9.0
6 * Copyright (C) 1991-2003 by Berndt M. Gammel. All rights reserved.
7 *
8 * Permission to use, copy, and distribute Matpack in its entirety
9 * and its documentation for non-commercial purpose and without fee
10 * is hereby granted, provided that this license information and
11 * copyright notice appear unmodified in all copies. This software
12 * is provided 'as is' without express or implied warranty. In no
13 * event will the author be held liable for any damages arising from
14 * the use of this software.
15 *
16 * Note that distributing Matpack 'bundled' in with any product is
17 * considered to be a 'commercial purpose'.
18 *
19 * The software may be modified for your own purposes, but modified
20 * versions may not be distributed without prior consent of the
21 * author.
22 *
23 * Read the COPYRIGHT and README files in this distribution about
24 * registration and installation of Matpack.
25 */
26
27#include "Wigner3jm.hpp"
28
29#include <cfloat>
30#include <cmath>
31#include <cstdio>
32
33#include "utils/simError.h"
34
35namespace MATPACK {
36
37 //-----------------------------------------------------------------------------//
38 //
39 // void ThreeJSymbolM (RealType l1, RealType l2, RealType l3, RealType m1,
40 // RealType &m2min, RealType &m2max, RealType *thrcof, int
41 // ndim, int &errflag)
42 //
43 // Evaluate the Wigner 3j symbol
44 //
45 // g(m2) = ( l1 l2 l3 )
46 // ( m1 m2 -m1-m2 )
47 //
48 // for all allowed values of m2, the other parameters being held fixed.
49 //
50 // Input Arguments:
51 // ----------------
52 //
53 // RealType l1
54 // RealType l2
55 // RealType l3
56 // RealType m1 Parameters in 3j symbol.
57 //
58 // int ndim Declared length of thrcof in calling program.
59 //
60 // Output Arguments:
61 // -----------------
62 //
63 // RealType &m2min Smallest allowable m2 in 3j symbol.
64 // RealType &m2max Largest allowable m2 in 3j symbol.
65 // RealType *thrcof Set of 3j coefficients generated by evaluating the
66 // 3j symbol for all allowed values of m2. thrcof(i)
67 // will contain g(m2min+i), i=0,2,...,m2max-m2min.
68 //
69 // int &errflag Error flag.
70 // errflag=0 No errors.
71 // errflag=1 Either l1 < abs(m1) or l1+abs(m1)
72 // non-integer. errflag=2 abs(l1-l2)<= l3 <= l1+l2 not
73 // satisfied. errflag=3 l1+l2+l3 not an integer.
74 // errflag=4 m2max-m2min not an integer. errflag=5 m2max
75 // less than m2min. errflag=6 ndim less than
76 // m2max-m2min+1.
77 // Description:
78 // ------------
79 //
80 // Although conventionally the parameters of the vector addition
81 // coefficients satisfy certain restrictions, such as being integers
82 // or integers plus 1/2, the restrictions imposed on input to this
83 // subroutine are somewhat weaker. See, for example, Section 27.9 of
84 // Abramowitz and Stegun or Appendix C of Volume II of A. Messiah.
85 //
86 // The restrictions imposed by this subroutine are
87 //
88 // 1. l1 >= abs(m1) and l1+abs(m1) must be an integer
89 // 2. abs(l1-l2) <= l3 <= l1+l2
90 // 3. l1+l2+l3 must be an integer
91 // 4. m2max-m2min must be an integer, where
92 // m2max=min(l2,l3-m1) and m2min=max(-l2,-l3-m1)
93 //
94 // If the conventional restrictions are satisfied, then these
95 // restrictions are also met.
96 //
97 // The user should be cautious in using input parameters that do
98 // not satisfy the conventional restrictions. For example, the
99 // the subroutine produces values of
100 // g(m2) = (0.75 1.50 1.75 )
101 // (0.25 m2 -0.25-m2)
102 // for m2=-1.5,-0.5,0.5,1.5 but none of the symmetry properties of the
103 // 3j symbol, set forth on page 1056 of Messiah, is satisfied.
104 //
105 // The subroutine generates g(m2min), g(m2min+1), ..., g(m2max)
106 // where m2min and m2max are defined above. The sequence g(m2) is
107 // generated by a three-term recurrence algorithm with scaling to
108 // control overflow. Both backward and forward recurrence are used to
109 // maintain numerical stability. The two recurrence sequences are
110 // matched at an interior point and are normalized from the unitary
111 // property of 3j coefficients and Wigner's phase convention.
112 //
113 // The algorithm is suited to applications in which large quantum
114 // numbers arise, such as in molecular dynamics.
115 //
116 // References:
117 // -----------
118 // 1. Abramowitz, M., and Stegun, I. A., Eds., Handbook
119 // of Mathematical Functions with Formulas, Graphs
120 // and Mathematical Tables, NBS Applied Mathematics
121 // Series 55, June 1964 and subsequent printings.
122 // 2. Messiah, Albert., Quantum Mechanics, Volume II,
123 // North-Holland Publishing Company, 1963.
124 // 3. Schulten, Klaus and Gordon, Roy G., Exact recursive
125 // evaluation of 3j and 6j coefficients for quantum-
126 // mechanical coupling of angular momenta, J Math
127 // Phys, v 16, no. 10, October 1975, pp. 1961-1970.
128 // 4. Schulten, Klaus and Gordon, Roy G., Semiclassical
129 // approximations to 3j and 6j coefficients for
130 // quantum-mechanical coupling of angular momenta,
131 // J Math Phys, v 16, no. 10, October 1975, pp. 1971-1988.
132 // 5. Schulten, Klaus and Gordon, Roy G., Recursive
133 // evaluation of 3j and 6j coefficients, Computer
134 // Phys Comm, v 11, 1976, pp. 269-278.
135 // 6. SLATEC library, category C19,
136 // double precision algorithm DRC3JM.F
137 // Keywords: 3j coefficients, 3j symbols, Clebsch-Gordan coefficients,
138 // Racah coefficients, vector addition coefficients,
139 // Wigner coefficients
140 // Author: Gordon, R. G., Harvard University
141 // Schulten, K., Max Planck Institute
142 // Revision history (YYMMDD)
143 // 750101 DATE WRITTEN
144 // 880515 SLATEC prologue added by G. C. Nielson, NBS; parameters
145 // HUGE and TINY revised to depend on D1MACH.
146 // 891229 Prologue description rewritten; other prologue sections
147 // revised; MMATCH (location of match point for recurrences)
148 // removed from argument list; argument IER changed to serve
149 // only as an error flag (previously, in cases without error,
150 // it returned the number of scalings); number of error codes
151 // increased to provide more precise error information;
152 // program comments revised; SLATEC error handler calls
153 // introduced to enable printing of error messages to meet
154 // SLATEC standards. These changes were done by D. W. Lozier,
155 // M. A. McClain and J. M. Smith of the National Institute
156 // of Standards and Technology, formerly NBS.
157 // 910415 Mixed type expressions eliminated; variable C1 initialized;
158 // description of THRCOF expanded. These changes were done by
159 // D. W. Lozier.
160 // 7. Rewritting of the SLATEX algorithm in C++ and adaption to the
161 // Matpack C++ Numerics and Graphics Library by Berndt M. Gammel
162 // in June 1997.
163 //
164 //-----------------------------------------------------------------------------//
165
166 void Wigner3jm(RealType l1, RealType l2, RealType l3, RealType m1,
167 RealType& m2min, RealType& m2max, RealType* thrcof, int ndim,
168 int& errflag) {
169 // In single precision, the largest floating point number is not
170 // the same as in double precision:
171#ifdef SINGLE_PRECISION
172 RealType MaxFloat = FLT_MAX;
173#else
174 RealType MaxFloat = DBL_MAX;
175#endif
176
177 const RealType zero = 0.0, eps = 0.01, one = 1.0, two = 2.0;
178
179 int nfin, nlim, i, n, index, lstep, nfinp1, nfinp2, nfinp3, nstep2;
180 RealType oldfac, dv, newfac,
181 sumbac = 0.0, thresh, a1s, sumfor, sumuni, sum1, sum2, x, y, m2, m3, x1,
182 x2, x3, y1, y2, y3, cnorm, ratio, a1, c1, c2, c1old = 0.0, sign1, sign2;
183
184 // Parameter adjustments
185 --thrcof;
186
187 errflag = 0;
188
189 // "hugeRealType" is the square root of one twentieth of the
190 // largest floating point number, approximately.
191
192 RealType hugeRealType = sqrt(MaxFloat / 20.0), srhuge = sqrt(hugeRealType),
193 tiny = one / hugeRealType, srtiny = one / srhuge;
194
195 // lmatch = zero
196
197 // Check error conditions 1, 2, and 3.
198 if (l1 - fabs(m1) + eps < zero ||
199 fmod(l1 + fabs(m1) + eps, one) >= eps + eps) {
200 errflag = 1;
201
202 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH, "%s: %s",
203 "ThreeJSymbolM",
204 "l1-abs(m1) less than zero or l1+abs(m1) not integer.");
205 painCave.isFatal = 1;
206 simError();
207
208 return;
209 } else if (l1 + l2 - l3 < -eps || l1 - l2 + l3 < -eps ||
210 -(l1) + l2 + l3 < -eps) {
211 errflag = 2;
212
213 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH, "%s: %s",
214 "ThreeJSymbolM",
215 "l1, l2, l3 do not satisfy triangular condition.");
216 painCave.isFatal = 1;
217 simError();
218
219 return;
220 } else if (fmod(l1 + l2 + l3 + eps, one) >= eps + eps) {
221 errflag = 3;
222
223 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH, "%s: %s",
224 "ThreeJSymbolM", "l1+l2+l3 not integer.");
225 painCave.isFatal = 1;
226 simError();
227
228 return;
229 }
230
231 // limits for m2
232 m2min = MpMax(-l2, -l3 - m1);
233 m2max = MpMin(l2, l3 - m1);
234
235 // Check error condition 4.
236 if (fmod(m2max - m2min + eps, one) >= eps + eps) {
237 errflag = 4;
238
239 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH, "%s: %s",
240 "ThreeJSymbolM", "m2max-m2min not integer.");
241 painCave.isFatal = 1;
242 simError();
243
244 return;
245 }
246 if (m2min < m2max - eps) goto L20;
247 if (m2min < m2max + eps) goto L10;
248
249 // Check error condition 5.
250 errflag = 5;
251
252 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH, "%s: %s",
253 "ThreeJSymbolM", "m2min greater than m2max.");
254 painCave.isFatal = 1;
255 simError();
256
257 return;
258
259 // This is reached in case that m2 and m3 can take only one value.
260 L10:
261 // mscale = 0
262 thrcof[1] = (odd(int(fabs(l2 - l3 - m1) + eps)) ? -one : one) /
263 sqrt(l1 + l2 + l3 + one);
264 return;
265
266 // This is reached in case that M1 and M2 take more than one
267 // value.
268 L20:
269 // mscale = 0
270 nfin = int(m2max - m2min + one + eps);
271 if (ndim - nfin >= 0) goto L23;
272
273 // Check error condition 6.
274
275 errflag = 6;
276 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH, "%s: %s",
277 "ThreeJSymbolM",
278 "Dimension of result array for 3j coefficients too small.");
279 painCave.isFatal = 1;
280 simError();
281
282 return;
283
284 // Start of forward recursion from m2 = m2min
285
286 L23:
287 m2 = m2min;
288 thrcof[1] = srtiny;
289 newfac = 0.0;
290 c1 = 0.0;
291 sum1 = tiny;
292
293 lstep = 1;
294 L30:
295 ++lstep;
296 m2 += one;
297 m3 = -m1 - m2;
298
299 oldfac = newfac;
300 a1 = (l2 - m2 + one) * (l2 + m2) * (l3 + m3 + one) * (l3 - m3);
301 newfac = sqrt(a1);
302
303 dv = (l1 + l2 + l3 + one) * (l2 + l3 - l1) -
304 (l2 - m2 + one) * (l3 + m3 + one) - (l2 + m2 - one) * (l3 - m3 - one);
305
306 if (lstep - 2 > 0) c1old = fabs(c1);
307
308 // L32:
309 c1 = -dv / newfac;
310
311 if (lstep > 2) goto L60;
312
313 // If m2 = m2min + 1, the third term in the recursion equation
314 // vanishes, hence
315
316 x = srtiny * c1;
317 thrcof[2] = x;
318 sum1 += tiny * c1 * c1;
319 if (lstep == nfin) goto L220;
320 goto L30;
321
322 L60:
323 c2 = -oldfac / newfac;
324
325 // Recursion to the next 3j coefficient
326 x = c1 * thrcof[lstep - 1] + c2 * thrcof[lstep - 2];
327 thrcof[lstep] = x;
328 sumfor = sum1;
329 sum1 += x * x;
330 if (lstep == nfin) goto L100;
331
332 // See if last unnormalized 3j coefficient exceeds srhuge
333
334 if (fabs(x) < srhuge) goto L80;
335
336 // This is reached if last 3j coefficient larger than srhuge, so
337 // that the recursion series thrcof(1), ... , thrcof(lstep) has to
338 // be rescaled to prevent overflow
339
340 // mscale = mscale + 1
341 for (i = 1; i <= lstep; ++i) {
342 if (fabs(thrcof[i]) < srtiny) thrcof[i] = zero;
343 thrcof[i] /= srhuge;
344 }
345 sum1 /= hugeRealType;
346 sumfor /= hugeRealType;
347 x /= srhuge;
348
349 // As long as abs(c1) is decreasing, the recursion proceeds
350 // towards increasing 3j values and, hence, is numerically stable.
351 // Once an increase of abs(c1) is detected, the recursion
352 // direction is reversed.
353
354 L80:
355 if (c1old - fabs(c1) > 0.0) goto L30;
356
357 // Keep three 3j coefficients around mmatch for comparison later
358 // with backward recursion values.
359
360 L100:
361 // mmatch = m2 - 1
362 nstep2 = nfin - lstep + 3;
363 x1 = x;
364 x2 = thrcof[lstep - 1];
365 x3 = thrcof[lstep - 2];
366
367 // Starting backward recursion from m2max taking nstep2 steps, so
368 // that forwards and backwards recursion overlap at the three
369 // points m2 = mmatch+1, mmatch, mmatch-1.
370
371 nfinp1 = nfin + 1;
372 nfinp2 = nfin + 2;
373 nfinp3 = nfin + 3;
374 thrcof[nfin] = srtiny;
375 sum2 = tiny;
376
377 m2 = m2max + two;
378 lstep = 1;
379 L110:
380 ++lstep;
381 m2 -= one;
382 m3 = -m1 - m2;
383 oldfac = newfac;
384 a1s = (l2 - m2 + two) * (l2 + m2 - one) * (l3 + m3 + two) * (l3 - m3 - one);
385 newfac = sqrt(a1s);
386 dv = (l1 + l2 + l3 + one) * (l2 + l3 - l1) -
387 (l2 - m2 + one) * (l3 + m3 + one) - (l2 + m2 - one) * (l3 - m3 - one);
388 c1 = -dv / newfac;
389 if (lstep > 2) goto L120;
390
391 // if m2 = m2max + 1 the third term in the recursion equation
392 // vanishes
393
394 y = srtiny * c1;
395 thrcof[nfin - 1] = y;
396 if (lstep == nstep2) goto L200;
397 sumbac = sum2;
398 sum2 += y * y;
399 goto L110;
400
401 L120:
402 c2 = -oldfac / newfac;
403
404 // Recursion to the next 3j coefficient
405
406 y = c1 * thrcof[nfinp2 - lstep] + c2 * thrcof[nfinp3 - lstep];
407
408 if (lstep == nstep2) goto L200;
409
410 thrcof[nfinp1 - lstep] = y;
411 sumbac = sum2;
412 sum2 += y * y;
413
414 // See if last 3j coefficient exceeds SRHUGE
415
416 if (fabs(y) < srhuge) goto L110;
417
418 // This is reached if last 3j coefficient larger than srhuge, so
419 // that the recursion series
420 // thrcof(nfin), ... , thrcof(nfin-lstep+1)
421 // has to be rescaled to prevent overflow.
422
423 // mscale = mscale + 1
424 for (i = 1; i <= lstep; ++i) {
425 index = nfin - i + 1;
426 if (fabs(thrcof[index]) < srtiny) thrcof[index] = zero;
427 thrcof[index] /= srhuge;
428 }
429 sum2 /= hugeRealType;
430 sumbac /= hugeRealType;
431
432 goto L110;
433
434 // The forward recursion 3j coefficients x1, x2, x3 are to be
435 // matched with the corresponding backward recursion values y1,
436 // y2, y3.
437
438 L200:
439 y3 = y;
440 y2 = thrcof[nfinp2 - lstep];
441 y1 = thrcof[nfinp3 - lstep];
442
443 // Determine now ratio such that yi = ratio * xi (i=1,2,3) holds
444 // with minimal error.
445
446 ratio = (x1 * y1 + x2 * y2 + x3 * y3) / (x1 * x1 + x2 * x2 + x3 * x3);
447 nlim = nfin - nstep2 + 1;
448
449 if (fabs(ratio) < one) goto L211;
450 for (n = 1; n <= nlim; ++n)
451 thrcof[n] = ratio * thrcof[n];
452 sumuni = ratio * ratio * sumfor + sumbac;
453 goto L230;
454
455 L211:
456 ++nlim;
457 ratio = one / ratio;
458 for (n = nlim; n <= nfin; ++n)
459 thrcof[n] = ratio * thrcof[n];
460 sumuni = sumfor + ratio * ratio * sumbac;
461 goto L230;
462
463 L220:
464 sumuni = sum1;
465
466 // Normalize 3j coefficients
467
468 L230:
469 cnorm = one / sqrt((l1 + l1 + one) * sumuni);
470
471 // Sign convention for last 3j coefficient determines overall
472 // phase
473
474 sign1 = sign(thrcof[nfin]);
475 sign2 = odd(int(fabs(l2 - l3 - m1) + eps)) ? -one : one;
476 if (sign1 * sign2 <= 0.0)
477 goto L235;
478 else
479 goto L236;
480
481 L235:
482 cnorm = -cnorm;
483
484 L236:
485 if (fabs(cnorm) < one) goto L250;
486
487 for (n = 1; n <= nfin; ++n)
488 thrcof[n] = cnorm * thrcof[n];
489 return;
490
491 L250:
492 thresh = tiny / fabs(cnorm);
493 for (n = 1; n <= nfin; ++n) {
494 if (fabs(thrcof[n]) < thresh) thrcof[n] = zero;
495 thrcof[n] = cnorm * thrcof[n];
496 }
497 }
498} // namespace MATPACK