2 #include "qpms_specfunc.h"
7 #include <gsl/gsl_sf_bessel.h>
9 #include "qpms_error.h"
14 #define M_LN2 0.69314718055994530942 /* log_e 2 */
17 static inline complex double ipow(int x
) {
22 // Inspired by scipy/special/_spherical_bessel.pxd
23 static inline complex double spherical_jn(qpms_l_t l
, complex double z
) {
24 if (isnan(creal(z
)) || isnan(cimag(z
))) return NAN
+I
*NAN
;
26 if (fpclassify(creal(z
)) == FP_INFINITE
)
30 return INFINITY
+ I
* INFINITY
;
34 return csqrt(M_PI_2
/z
) * zbesj(l
+ .5, z
);
37 static inline complex double spherical_yn(qpms_l_t l
, complex double z
) {
38 if (isnan(creal(z
)) || isnan(cimag(z
))) return NAN
+I
*NAN
;
40 if (fpclassify(creal(z
)) == FP_INFINITE
)
44 return INFINITY
+ I
* INFINITY
;
47 return csqrt(M_PI_2
/z
) * zbesy(l
+ .5, z
);
51 // There is a big issue with gsl's precision of spherical bessel function; these have to be implemented differently
52 qpms_errno_t
qpms_sph_bessel_realx_fill(qpms_bessel_t typ
, qpms_l_t lmax
, double x
, complex double *result_array
) {
54 double tmparr
[lmax
+1];
55 if (typ
!= QPMS_BESSEL_REGULAR
&& x
== 0) {
56 for (int l
= 0; l
<= lmax
; ++l
) result_array
[l
] = NAN
+ I
*NAN
;
57 QPMS_WARN("Evaluating singular Bessel functions at zero!");
58 return QPMS_SUCCESS
; // Really?
61 case QPMS_BESSEL_REGULAR
:
62 retval
= gsl_sf_bessel_jl_steed_array(lmax
, x
, tmparr
);
63 for (int l
= 0; l
<= lmax
; ++l
) result_array
[l
] = tmparr
[l
];
66 case QPMS_BESSEL_SINGULAR
: //FIXME: is this precise enough? Would it be better to do it one-by-one?
67 retval
= gsl_sf_bessel_yl_array(lmax
,x
,tmparr
);
68 for (int l
= 0; l
<= lmax
; ++l
) result_array
[l
] = tmparr
[l
];
71 case QPMS_HANKEL_PLUS
:
72 case QPMS_HANKEL_MINUS
:
73 retval
= gsl_sf_bessel_jl_steed_array(lmax
, x
, tmparr
);
74 for (int l
= 0; l
<= lmax
; ++l
) result_array
[l
] = tmparr
[l
];
75 if(retval
) return retval
;
76 retval
= gsl_sf_bessel_yl_array(lmax
, x
, tmparr
);
77 if (typ
==QPMS_HANKEL_PLUS
)
78 for (int l
= 0; l
<= lmax
; ++l
) result_array
[l
] += I
* tmparr
[l
];
80 for (int l
= 0; l
<= lmax
; ++l
) result_array
[l
] +=-I
* tmparr
[l
];
91 qpms_errno_t
qpms_sph_bessel_fill(qpms_bessel_t typ
, qpms_l_t lmax
, complex double x
, complex double *res
) {
93 creal(x
) < 10000) // For large arguments (around 30000 or more), gsl_sf_bessel_jl_steed_array fails
94 return qpms_sph_bessel_realx_fill(typ
, lmax
, creal(x
), res
);
95 else if (isnan(creal(x
)) || isnan(cimag(x
)))
96 for(qpms_l_t l
= 0; l
<= lmax
; ++l
) res
[l
] = NAN
+ I
*NAN
;
97 else if (lmax
< 0) QPMS_WTF
;
98 else if (fpclassify(creal(x
)) == FP_INFINITE
)
99 for(qpms_l_t l
= 0; l
<= lmax
; ++l
) res
[l
] = INFINITY
+ I
* INFINITY
;
102 int retry_counter
= 0;
103 const double zr
= creal(x
), zi
= cimag(x
), fnu
= 0.5;
104 const int n
= lmax
+ 1, kode
= 1 /* No exponential scaling */;
105 double cyr
[n
], cyi
[n
];
107 unsigned int kindchar
; // Only for error output
108 const complex double prefac
= csqrt(M_PI_2
/x
);
110 case QPMS_BESSEL_REGULAR
:
112 ierr
= camos_zbesj(zr
, zi
, fnu
, kode
, n
, cyr
, cyi
, &nz
);
114 case QPMS_BESSEL_SINGULAR
:
117 double cwrkr
[lmax
+ 1], cwrki
[lmax
+ 1];
118 ierr
= camos_zbesy(zr
, zi
, fnu
, kode
, n
, cyr
, cyi
, &nz
, cwrkr
, cwrki
);
121 case QPMS_HANKEL_PLUS
:
122 case QPMS_HANKEL_MINUS
:
125 const int m
= (typ
== QPMS_HANKEL_PLUS
) ? 1 : 2;
126 ierr
= camos_zbesh(zr
, zi
, fnu
, kode
, m
, n
, cyr
, cyi
, &nz
);
132 // TODO check for underflows? (nz != 0)
133 if (ierr
== 0 || ierr
== 3) {
134 for (qpms_l_t l
= 0; l
<= lmax
; ++l
)
135 res
[l
] = prefac
* (cyr
[l
] + I
* cyi
[l
]);
137 if (creal(x
) >= 10000) {
138 QPMS_WARN_ONCE("Due to large argument, "
139 "Amos's zbes%c computation done but losses of significance "
140 "by argument reduction produce less than half of machine accuracy. "
141 "This warning is shown only once.",
144 QPMS_WARN("Amos's zbes%c computation done but losses of significance "
145 "by argument reduction produce less than half of machine accuracy.",
148 return QPMS_SUCCESS
; //TODO maybe something else if ierr == 3
151 if (retry_counter
< 5) {
152 QPMS_WARN("Amos's zbes%c failed with ierr == %d (lMax = %d, x = %+.16g%+.16gi). Retrying.\n",
153 kindchar
, (int) ierr
, lmax
, creal(x
), cimag(x
));
156 } else QPMS_PR_ERROR("Amos's zbes%c failed with ierr == %d (lMax = %d, x = %+.16g%+.16gi).",
157 kindchar
, (int) ierr
, lmax
, creal(x
), cimag(x
));
163 static inline ptrdiff_t akn_index(qpms_l_t n
, qpms_l_t k
) {
165 return ((ptrdiff_t) n
+ 1) * n
/ 2 + k
;
167 static inline ptrdiff_t bkn_index(qpms_l_t n
, qpms_l_t k
) {
169 return ((ptrdiff_t) n
+ 2) * (n
+ 1) / 2 - 1 + k
;
172 static inline qpms_errno_t
qpms_sbessel_calculator_ensure_lMax(qpms_sbessel_calculator_t
*c
, qpms_l_t lMax
) {
176 if ( NULL
== (c
->akn
= realloc(c
->akn
, sizeof(double) * akn_index(lMax
+ 2, 0))))
178 //if ( NULL == (c->bkn = realloc(c->bkn, sizeof(complex double) * bkn_index(lMax + 1, 0))))
180 for(qpms_l_t n
= c
->lMax
+1; n
<= lMax
+ 1; ++n
)
181 for(qpms_l_t k
= 0; k
<= n
; ++k
)
182 c
->akn
[akn_index(n
,k
)] = exp(lgamma(n
+ k
+ 1) - k
*M_LN2
- lgamma(k
+ 1) - lgamma(n
- k
+ 1));
189 complex double qpms_sbessel_calc_h1(qpms_sbessel_calculator_t
*c
, qpms_l_t n
, complex double x
) {
190 if(QPMS_SUCCESS
!= qpms_sbessel_calculator_ensure_lMax(c
, n
))
192 complex double z
= I
/x
;
193 complex double result
= 0;
194 for (qpms_l_t k
= n
; k
>= 0; --k
)
195 // can we use fma for complex?
196 //result = fma(result, z, c->akn(n, k));
197 result
= result
* z
+ c
->akn
[akn_index(n
,k
)];
198 result
*= z
* ipow(-n
-2) * cexp(I
* x
);
202 qpms_errno_t
qpms_sbessel_calc_h1_fill(qpms_sbessel_calculator_t
* const c
,
203 const qpms_l_t lMax
, const complex double x
, complex double * const target
) {
204 if(QPMS_SUCCESS
!= qpms_sbessel_calculator_ensure_lMax(c
, lMax
))
206 memset(target
, 0, sizeof(complex double) * lMax
);
207 complex double kahancomp
[lMax
];
208 memset(kahancomp
, 0, sizeof(complex double) * lMax
);
209 for(qpms_l_t k
= 0; k
<= lMax
; ++k
){
210 double xp
= cpow(x
, -k
-1);
211 for(qpms_l_t l
= k
; l
<= lMax
; ++l
)
212 ckahanadd(target
+ l
, kahancomp
+ l
, c
->akn
[akn_index(l
,k
)] * xp
* ipow(k
-l
-1));
214 complex double eix
= cexp(I
* x
);
215 for (qpms_l_t l
= 0; l
<= lMax
; ++l
)
220 qpms_sbessel_calculator_t
*qpms_sbessel_calculator_init() {
221 qpms_sbessel_calculator_t
*c
= malloc(sizeof(qpms_sbessel_calculator_t
));
228 void qpms_sbessel_calculator_pfree(qpms_sbessel_calculator_t
*c
) {
229 if(c
->akn
) free(c
->akn
);
230 //if(c->bkn) free(c->bkn);