41const std::complex<double>
M_I(0., 1.);
45 return r * (std::cos(theta) +
M_I * std::sin(theta));
54 return std::sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]);
58 return std::sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]);
77 const int nterm_lanczos = 9;
78 const double pcoeff_lanczos[] = {
79 0.999999999999809932277,
80 676.520368121885098567,
81 -1259.13921672240287047,
82 771.323428777653078849,
83 -176.615029162140599066,
84 12.5073432786869048145,
85 -0.138571095265720116896,
86 9.98436957801957085956e-6,
87 1.50563273514931155834e-7,
90 std::complex<double> series = pcoeff_lanczos[0];
91 for (
int i = 1; i < nterm_lanczos; i++) {
92 series += pcoeff_lanczos[i] / (z + double(i));
101 if (z.real() < 1./2) {
111 std::complex<double> gamma = std::sqrt(2.*M_PI)
112 * std::pow(t, z + 1./2) * std::exp(-t) * series;
123 gsl_sf_result lnr_result, theta_result;
124 gsl_sf_lngamma_complex_e(x, y, &lnr_result, &theta_result);
126 lnr = lnr_result.val;
127 theta = theta_result.val;
131 std::complex<double> x_p = (mu + 1. + nu)/2.;
132 std::complex<double> x_m = (mu + 1. - nu)/2.;
136 const double cutoff_asymp = 100.;
137 std::complex<double> lnratio;
138 if (nu.imag() < cutoff_asymp) {
139 double lnr_p, theta_p;
140 double lnr_m, theta_m;
145 lnratio.real(lnr_p - lnr_m);
146 lnratio.imag(theta_p - theta_m);
150 + (x_p - 1./2) * std::log(x_p) - (x_m - 1./2) * std::log(x_m)
151 + 1./12 * (1./x_p - 1./x_m)
152 - 1./360 * (1./std::pow(x_p, 3) - 1./std::pow(x_m, 3))
153 + 1./1260 * (1./std::pow(x_p, 5) - 1./std::pow(x_m, 5));
167double wigner_3j(
int j1,
int j2,
int j3,
int m1,
int m2,
int m3) {
168 return gsl_sf_coupling_3j(2*j1, 2*j2, 2*j3, 2*m1, 2*m2, 2*m3);
171std::complex<double> \
172SphericalHarmonicCalculator::calc_reduced_spherical_harmonic(
173 const int ell,
const int m,
double pos[3]
176 const double eps = 1.e-9;
179 if (ell == 0 && m == 0) {
return 1.;}
182 double xyz_mod_sq = 0.;
183 for (
int iaxis = 0; iaxis < 3; iaxis++) {
184 xyz_mod_sq += pos[iaxis] * pos[iaxis];
187 double xyz_mod = std::sqrt(xyz_mod_sq);
190 if (std::fabs(xyz_mod) < eps) {
return 0.;}
193 double mu = pos[2] / xyz_mod;
196 double xy_mod = std::sqrt(pos[0] * pos[0] + pos[1] * pos[1]);
200 if (std::fabs(xy_mod) >= eps) {
201 phi = std::acos(pos[0] / xy_mod);
203 phi = - phi + 2.*M_PI;
210 std::complex<double> ylm = std::exp(
M_I *
double(m) * phi)
211 * gsl_sf_legendre_sphPlm(ell, std::abs(m), mu);
214 ylm = std::pow(-1, (m - std::abs(m))/2) * std::conj(ylm);
217 ylm *= std::sqrt(4.*M_PI / (2.*ell + 1.));
222void SphericalHarmonicCalculator::\
223store_reduced_spherical_harmonic_in_fourier_space(
224 const int ell,
const int m,
225 const double boxsize[3],
const int ngrid[3],
226 std::vector< std::complex<double> >& ylm_out
230 2.*M_PI / boxsize[0], 2.*M_PI / boxsize[1], 2.*M_PI / boxsize[2]
235#pragma omp parallel for collapse(3)
237 for (
int i = 0; i < ngrid[0]; i++) {
238 for (
int j = 0; j < ngrid[1]; j++) {
239 for (
int k = 0; k < ngrid[2]; k++) {
246 (i *
static_cast<long long>(ngrid[1]) + j) * ngrid[2] + k;
252 kvec[0] = (i < ngrid[0]/2) ? i * dk[0] : (i - ngrid[0]) * dk[0];
253 kvec[1] = (j < ngrid[1]/2) ? j * dk[1] : (j - ngrid[1]) * dk[1];
254 kvec[2] = (k < ngrid[2]/2) ? k * dk[2] : (k - ngrid[2]) * dk[2];
262void SphericalHarmonicCalculator::\
263store_reduced_spherical_harmonic_in_config_space(
264 const int ell,
const int m,
265 const double boxsize[3],
const int ngrid[3],
266 std::vector< std::complex<double> >& ylm_out
270 boxsize[0] / double(ngrid[0]),
271 boxsize[1] / double(ngrid[1]),
272 boxsize[2] / double(ngrid[2])
277#pragma omp parallel for collapse(3)
279 for (
int i = 0; i < ngrid[0]; i++) {
280 for (
int j = 0; j < ngrid[1]; j++) {
281 for (
int k = 0; k < ngrid[2]; k++) {
288 (i *
static_cast<long long>(ngrid[1]) + j) * ngrid[2] + k;
294 rvec[0] = (i < ngrid[0]/2) ? i * dr[0] : (i - ngrid[0]) * dr[0];
295 rvec[1] = (j < ngrid[1]/2) ? j * dr[1] : (j - ngrid[1]) * dr[1];
296 rvec[2] = (k < ngrid[2]/2) ? k * dr[2] : (k - ngrid[2]) * dr[2];
313 this->split = (this->split >= this->
order * this->
order) ?
316 const double xmin = 0.;
317 const double xmax = this->split;
318 const double dx = this->step;
320 int nsample = int((xmax - xmin)/dx) + 1;
323 double* x =
new double[nsample];
324 double* j_ell =
new double[nsample];
327#pragma omp parallel for
329 for (
int i = 0; i < nsample; i++) {
330 x[i] = xmin + dx * i;
331 j_ell[i] = gsl_sf_bessel_jl(this->order, x[i]);
335 this->accel = gsl_interp_accel_alloc();
336 this->spline = gsl_spline_alloc(gsl_interp_cspline, nsample);
338 gsl_spline_init(this->spline, x, j_ell, nsample);
340 delete[] x;
delete[] j_ell;
347 this->split = other.split;
348 this->step = other.step;
350 this->accel = gsl_interp_accel_alloc();
351 this->spline = gsl_spline_alloc(gsl_interp_cspline, other.spline->size);
354 this->spline, other.spline->x, other.spline->y, other.spline->size
359 if (this->accel !=
nullptr) {
360 gsl_interp_accel_free(this->accel); this->accel =
nullptr;
363 if (this->spline !=
nullptr) {
364 gsl_spline_free(this->spline); this->spline =
nullptr;
369 if (x >= this->split) {
370 return gsl_sf_bessel_jl(this->
order, x);
373 return gsl_spline_eval(this->spline, x, this->accel);
double eval(double x)
Evaluate the interpolated function.
SphericalBesselCalculator(const int ell)
Construct the interpolated function calculator.
~SphericalBesselCalculator()
Destruct the interpolated function.
static std::complex< double > calc_reduced_spherical_harmonic(const int ell, const int m, double pos[3])
Calculate the reduced spherical harmonic.
Mathematical calculations.
std::complex< double > eval_complex_in_polar(double r, double theta)
Evaluate a complex number in the polar form.
double get_vec3d_magnitude(std::vector< double > vec)
Return the magnitude of a 3-d vector.
std::complex< double > eval_gamma_lanczos(std::complex< double > z)
Evaluate the gamma function on the complex plane using the Lanczos approximation.
void get_lngamma_parts(double x, double y, double &lnr, double &theta)
Get the real and imaginary parts of the log-gamma function .
std::complex< double > eval_gamma_lnratio(double mu, std::complex< double > nu)
Evaluate the logarithm of the ratio of two gamma functions.
std::complex< double > eval_lanczos_approx_series(std::complex< double > z)
Evaluate the Lanczos approximation series for the gamma function.
const std::complex< double > M_I
imaginary unit
const double eps_coupling
zero-tolerance for Wigner 3-j coupling coefficients
const double gconst_lanczos
Lanczos approximation constant.
double wigner_3j(int j1, int j2, int j3, int m1, int m2, int m3)
Calculate Wigner 3-j symbol.