39const std::complex<double>
M_I(0., 1.);
43 return r * (std::cos(theta) +
M_I * std::sin(theta));
52 return std::sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]);
56 return std::sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]);
75 const int nterm_lanczos = 9;
76 const double pcoeff_lanczos[] = {
77 0.999999999999809932277,
78 676.520368121885098567,
79 -1259.13921672240287047,
80 771.323428777653078849,
81 -176.615029162140599066,
82 12.5073432786869048145,
83 -0.138571095265720116896,
84 9.98436957801957085956e-6,
85 1.50563273514931155834e-7,
88 std::complex<double> series = pcoeff_lanczos[0];
89 for (
int i = 1; i < nterm_lanczos; i++) {
90 series += pcoeff_lanczos[i] / (z + double(i));
99 if (z.real() < 1./2) {
109 std::complex<double> gamma = std::sqrt(2.*M_PI)
110 * std::pow(t, z + 1./2) * std::exp(-t) * series;
121 gsl_sf_result lnr_result, theta_result;
122 gsl_sf_lngamma_complex_e(x, y, &lnr_result, &theta_result);
124 lnr = lnr_result.val;
125 theta = theta_result.val;
129 std::complex<double> x_p = (mu + 1. + nu)/2.;
130 std::complex<double> x_m = (mu + 1. - nu)/2.;
134 const double cutoff_asymp = 100.;
135 std::complex<double> lnratio;
136 if (nu.imag() < cutoff_asymp) {
137 double lnr_p, theta_p;
138 double lnr_m, theta_m;
143 lnratio.real(lnr_p - lnr_m);
144 lnratio.imag(theta_p - theta_m);
148 + (x_p - 1./2) * std::log(x_p) - (x_m - 1./2) * std::log(x_m)
149 + 1./12 * (1./x_p - 1./x_m)
150 - 1./360 * (1./std::pow(x_p, 3) - 1./std::pow(x_m, 3))
151 + 1./1260 * (1./std::pow(x_p, 5) - 1./std::pow(x_m, 5));
165double wigner_3j(
int j1,
int j2,
int j3,
int m1,
int m2,
int m3) {
166 return gsl_sf_coupling_3j(2*j1, 2*j2, 2*j3, 2*m1, 2*m2, 2*m3);
169std::complex<double> \
170SphericalHarmonicCalculator::calc_reduced_spherical_harmonic(
171 const int ell,
const int m,
double pos[3]
174 const double eps = 1.e-9;
177 if (ell == 0 && m == 0) {
return 1.;}
180 double xyz_mod_sq = 0.;
181 for (
int iaxis = 0; iaxis < 3; iaxis++) {
182 xyz_mod_sq += pos[iaxis] * pos[iaxis];
185 double xyz_mod = std::sqrt(xyz_mod_sq);
188 if (std::fabs(xyz_mod) < eps) {
return 0.;}
191 double mu = pos[2] / xyz_mod;
194 double xy_mod = std::sqrt(pos[0] * pos[0] + pos[1] * pos[1]);
198 if (std::fabs(xy_mod) >= eps) {
199 phi = std::acos(pos[0] / xy_mod);
201 phi = - phi + 2.*M_PI;
208 std::complex<double> ylm = std::exp(
M_I *
double(m) * phi)
209 * gsl_sf_legendre_sphPlm(ell, std::abs(m), mu);
212 ylm = std::pow(-1, (m - std::abs(m))/2) * std::conj(ylm);
215 ylm *= std::sqrt(4.*M_PI / (2.*ell + 1.));
220void SphericalHarmonicCalculator::\
221store_reduced_spherical_harmonic_in_fourier_space(
222 const int ell,
const int m,
223 const double boxsize[3],
const int ngrid[3],
224 std::vector< std::complex<double> >& ylm_out
228 2.*M_PI / boxsize[0], 2.*M_PI / boxsize[1], 2.*M_PI / boxsize[2]
233#pragma omp parallel for collapse(3)
235 for (
int i = 0; i < ngrid[0]; i++) {
236 for (
int j = 0; j < ngrid[1]; j++) {
237 for (
int k = 0; k < ngrid[2]; k++) {
244 (i *
static_cast<long long>(ngrid[1]) + j) * ngrid[2] + k;
250 kvec[0] = (i < ngrid[0]/2) ? i * dk[0] : (i - ngrid[0]) * dk[0];
251 kvec[1] = (j < ngrid[1]/2) ? j * dk[1] : (j - ngrid[1]) * dk[1];
252 kvec[2] = (k < ngrid[2]/2) ? k * dk[2] : (k - ngrid[2]) * dk[2];
260void SphericalHarmonicCalculator::\
261store_reduced_spherical_harmonic_in_config_space(
262 const int ell,
const int m,
263 const double boxsize[3],
const int ngrid[3],
264 std::vector< std::complex<double> >& ylm_out
268 boxsize[0] / double(ngrid[0]),
269 boxsize[1] / double(ngrid[1]),
270 boxsize[2] / double(ngrid[2])
275#pragma omp parallel for collapse(3)
277 for (
int i = 0; i < ngrid[0]; i++) {
278 for (
int j = 0; j < ngrid[1]; j++) {
279 for (
int k = 0; k < ngrid[2]; k++) {
286 (i *
static_cast<long long>(ngrid[1]) + j) * ngrid[2] + k;
292 rvec[0] = (i < ngrid[0]/2) ? i * dr[0] : (i - ngrid[0]) * dr[0];
293 rvec[1] = (j < ngrid[1]/2) ? j * dr[1] : (j - ngrid[1]) * dr[1];
294 rvec[2] = (k < ngrid[2]/2) ? k * dr[2] : (k - ngrid[2]) * dr[2];
311 this->split = (this->split >= this->
order * this->
order) ?
314 const double xmin = 0.;
315 const double xmax = this->split;
316 const double dx = this->step;
318 int nsample = int((xmax - xmin)/dx) + 1;
321 double* x =
new double[nsample];
322 double* j_ell =
new double[nsample];
325#pragma omp parallel for
327 for (
int i = 0; i < nsample; i++) {
328 x[i] = xmin + dx * i;
329 j_ell[i] = gsl_sf_bessel_jl(this->order, x[i]);
333 this->accel = gsl_interp_accel_alloc();
334 this->spline = gsl_spline_alloc(gsl_interp_cspline, nsample);
336 gsl_spline_init(this->spline, x, j_ell, nsample);
338 delete[] x;
delete[] j_ell;
345 this->split = other.split;
346 this->step = other.step;
348 this->accel = gsl_interp_accel_alloc();
349 this->spline = gsl_spline_alloc(gsl_interp_cspline, other.spline->size);
352 this->spline, other.spline->x, other.spline->y, other.spline->size
357 if (this->accel !=
nullptr) {
358 gsl_interp_accel_free(this->accel); this->accel =
nullptr;
361 if (this->spline !=
nullptr) {
362 gsl_spline_free(this->spline); this->spline =
nullptr;
367 if (x >= this->split) {
368 return gsl_sf_bessel_jl(this->
order, x);
371 return gsl_spline_eval(this->spline, x, this->accel);
Interpolated spherical Bessel function of the first kind.
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.