source: CIVL/examples/omp/shtns/sht_legendre.c

main
Last change on this file was ea777aa, checked in by Alex Wilton <awilton@…>, 3 years ago

Moved examples, include, build_default.properties, common.xml, and README out from dev.civl.com into the root of the repo.

git-svn-id: svn://vsl.cis.udel.edu/civl/trunk@5704 fb995dde-84ed-4084-dfe6-e5aef3e2452c

  • Property mode set to 100644
File size: 21.6 KB
Line 
1/*
2 * Copyright (c) 2010-2015 Centre National de la Recherche Scientifique.
3 * written by Nathanael Schaeffer (CNRS, ISTerre, Grenoble, France).
4 *
5 * nathanael.schaeffer@ujf-grenoble.fr
6 *
7 * This software is governed by the CeCILL license under French law and
8 * abiding by the rules of distribution of free software. You can use,
9 * modify and/or redistribute the software under the terms of the CeCILL
10 * license as circulated by CEA, CNRS and INRIA at the following URL
11 * "http://www.cecill.info".
12 *
13 * The fact that you are presently reading this means that you have had
14 * knowledge of the CeCILL license and that you accept its terms.
15 *
16 */
17
18/** \internal \file sht_legendre.c
19 \brief Compute legendre polynomials and associated functions.
20
21 - Normalization of the associated functions for various spherical harmonics conventions are possible, with or without Condon-Shortley phase.
22 - When computing the derivatives (with respect to colatitude theta), there are no singularities.
23 - written by Nathanael Schaeffer / CNRS, with some ideas and code from the GSL 1.13 and Numerical Recipies.
24
25 The associated legendre functions are computed using the following recurrence formula :
26 \f[ Y_l^m(x) = a_l^m \, x \ Y_{l-1}^m(x) + b_l^m \ Y_{l-2}^m(x) \f]
27 with starting values :
28 \f[ Y_m^m(x) = a_m^m \ (1-x^2)^{m/2} \f]
29 and
30 \f[ Y_{m+1}^m(x) = a_{m+1}^m \ Y_m^m(x) \f]
31 where \f$ a_l^m \f$ and \f$ b_l^m \f$ are coefficients that depend on the normalization, and which are
32 precomputed once for all by \ref legendre_precomp.
33*/
34
35// index in alm array for given im.
36#define alm_im(shtns, im) (shtns->alm + (im)*(2*shtns->lmax - ((im)-1)*shtns->mres))
37
38#if SHT_VERBOSE > 1
39 #define LEG_RANGE_CHECK
40#endif
41
42#ifndef HAVE_LONG_DOUBLE_WIDER
43 #define long_double_caps 0
44 typedef double real;
45 #define SQRT sqrt
46 #define COS cos
47 #define SIN sin
48 #define FABS fabs
49 #define legendre_sphPlm_hp legendre_sphPlm
50 #define legendre_sphPlm_array_hp legendre_sphPlm_array
51 #define legendre_sphPlm_deriv_array_hp legendre_sphPlm_deriv_array
52#else
53 int long_double_caps = 0;
54 typedef long double real;
55 #define SQRT sqrtl
56 #define COS cosl
57 #define SIN sinl
58 #define FABS fabsl
59
60// scale factor applied for LMAX larger than SHT_L_RESCALE. Allows accurate transforms up to l=2700 with 64 bit double precision.
61#define SHT_LEG_SCALEF 1.1018032079253110206e-280
62#define SHT_L_RESCALE 1536
63
64// returns 1 for large exponent only => useless.
65// returns 2 for extended precision only => ok to improve gauss points.
66// returns 3 for extended precision and large exponent => ok to improve legendre recurrence.
67static int test_long_double()
68{
69 volatile real tt; // volatile avoids optimizations.
70 int p = 0;
71
72 tt = 1.0e-1000L; tt *= tt;
73 if (tt > 0) p |= 1; // bit 0 set for large exponent
74 tt = 1.0; tt += 1.e-18;
75 if (tt > 1.0) p |= 2; // bit 1 set for extended precision
76 long_double_caps = p;
77 return p;
78}
79
80/// \internal high precision version of \ref a_sint_pow_n
81static real a_sint_pow_n_hp(real val, real cost, long int n)
82{
83 real s2 = (1.-cost)*(1.+cost); // sin(t)^2 = 1 - cos(t)^2
84 long int k = n >> 7;
85 if (sizeof(s2) > 8) k = 0; // enough accuracy, we do not bother.
86
87#ifdef LEG_RANGE_CHECK
88 if (s2 < 0) shtns_runerr("sin(t)^2 < 0 !!!");
89#endif
90
91 if (n&1) val *= SQRT(s2); // = sin(t)
92 do {
93 if (n&2) val *= s2;
94 n >>= 1;
95 s2 *= s2;
96 } while(n > k);
97 n >>= 1;
98 while(n > 0) { // take care of very large power n
99 n--;
100 val *= s2;
101 }
102 return val; // = sint(t)^n
103}
104#endif
105
106
107/// \internal computes val.sin(t)^n from cos(t). ie returns val.(1-x^2)^(n/2), with x = cos(t)
108/// assumes: -1 <= cost <= 1, n>=0, and nval<=0 is the extended exponent associated to val.
109/// updates nval, and returns val such as the result is val.SHT_SCALE_FACTOR^(nval)
110static double a_sint_pow_n_ext(double val, double cost, int n, int *nval)
111{
112 double s2 = (1.-cost)*(1.+cost); // sin(t)^2 = 1 - cos(t)^2 >= 0
113 double val0 = val; // store sign
114 int ns2 = 0;
115 int nv = *nval;
116
117#ifdef LEG_RANGE_CHECK
118 if (s2 < 0) shtns_runerr("sin(t)^2 < 0 !!!");
119#endif
120
121 val = fabs(val); // val >= 0
122 if (n&1) val *= sqrt(s2); // = sin(t)
123 while (n >>= 1) {
124 if (n&1) {
125 if (val < 1.0/SHT_SCALE_FACTOR) { // val >= 0
126 nv--; val *= SHT_SCALE_FACTOR;
127 }
128 val *= s2; nv += ns2; // 1/S^2 < val < 1
129 }
130 s2 *= s2; ns2 += ns2;
131 if (s2 < 1.0/SHT_SCALE_FACTOR) { // s2 >= 0
132 ns2--; s2 *= SHT_SCALE_FACTOR; // 1/S < s2 < 1
133 }
134 }
135 while ((nv < 0) && (val > 1.0/SHT_SCALE_FACTOR)) { // try to minimize |nv|
136 ++nv; val *= 1.0/SHT_SCALE_FACTOR;
137 }
138 if (val0 < 0) val *= -1.0; // restore sign.
139 *nval = nv;
140 return val; // 1/S^2 < val < 1
141}
142
143
144/// \internal Returns the value of a legendre polynomial of degree l and order im*MRES, noramalized for spherical harmonics, using recurrence.
145/// Requires a previous call to \ref legendre_precomp().
146/// Output compatible with the GSL function gsl_sf_legendre_sphPlm(l, m, x)
147static double legendre_sphPlm(shtns_cfg shtns, const int l, const int im, const double x)
148{
149 double *al;
150 int m, ny;
151 double ymm, ymmp1;
152
153 m = im*MRES;
154#ifdef LEG_RANGE_CHECK
155 if ( (l>LMAX) || (l<m) || (im>MMAX) ) shtns_runerr("argument out of range in legendre_sphPlm");
156#endif
157
158 ny = 0;
159 al = alm_im(shtns, im);
160 ymm = al[0];
161 if (m>0) ymm = a_sint_pow_n_ext(ymm, x, m, &ny); // ny <= 0
162
163 ymmp1 = ymm; // l=m
164 if (l == m) goto done;
165
166 ymmp1 = al[1] * (x*ymmp1); // l=m+1
167 if (l == m+1) goto done;
168
169 m+=2; al+=2;
170 while ((ny < 0) && (m < l)) { // take care of scaled values
171 ymm = al[1]*(x*ymmp1) + al[0]*ymm;
172 ymmp1 = al[3]*(x*ymm) + al[2]*ymmp1;
173 m+=2; al+=4;
174 if (fabs(ymm) > 1.0/SHT_SCALE_FACTOR) { // rescale when value is significant
175 ++ny; ymm *= 1.0/SHT_SCALE_FACTOR; ymmp1 *= 1.0/SHT_SCALE_FACTOR;
176 }
177 }
178 while (m < l) { // values are unscaled.
179 ymm = al[1]*(x*ymmp1) + al[0]*ymm;
180 ymmp1 = al[3]*(x*ymm) + al[2]*ymmp1;
181 m+=2; al+=4;
182 }
183 if (m == l) {
184 ymmp1 = al[1]*(x*ymmp1) + al[0]*ymm;
185 }
186done:
187 if (ny < 0) {
188 if (ny+3 < 0) return 0.0;
189 do { ymmp1 *= 1.0/SHT_SCALE_FACTOR; } while (++ny < 0); // return unscaled values.
190 }
191 return ymmp1;
192}
193
194#if HAVE_LONG_DOUBLE_WIDER
195static double legendre_sphPlm_hp(shtns_cfg shtns, const int l, const int im, double x)
196{
197 double *al;
198 int i,m;
199 real ymm, ymmp1;
200
201 if (long_double_caps < 3) legendre_sphPlm(shtns, l, im, x); // not worth it.
202
203 m = im*MRES;
204#ifdef LEG_RANGE_CHECK
205 if ( (l>LMAX) || (l<m) || (im>MMAX) ) shtns_runerr("argument out of range in legendre_sphPlm");
206#endif
207
208 al = alm_im(shtns, im);
209 ymm = al[0]; // l=m
210 if (m>0) ymm *= SHT_LEG_SCALEF;
211 ymmp1 = ymm;
212 if (l==m) goto done;
213
214 ymmp1 = al[1] * (x*ymmp1); // l=m+1
215 al+=2;
216 if (l == m+1) goto done;
217
218 for (i=m+2; i<l; i+=2) {
219 ymm = al[1]*(x*ymmp1) + al[0]*ymm;
220 ymmp1 = al[3]*(x*ymm) + al[2]*ymmp1;
221 al+=4;
222 }
223 if (i==l) {
224 ymmp1 = al[1]*(x*ymmp1) + al[0]*ymm;
225 }
226done:
227 if (m>0) ymmp1 *= a_sint_pow_n_hp(1.0/SHT_LEG_SCALEF, x, m);
228 return ((double) ymmp1);
229}
230#endif
231
232
233/// \internal Compute values of legendre polynomials noramalized for spherical harmonics,
234/// for a range of l=m..lmax, at given m and x, using recurrence.
235/// Requires a previous call to \ref legendre_precomp().
236/// Output compatible with the GSL function gsl_sf_legendre_sphPlm_array(lmax, m, x, yl)
237/// \param lmax maximum degree computed, \param im = m/MRES with m the SH order, \param x argument, x=cos(theta).
238/// \param[out] yl is a double array of size (lmax-m+1) filled with the values.
239static void legendre_sphPlm_array(shtns_cfg shtns, const int lmax, const int im, const double x, double *yl)
240{
241 double *al;
242 int l, m, ny;
243 double ymm, ymmp1;
244
245 m = im*MRES;
246#ifdef LEG_RANGE_CHECK
247 if ( (lmax>LMAX) || (lmax<m) || (im>MMAX) ) shtns_runerr("argument out of range in legendre_sphPlm");
248#endif
249
250 al = alm_im(shtns, im);
251 yl -= m; // shift pointer
252 for (l=m; l<=lmax; ++l) yl[l] = 0.0; // zero out array.
253
254 ny = 0;
255 ymm = al[0];
256 if (m>0) ymm = a_sint_pow_n_ext(ymm, x, m, &ny); // l=m, ny <= 0
257 if (ny==0) yl[m] = ymm;
258 if (lmax==m) return;
259
260 ymmp1 = ymm * al[1] * x; // l=m+1
261 if (ny==0) yl[m+1] = ymmp1;
262 if (lmax==m+1) return;
263
264 l=m+2; al+=2;
265 while ((ny < 0) && (l < lmax)) { // values are negligible => discard.
266 ymm = al[1]*(x*ymmp1) + al[0]*ymm;
267 ymmp1 = al[3]*(x*ymm) + al[2]*ymmp1;
268 l+=2; al+=4;
269 if (fabs(ymm) > 1.0/SHT_SCALE_FACTOR) { // rescale when value is significant
270 ++ny; ymm *= 1.0/SHT_SCALE_FACTOR; ymmp1 *= 1.0/SHT_SCALE_FACTOR;
271 }
272 }
273 while (l < lmax) { // values are unscaled => store
274 ymm = al[1]*(x*ymmp1) + al[0]*ymm;
275 ymmp1 = al[3]*(x*ymm) + al[2]*ymmp1;
276 yl[l] = ymm; yl[l+1] = ymmp1;
277 l+=2; al+=4;
278 }
279 if ((l == lmax) && (ny == 0)) {
280 yl[l] = al[1]*(x*ymmp1) + al[0]*ymm;
281 }
282}
283
284#if HAVE_LONG_DOUBLE_WIDER
285/// \internal high precision version of \ref legendre_sphPlm_array
286static void legendre_sphPlm_array_hp(shtns_cfg shtns, const int lmax, const int im, const double cost, double *yl)
287{
288 double *al;
289 long int l,m;
290 int rescale = 0; // flag for rescale.
291 real ymm, ymmp1, x;
292
293 if (long_double_caps < 3) { // not worth it.
294 legendre_sphPlm_array(shtns, lmax, im, cost, yl); return;
295 }
296
297 m = im*MRES;
298#ifdef LEG_RANGE_CHECK
299 if ((lmax > LMAX)||(lmax < m)||(im>MMAX)) shtns_runerr("argument out of range in legendre_sphPlm_array");
300#endif
301
302 x = cost;
303 al = alm_im(shtns, im);
304 yl -= m; // shift pointer
305 ymm = al[0]; // l=m
306 if (m>0) {
307 if ((lmax <= SHT_L_RESCALE) || (sizeof(ymm) > 8)) {
308 ymm = a_sint_pow_n_hp(ymm, x, m);
309 } else {
310 rescale = 1;
311 ymm *= SHT_LEG_SCALEF;
312 }
313 }
314 yl[m] = ymm;
315 if (lmax==m) goto done; // done.
316
317 ymmp1 = ymm * al[1] * x; // l=m+1
318 yl[m+1] = ymmp1;
319 al+=2;
320 if (lmax==m+1) goto done; // done.
321
322 for (l=m+2; l<lmax; l+=2) {
323 ymm = al[1]*(x*ymmp1) + al[0]*ymm;
324 ymmp1 = al[3]*(x*ymm) + al[2]*ymmp1;
325 yl[l] = ymm;
326 yl[l+1] = ymmp1;
327 al+=4;
328 }
329 if (l==lmax) {
330 yl[l] = al[1]*(x*ymmp1) + al[0]*ymm;
331 }
332done:
333 if (rescale != 0) {
334 ymm = a_sint_pow_n_hp(1.0/SHT_LEG_SCALEF, x, m);
335 for (l=m; l<=lmax; ++l) { // rescale.
336 yl[l] *= ymm;
337 }
338 }
339 return;
340}
341#endif
342
343/// \internal Compute values of a legendre polynomial normalized for spherical harmonics derivatives, for a range of l=m..lmax, using recurrence.
344/// Requires a previous call to \ref legendre_precomp(). Output is not directly compatible with GSL :
345/// - if m=0 : returns ylm(x) and d(ylm)/d(theta) = -sin(theta)*d(ylm)/dx
346/// - if m>0 : returns ylm(x)/sin(theta) and d(ylm)/d(theta).
347/// This way, there are no singularities, everything is well defined for x=[-1,1], for any m.
348/// \param lmax maximum degree computed, \param im = m/MRES with m the SH order, \param x argument, x=cos(theta).
349/// \param sint = sqrt(1-x^2) to avoid recomputation of sqrt.
350/// \param[out] yl is a double array of size (lmax-m+1) filled with the values (divided by sin(theta) if m>0)
351/// \param[out] dyl is a double array of size (lmax-m+1) filled with the theta-derivatives.
352static void legendre_sphPlm_deriv_array(shtns_cfg shtns, const int lmax, const int im, const double x, const double sint, double *yl, double *dyl)
353{
354 double *al;
355 int l,m, ny;
356 double st, y0, y1, dy0, dy1;
357
358 m = im*MRES;
359#ifdef LEG_RANGE_CHECK
360 if ((lmax > LMAX)||(lmax < m)||(im>MMAX)) shtns_runerr("argument out of range in legendre_sphPlm_deriv_array");
361#endif
362
363 al = alm_im(shtns, im);
364 yl -= m; dyl -= m; // shift pointers
365 for (l=m; l<=lmax; ++l) {
366 yl[l] = 0.0; dyl[l] = 0.0; // zero out arrays.
367 }
368
369 ny = 0;
370 st = sint;
371 y0 = al[0];
372 dy0 = 0.0;
373 if (m>0) {
374 y0 = a_sint_pow_n_ext(y0, x, m-1, &ny);
375 dy0 = x*m*y0;
376 st *= st; // st = sin(theta)^2 is used in the recurrence for m>0
377 }
378 if (ny==0) {
379 yl[m] = y0; dyl[m] = dy0; // l=m
380 }
381 if (lmax==m) return; // done.
382
383 y1 = al[1] * (x * y0);
384 dy1 = al[1]*( x*dy0 - st*y0 );
385 if (ny == 0) {
386 yl[m+1] = y1; dyl[m+1] = dy1; // l=m+1
387 }
388 if (lmax==m+1) return; // done.
389
390 l=m+2; al+=2;
391 while ((ny < 0) && (l < lmax)) { // values are negligible => discard.
392 y0 = al[1]*(x*y1) + al[0]*y0;
393 dy0 = al[1]*(x*dy1 - y1*st) + al[0]*dy0;
394 y1 = al[3]*(x*y0) + al[2]*y1;
395 dy1 = al[3]*(x*dy0 - y0*st) + al[2]*dy1;
396 l+=2; al+=4;
397 if (fabs(y0) > 1.0/SHT_SCALE_FACTOR) { // rescale when value is significant
398 ++ny; y0 *= 1.0/SHT_SCALE_FACTOR; dy0 *= 1.0/SHT_SCALE_FACTOR;
399 y1 *= 1.0/SHT_SCALE_FACTOR; dy1 *= 1.0/SHT_SCALE_FACTOR;
400 }
401 }
402 while (l < lmax) { // values are unscaled => store.
403 y0 = al[1]*(x*y1) + al[0]*y0;
404 dy0 = al[1]*(x*dy1 - y1*st) + al[0]*dy0;
405 y1 = al[3]*(x*y0) + al[2]*y1;
406 dy1 = al[3]*(x*dy0 - y0*st) + al[2]*dy1;
407 yl[l] = y0; dyl[l] = dy0;
408 yl[l+1] = y1; dyl[l+1] = dy1;
409 l+=2; al+=4;
410 }
411 if ((l==lmax) && (ny == 0)) {
412 yl[l] = al[1]*(x*y1) + al[0]*y0;
413 dyl[l] = al[1]*(x*dy1 - y1*st) + al[0]*dy0;
414 }
415}
416
417#if HAVE_LONG_DOUBLE_WIDER
418/// \internal high precision version of \ref legendre_sphPlm_deriv_array
419static void legendre_sphPlm_deriv_array_hp(shtns_cfg shtns, const int lmax, const int im, const double cost, const double sint, double *yl, double *dyl)
420{
421 double *al;
422 long int l,m;
423 int rescale = 0; // flag for rescale.
424 real x, st, y0, y1, dy0, dy1;
425
426 if (long_double_caps < 3) { // not worth it.
427 legendre_sphPlm_deriv_array(shtns, lmax, im, cost, sint, yl, dyl); return;
428 }
429
430 x = cost;
431 m = im*MRES;
432#ifdef LEG_RANGE_CHECK
433 if ((lmax > LMAX)||(lmax < m)||(im>MMAX)) shtns_runerr("argument out of range in legendre_sphPlm_deriv_array");
434#endif
435 al = alm_im(shtns, im);
436 yl -= m; dyl -= m; // shift pointers
437
438 st = sint;
439 y0 = al[0];
440 dy0 = 0.0;
441 if (m>0) { // m > 0
442 l = m-1;
443 if ((lmax <= SHT_L_RESCALE) || (sizeof(y0) > 8)) {
444 if (l&1) {
445 y0 = a_sint_pow_n_hp(y0, x, l-1) * sint; // avoid computation of sqrt
446 } else y0 = a_sint_pow_n_hp(y0, x, l);
447 } else {
448 rescale = 1;
449 y0 *= SHT_LEG_SCALEF;
450 }
451 dy0 = x*m*y0;
452 st *= st; // st = sin(theta)^2 is used in the recurrence for m>0
453 }
454 yl[m] = y0; // l=m
455 dyl[m] = dy0;
456 if (lmax==m) goto done; // done.
457
458 y1 = al[1] * (x * y0);
459 dy1 = al[1]*( x*dy0 - st*y0 );
460 yl[m+1] = y1; // l=m+1
461 dyl[m+1] = dy1;
462 if (lmax==m+1) goto done; // done.
463 al+=2;
464
465 for (l=m+2; l<lmax; l+=2) {
466 y0 = al[1]*(x*y1) + al[0]*y0;
467 dy0 = al[1]*(x*dy1 - y1*st) + al[0]*dy0;
468 yl[l] = y0; dyl[l] = dy0;
469 y1 = al[3]*(x*y0) + al[2]*y1;
470 dy1 = al[3]*(x*dy0 - y0*st) + al[2]*dy1;
471 yl[l+1] = y1; dyl[l+1] = dy1;
472 al+=4;
473 }
474 if (l==lmax) {
475 yl[l] = al[1]*(x*y1) + al[0]*y0;
476 dyl[l] = al[1]*(x*dy1 - y1*st) + al[0]*dy0;
477 }
478done:
479 if (rescale != 0) {
480 l = m-1; // compute sin(theta)^(m-1)
481 if (l&1) {
482 y0 = a_sint_pow_n_hp(1.0/SHT_LEG_SCALEF, x, l-1) * sint; // avoid computation of sqrt
483 } else y0 = a_sint_pow_n_hp(1.0/SHT_LEG_SCALEF, x, l);
484 for (l=m; l<=lmax; ++l) {
485 yl[l] *= y0; dyl[l] *= y0; // rescale
486 }
487 }
488 return;
489}
490#endif
491
492/// same as legendre_sphPlm_deriv_array for x=0 and sint=1 (equator)
493static void legendre_sphPlm_deriv_array_equ(shtns_cfg shtns, const int lmax, const int im, double *yl, double *dyl)
494{
495 double *al;
496 int l,m;
497 double y0, dy1;
498
499 m = im*MRES;
500#ifdef LEG_RANGE_CHECK
501 if ((lmax > LMAX)||(lmax < m)||(im>MMAX)) shtns_runerr("argument out of range in legendre_sphPlm_deriv_array");
502#endif
503
504 al = alm_im(shtns, im);
505 yl -= m; dyl -= m; // shift pointers
506
507 y0 = al[0];
508 yl[m] = y0; dyl[m] = 0.0; // l=m
509 if (lmax==m) return; // done.
510
511 dy1 = -al[1]*y0;
512 yl[m+1] = 0.0; dyl[m+1] = dy1; // l=m+1
513 if (lmax==m+1) return; // done.
514
515 l=m+2; al+=2;
516 while (l < lmax) {
517 y0 = al[0]*y0;
518 dy1 = al[2]*dy1 - al[3]*y0;
519 yl[l] = y0; dyl[l] = 0.0;
520 yl[l+1] = 0.0; dyl[l+1] = dy1;
521 l+=2; al+=4;
522 }
523 if (l==lmax) {
524 yl[l] = al[0]*y0;
525 dyl[l] = 0.0;
526 }
527}
528
529
530/// \internal Precompute constants for the recursive generation of Legendre associated functions, with given normalization.
531/// this function is called by \ref shtns_set_size, and assumes up-to-date values in \ref shtns.
532/// For the same conventions as GSL, use \c legendre_precomp(sht_orthonormal,1);
533/// \param[in] norm : normalization of the associated legendre functions (\ref shtns_norm).
534/// \param[in] with_cs_phase : Condon-Shortley phase (-1)^m is included (1) or not (0)
535/// \param[in] mpos_renorm : Optional renormalization for m>0.
536/// 1.0 (no renormalization) is the "complex" convention, while 0.5 leads to the "real" convention (with FFTW).
537static void legendre_precomp(shtns_cfg shtns, enum shtns_norm norm, int with_cs_phase, double mpos_renorm)
538{
539 double *alm, *blm;
540 long int im, m, l, lm;
541 real t1, t2;
542
543#if HAVE_LONG_DOUBLE_WIDER
544 test_long_double();
545#endif
546#if SHT_VERBOSE > 1
547 if (verbose) {
548 printf(" > Condon-Shortley phase = %d, normalization = %d\n", with_cs_phase, norm);
549 if (long_double_caps == 3) printf(" > long double has extended precision and large exponent\n");
550 }
551#endif
552
553 if (with_cs_phase != 0) with_cs_phase = 1; // force to 1 if !=0
554
555 alm = (double *) malloc( (2*NLM)*sizeof(double) );
556 blm = alm;
557 if ((norm == sht_schmidt) || (mpos_renorm != 1.0)) {
558 blm = (double *) malloc( (2*NLM)*sizeof(double) );
559 }
560 if ((alm==0) || (blm==0)) shtns_runerr("not enough memory.");
561 shtns->alm = alm; shtns->blm = blm;
562
563/// - Compute and store the prefactor (independant of x) of the starting value for the recurrence :
564/// \f[ Y_m^m(x) = Y_0^0 \ \sqrt{ \prod_{k=1}^{m} \frac{2k+1}{2k} } \ \ (-1)^m \ (1-x^2)^{m/2} \f]
565 if ((norm == sht_fourpi)||(norm == sht_schmidt)) {
566 t1 = 1.0;
567 alm[0] = t1; /// \f$ Y_0^0 = 1 \f$ for Schmidt or 4pi-normalized
568 } else {
569 t1 = 0.25L/M_PIl;
570 alm[0] = SQRT(t1); /// \f$ Y_0^0 = 1/\sqrt{4\pi} \f$ for orthonormal
571 }
572 t1 *= mpos_renorm; // renormalization for m>0
573 for (im=1, m=0; im<=MMAX; ++im) {
574 while(m<im*MRES) {
575 ++m;
576 t1 *= ((real)m + 0.5)/m; // t1 *= (m+0.5)/m;
577 }
578 t2 = SQRT(t1);
579 if ( m & with_cs_phase ) t2 = -t2; /// optional \f$ (-1)^m \f$ Condon-Shortley phase.
580 alm_im(shtns, im)[0] = t2;
581 }
582
583/// - Precompute the factors alm and blm of the recurrence relation :
584 #pragma omp parallel for private(im,m,l,lm, t1, t2) schedule(dynamic)
585 for (im=0; im<=MMAX; ++im) {
586 m = im*MRES;
587 lm = im*(2*LMAX - (im-1)*MRES);
588 if (norm == sht_schmidt) { /// <b> For Schmidt semi-normalized </b>
589 t2 = SQRT(2*m+1);
590 alm[lm] /= t2; /// starting value divided by \f$ \sqrt{2m+1} \f$
591 alm[lm+1] = t2; // l=m+1
592 lm+=2;
593 for (l=m+2; l<=LMAX; ++l) {
594 t1 = SQRT((l+m)*(l-m));
595 alm[lm+1] = (2*l-1)/t1; /// \f[ a_l^m = \frac{2l-1}{\sqrt{(l+m)(l-m)}} \f]
596 alm[lm] = - t2/t1; /// \f[ b_l^m = -\sqrt{\frac{(l-1+m)(l-1-m)}{(l+m)(l-m)}} \f]
597 t2 = t1; lm+=2;
598 }
599 } else { /// <b> For orthonormal or 4pi-normalized </b>
600 t2 = 2*m+1;
601 // starting value unchanged.
602 alm[lm+1] = SQRT(2*m+3); // l=m+1
603 lm+=2;
604 for (l=m+2; l<=LMAX; ++l) {
605 t1 = (l+m)*(l-m);
606 alm[lm+1] = SQRT(((2*l+1)*(2*l-1))/t1); /// \f[ a_l^m = \sqrt{\frac{(2l+1)(2l-1)}{(l+m)(l-m)}} \f]
607 alm[lm] = - SQRT(((2*l+1)*t2)/((2*l-3)*t1)); /// \f[ b_l^m = -\sqrt{\frac{2l+1}{2l-3}\,\frac{(l-1+m)(l-1-m)}{(l+m)(l-m)}} \f]
608 t2 = t1; lm+=2;
609 }
610 }
611/// - Compute analysis recurrence coefficients if necessary
612 if ((norm == sht_schmidt) || (mpos_renorm != 1.0)) {
613 lm = im*(2*LMAX - (im-1)*MRES);
614 t1 = 1.0;
615 t2 = alm[lm+1];
616 if (norm == sht_schmidt) {
617 t1 = 2*m+1;
618 t2 *= (2*m+3)/t1;
619 }
620 if (m>0) t1 /= mpos_renorm;
621 blm[lm] = alm[lm]*t1;
622 blm[lm+1] = t2;
623 lm+=2;
624 for (l=m+2; l<=LMAX; ++l) {
625 t1 = alm[lm];
626 t2 = alm[lm+1];
627 if (norm == sht_schmidt) {
628 double t3 = 2*l+1;
629 t1 *= t3/(2*l-3);
630 t2 *= t3/(2*l-1);
631 }
632 blm[lm] = t1;
633 blm[lm+1] = t2;
634 lm+=2;
635 }
636 }
637 }
638}
639
640/// \internal returns the value of the Legendre Polynomial of degree l.
641/// l is arbitrary, a direct recurrence relation is used, and a previous call to legendre_precomp() is not required.
642static double legendre_Pl(const int l, double x)
643{
644 long int i;
645 real p, p0, p1;
646
647 if ((l==0)||(x==1.0)) return ( 1. );
648 if (x==-1.0) return ( (l&1) ? -1. : 1. );
649
650 p0 = 1.0; /// \f$ P_0 = 1 \f$
651 p1 = x; /// \f$ P_1 = x \f$
652 for (i=2; i<=l; ++i) { /// recurrence : \f[ l P_l = (2l-1) x P_{l-1} - (l-1) P_{l-2} \f] (works ok up to l=100000)
653 p = ((x*p1)*(2*i-1) - (i-1)*p0)/i;
654 p0 = p1;
655 p1 = p;
656 }
657 return ((double) p1);
658}
659
660
661/// \internal Generates the abscissa and weights for a Gauss-Legendre quadrature.
662/// Newton method from initial Guess to find the zeros of the Legendre Polynome
663/// \param x = abscissa, \param w = weights, \param n points.
664/// \note Reference: Numerical Recipes, Cornell press.
665static void gauss_nodes(real *x, real *w, int n)
666{
667 long int i,l,m, k;
668 real z, z1, p1, p2, p3, pp;
669 real eps;
670
671 eps = 2.3e-16; // desired precision, minimum = 2.2204e-16 (double)
672 if ((sizeof(eps) > 8) && (long_double_caps > 1)) eps = 1.1e-19; // desired precision, minimum = 1.0842e-19 (long double i387)
673
674 m = (n+1)/2;
675 for (i=1;i<=m;++i) {
676 k=10; // maximum Newton iteration count to prevent infinite loop.
677 p1 = M_PIl; p2 = 2*n;
678 z = (1.0 - (n-1)/(p2*p2*p2)) * COS((p1*(4*i-1))/(4*n+2)); // initial guess
679 do {
680 p1 = z; // P_1
681 p2 = 1.0; // P_0
682 for(l=2;l<=n;++l) { // recurrence : l P_l = (2l-1) z P_{l-1} - (l-1) P_{l-2} (works ok up to l=100000)
683 p3 = p2;
684 p2 = p1;
685 p1 = ((2*l-1)*z*p2 - (l-1)*p3)/l; // The Legendre polynomial...
686 }
687 pp = ((1.-z)*(1.+z))/(n*(p2-z*p1)); // ... and its inverse derivative.
688 z1 = z;
689 z -= p1*pp; // Newton's method
690 } while (( FABS(z-z1) > (z1+z)*0.5*eps ) && (--k > 0));
691 x[i-1] = z; // Build up the abscissas.
692 w[i-1] = 2.0*pp*pp/((1.-z)*(1.+z)); // Build up the weights.
693 x[n-i] = -z;
694 w[n-i] = w[i-1];
695 }
696 if (n&1) x[n/2] = 0.0; // exactly zero.
697
698#if SHT_VERBOSE > 1
699// test integral to compute :
700 if (verbose) {
701 z = 0;
702 for (i=0;i<m;++i) {
703 z += w[i]*x[i]*x[i];
704 }
705 #ifndef HAVE_LONG_DOUBLE_WIDER
706 printf(" Gauss quadrature for 3/2.x^2 = %g (should be 1.0) error = %g\n",z*3.,z*3.-1.0);
707 #else
708 printf(" Gauss quadrature for 3/2.x^2 = %Lg (should be 1.0) error = %Lg\n",z*3.,z*3.-1.0);
709 #endif
710 }
711#endif
712
713// as we started with initial guesses, we should check if the gauss points are actually unique.
714 for (i=m-1; i>0; i--) {
715 if (((double) x[i]) == ((double) x[i-1])) shtns_runerr("bad gauss points");
716 }
717}
Note: See TracBrowser for help on using the repository browser.