40 ((mu + 1. + q) <= 0. && std::floor(mu + 1. + q) == (mu + 1. + q)) ||
41 ((mu + 1. - q) <= 0. && std::floor(mu + 1. - q) == (mu + 1. - q))
44 "Invalid values of order `mu` and bias `q`: "
45 "negative poles in the gamma function."
52 this->threaded = threaded;
55#if defined(TRV_USE_OMP) && defined(TRV_USE_FFTWOMP)
58 fftw_plan_with_nthreads(omp_get_max_threads());
79void HankelTransform::reset() {
80 if (this->plan_init) {
81 fftw_destroy_plan(this->pre_plan);
82 fftw_destroy_plan(this->post_plan);
83 this->plan_init =
false;
85 if (this->pre_buffer !=
nullptr) {
86 fftw_free(this->pre_buffer); this->pre_buffer =
nullptr;
88 if (this->post_buffer !=
nullptr) {
89 fftw_free(this->post_buffer); this->post_buffer =
nullptr;
94 std::vector<double> sample_pts,
double kr_c,
bool lowring,
98 if (sample_pts.size() < 2) {
100 "The number of sample points must be at least 2."
105 "The sample points are not logarithmically spaced."
110 this->
nsamp = sample_pts.size();
112 std::log(sample_pts.back() / sample_pts.front()) / (this->
nsamp - 1);
114 this->extrap = extrap;
115 if (this->extrap != trva::ExtrapOption::NONE) {
116 if (this->
nsamp % 2 != 0) {
118 "The number of sample points must be even for extrapolation."
122 2, std::ceil(std::log2(extrap_exp * this->
nsamp))
124 if (this->nsamp_trans < this->
nsamp) {
126 "The sample size expansion factor results in a shrunken sample size."
132 this->
pre_sampts, this->n_ext, this->pre_sampts_extrap
155 double kr_aprod = this->
pivot * std::exp(- this->
logres);
158 for (
int j = 0; j < this->
nsamp; j++) {
162 if (this->extrap != trva::ExtrapOption::NONE) {
163 this->post_sampts_extrap.resize(this->
nsamp_trans);
165 this->post_sampts_extrap[j] =
166 kr_aprod / this->
pre_sampts[this->nsamp_trans - j - 1];
182 this->pre_buffer = fftw_alloc_complex(this->
nsamp_trans);
183 this->pre_plan = fftw_plan_dft_1d(
184 this->
nsamp_trans, this->pre_buffer, this->pre_buffer,
185 FFTW_FORWARD, FFTW_ESTIMATE
188 this->post_buffer = fftw_alloc_complex(this->
nsamp_trans);
189 this->post_plan = fftw_plan_dft_1d(
190 this->
nsamp_trans, this->post_buffer, this->post_buffer,
191 FFTW_FORWARD, FFTW_ESTIMATE
194 this->plan_init =
true;
198 std::vector<double> sample_pts,
double kr_c,
bool lowring,
199 int extrap,
double extrap_exp
208 double mu = this->
order;
209 double q = this->
bias;
212 double x_p = (mu + 1. + q)/2.;
213 double x_m = (mu + 1. - q)/2.;
214 double y = M_PI / (2.*dL);
219 double lnr_p, lnr_m, phi_p, phi_m;
223 double argphase = std::log(2./kr_c) / dL + (phi_p + phi_m) / M_PI;
225 kr_c *= std::exp(dL * (argphase - std::round(argphase)));
230 double lnkr_c = std::log(2) + gamma_lnratio.imag() / (2*y);
232 kr_c = std::exp(lnkr_c - std::floor(lnkr_c / dL) * dL);
240 double mu = this->
order;
241 double q = this->
bias;
244 double kr_c = this->
pivot;
246 if (N_trans <= 0 || dL <= 0. || kr_c <= 0.) {
247 throw std::runtime_error(
248 "This instance of trv::maths::HankelTransform has not been "
249 "initialised with `initialise`."
253 double x_p = (mu + 1. + q)/2.;
254 double x_m = (mu + 1. - q)/2.;
255 double x_eq = (mu + 1.)/2.;
257 double y = M_PI / (N_trans * dL);
259 double t = -2. * y * std::log(kr_c/2.);
263 std::vector< std::complex<double> > u(N_trans);
265 for (
int m = 0; m <= N_trans/2; m++) {
272 double qln2 = q * std::log(2.);
273 for (
int m = 0; m <= N_trans/2; m++) {
280 std::exp(qln2 + lnr_p - lnr_m), m*t + phi_p + phi_m
285 for (
int m = N_trans/2 + 1; m < N_trans; m++) {
286 u[m] = conj(u[N_trans - m]);
289 if (N_trans % 2 == 0) {
291 u[N_trans/2] = u[N_trans/2].real() +
trvm::M_I * 0.;
298 std::complex<double>* a, std::complex<double>* b
304 if (this->kernel.empty() || N <= 2 || N_trans <= 2) {
305 throw std::runtime_error(
306 "This instance of trv::maths::HankelTransform has not been "
307 "initialised with `initialise`."
312 if (this->extrap == trva::ExtrapOption::NONE) {
313 for (
int j = 0; j < N_trans; j++) {
314 this->pre_buffer[j][0] = (a[j]).real();
315 this->pre_buffer[j][1] = (a[j]).imag();
316 this->post_buffer[j][0] = 0.;
317 this->post_buffer[j][1] = 0.;
321 std::vector<double> a_vec(N);
322 for (
int j = 0; j < N; j++) {
323 a_vec[j] = a[j].real();
326 std::vector<double> a_trans_vec(N_trans);
327 switch (this->extrap) {
328 case trva::ExtrapOption::NONE:
330 case trva::ExtrapOption::ZERO:
332 a_vec, this->n_ext, 0., 0., a_trans_vec
335 case trva::ExtrapOption::PAD:
337 a_vec, this->n_ext, a_vec.front(), a_vec.back(), a_trans_vec
340 case trva::ExtrapOption::LIN:
343 case trva::ExtrapOption::LOGLIN:
350 for (
int j = 0; j < N_trans; j++) {
351 this->pre_buffer[j][0] = a_trans_vec[j];
352 this->pre_buffer[j][1] = 0.;
353 this->post_buffer[j][0] = 0.;
354 this->post_buffer[j][1] = 0.;
359 fftw_execute(this->pre_plan);
361 for (
int m = 0; m < N_trans; m++) {
363 std::complex<double> a_(this->pre_buffer[m][0], this->pre_buffer[m][1]);
364 std::complex<double> b_ = a_ * this->kernel[m] / double(N_trans);
365 this->post_buffer[m][0] = b_.real();
366 this->post_buffer[m][1] = b_.imag();
369 fftw_execute(this->post_plan);
372 for (
int j = 0; j < N; j++) {
373 b[j] = std::complex<double>(
374 this->post_buffer[j + this->n_ext][0],
375 this->post_buffer[j + this->n_ext][1]
403 int ell,
int n,
bool threaded
409 std::vector<double> sample_pts,
double kr_c,
bool lowring,
416 std::vector<double> sample_pts,
double kr_c,
bool lowring,
417 int extrap,
double extrap_exp
423 std::vector< std::complex<double> >& a,
424 std::vector< std::complex<double> >& b
429 if (
int(a.size()) != N) {
431 "The size of array `a` must be equal to the number of samples."
434 if (
int(a.size()) != N) {
438 std::complex<double> A[N];
439 std::complex<double> B[N];
441 for (
int j = 0; j < N; j++) {
442 A[j] = std::pow(this->
pre_sampts[j], 3./2) * a[j];
447 for (
int j = 0; j < N; j++) {
448 b[j] = std::pow(2*M_PI / this->
post_sampts[j], 3./2) * B[j];
454 std::vector< std::complex<double> >& pre_samples,
455 std::vector< std::complex<double> >& post_samples
459 "The transform direction must be either +1 (forward) or -1 (backward).\n"
463 double pifactor = (dir == -1) ? std::pow(2*M_PI, -3) : 1.;
464 std::complex<double> parity = std::pow(
trvm::M_I, - dir * this->
degree);
465 std::complex<double> prefactor = pifactor * parity;
469 for (
int j = 0; j <this->
nsamp; j++) {
470 post_samples[j] *= prefactor;
Exception raised when parameters are invalid.
FFTLog algorithm for Hankel and related transforms and its application to cosmological functions.
int check_1d_array(std::vector< double > &a, bool check_lin, bool check_loglin, bool check_sign)
Check the linearity or log-linearity of a 1-d array.
ExtrapOption
Extrapolation scheme.
void extrap_loglin(std::vector< double > &a, int N_ext, std::vector< double > &a_ext)
Extrapolate a 1-d array exponentially (i.e. log-linearly).
void extrap_lin(std::vector< double > &a, int N_ext, std::vector< double > &a_ext)
Extrapolate a 1-d array linearly.
void extrap_pad(std::vector< double > &a, int N_ext, double c_lower, double c_upper, std::vector< double > &a_ext)
Extrapolate a 1-d array by constant padding.
std::complex< double > eval_complex_in_polar(double r, double theta)
Evaluate a complex number in the polar form.
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.
const std::complex< double > M_I
imaginary unit