53 this->
field = fftw_alloc_complex(this->params.
nmesh);
62 this->field_s = fftw_alloc_complex(this->params.
nmesh);
75#if defined(TRV_USE_OMP) && defined(TRV_USE_FFTWOMP)
76 fftw_plan_with_nthreads(omp_get_max_threads());
78 bool import_fftw_wisdom_f =
false;
79 bool import_fftw_wisdom_b =
false;
80 bool export_fftw_wisdom_f =
false;
81 bool export_fftw_wisdom_b =
false;
82 std::FILE* fftw_wisdom_file_f =
nullptr;
83 std::FILE* fftw_wisdom_file_b =
nullptr;
89 if (fftw_wisdom_file_f !=
nullptr) {
90 import_fftw_wisdom_f =
true;
91 export_fftw_wisdom_f =
false;
95 "FFTW planner flag is set to `FFTW_PATIENT`. "
96 "Ensure that the FFTW wisdom file '%s' imported has been "
97 "generated with an equivalent or higher planner flag.",
103 import_fftw_wisdom_f =
false;
104 export_fftw_wisdom_f =
true;
107 "No FFTW wisdom file for forward transforms "
108 "could be imported: %s",
118 if (fftw_wisdom_file_b !=
nullptr) {
119 import_fftw_wisdom_b =
true;
120 export_fftw_wisdom_b =
false;
124 "FFTW planner flag is set to `FFTW_PATIENT`. "
125 "Ensure that the FFTW wisdom file '%s' imported has been "
126 "generated with an equivalent or higher planner flag.",
132 import_fftw_wisdom_b =
false;
133 export_fftw_wisdom_b =
true;
136 "No FFTW wisdom file for backward transforms "
137 "could be imported: %s",
145 if (import_fftw_wisdom_f) {
146 fftw_import_wisdom_from_filename(
152 "FFTW wisdom file for forward transforms has been imported: %s",
158 auto pre_plan_f_timept = std::chrono::system_clock::now();
159 this->transform = fftw_plan_dft_3d(
160 this->params.
ngrid[0], this->params.ngrid[1], this->params.ngrid[2],
161 this->field, this->field,
162 FFTW_FORWARD, this->params.fftw_planner_flag
164 auto post_plan_f_timept = std::chrono::system_clock::now();
166 double plan_f_time = std::chrono::duration<double>(
167 post_plan_f_timept - pre_plan_f_timept
169 if (import_fftw_wisdom_f && plan_f_time > 1.) {
172 "FFTW plan for forward transforms took %.3f (> 1.) seconds "
173 "despite importing wisdom file, which may have been created "
174 "under different runtime conditions. "
175 "A new wisdom file will be exported.",
179 export_fftw_wisdom_f =
true;
182 if (export_fftw_wisdom_f) {
183 fftw_export_wisdom_to_filename(
188 "FFTW wisdom file for forward transforms has been exported: %s",
195 if (import_fftw_wisdom_b) {
196 fftw_import_wisdom_from_filename(
202 "FFTW wisdom file for backward transforms has been imported: %s",
208 auto pre_plan_b_timept = std::chrono::system_clock::now();
209 this->inv_transform = fftw_plan_dft_3d(
210 this->params.
ngrid[0], this->params.ngrid[1], this->params.ngrid[2],
211 this->field, this->field,
212 FFTW_BACKWARD, this->params.fftw_planner_flag
214 auto post_plan_b_timept = std::chrono::system_clock::now();
216 double plan_b_time = std::chrono::duration<double>(
217 post_plan_b_timept - pre_plan_b_timept
219 if (import_fftw_wisdom_b && plan_b_time > 1.) {
222 "FFTW plan for backward transforms took %.3f (> 1.) seconds "
223 "despite importing wisdom file, which may have been created "
224 "under different runtime conditions. "
225 "A new wisdom file will be exported.",
229 export_fftw_wisdom_b =
true;
232 if (export_fftw_wisdom_b) {
233 fftw_export_wisdom_to_filename(
238 "FFTW wisdom file for backward transforms has been exported: %s",
246 this->transform_s = fftw_plan_dft_3d(
247 this->params.
ngrid[0], this->params.ngrid[1], this->params.ngrid[2],
248 this->field_s, this->field_s,
249 FFTW_FORWARD, this->params.fftw_planner_flag
252 this->plan_ini =
true;
254 if (fftw_wisdom_file_f !=
nullptr) {std::fclose(fftw_wisdom_file_f);}
255 if (fftw_wisdom_file_b !=
nullptr) {std::fclose(fftw_wisdom_file_b);}
264 this->
dk[0] = 2.*M_PI / this->params.
boxsize[0];
265 this->
dk[1] = 2.*M_PI / this->params.
boxsize[1];
266 this->
dk[2] = 2.*M_PI / this->params.
boxsize[2];
275 fftw_plan& transform, fftw_plan& inv_transform,
276 const std::string& name
286 this->
field = fftw_alloc_complex(this->params.
nmesh);
295 this->field_s = fftw_alloc_complex(this->params.
nmesh);
307 this->transform = transform;
308 this->inv_transform = inv_transform;
310 this->transform_s = transform;
312 this->plan_ext =
true;
320 this->
dk[0] = 2.*M_PI / this->params.
boxsize[0];
321 this->
dk[1] = 2.*M_PI / this->params.
boxsize[1];
322 this->
dk[2] = 2.*M_PI / this->params.
boxsize[2];
330 if (this->plan_ini) {
331 fftw_destroy_plan(this->transform);
332 fftw_destroy_plan(this->inv_transform);
334 fftw_destroy_plan(this->transform_s);
338 if (this->window_assign_order != -1) {
344 if (this->
field !=
nullptr) {
345 fftw_free(this->
field); this->
field =
nullptr;
350 if (this->field_s !=
nullptr) {
351 fftw_free(this->field_s); this->field_s =
nullptr;
360#pragma omp parallel for simd
362 for (
long long gid = 0; gid < this->
params.
nmesh; gid++) {
363 this->
field[gid][0] = 0.;
364 this->
field[gid][1] = 0.;
368#pragma omp parallel for simd
370 for (
long long gid = 0; gid < this->
params.
nmesh; gid++) {
371 this->field_s[gid][0] = 0.;
372 this->field_s[gid][1] = 0.;
383 return this->
field[gid];
391long long MeshField::ret_grid_index(
int i,
int j,
int k) {
393 (i *
static_cast<long long>(this->
params.
ngrid[1]) + j)
398void MeshField::shift_grid_indices_fourier(
int& i,
int& j,
int& k) {
404void MeshField::get_grid_pos_vector(
int i,
int j,
int k,
double rvec[3]) {
406 i * this->
dr[0] : (i - this->
params.ngrid[0]) * this->
dr[0];
408 j * this->
dr[1] : (j - this->
params.ngrid[1]) * this->
dr[1];
410 k * this->
dr[2] : (k - this->
params.ngrid[2]) * this->
dr[2];
413void MeshField::get_grid_wavevector(
int i,
int j,
int k,
double kvec[3]) {
415 i * this->
dk[0] : (i - this->
params.ngrid[0]) * this->
dk[0];
417 j * this->
dk[1] : (j - this->
params.ngrid[1]) * this->
dk[1];
419 k * this->
dk[2] : (k - this->
params.ngrid[2]) * this->
dk[2];
432 "Performing mesh assignment scheme '%s' to '%s'.",
438 for (
int iaxis = 0; iaxis < 3; iaxis++) {
439 double extent = particles.
pos_max[iaxis] - particles.
pos_min[iaxis];
443 "Box size in dimension %d is smaller than catalogue extents: "
452 this->assign_weighted_field_to_mesh_ngp(particles, weights);
455 this->assign_weighted_field_to_mesh_cic(particles, weights);
458 this->assign_weighted_field_to_mesh_tsc(particles, weights);
461 this->assign_weighted_field_to_mesh_pcs(particles, weights);
465 "Unsupported mesh assignment scheme: '%s'.",
470 "Unsupported mesh assignment scheme: '%s'.\n",
476void MeshField::assign_weighted_field_to_mesh_ngp(
485 const double inv_vol_cell = 1 / this->
vol_cell;
492#pragma omp parallel for
494 for (
int pid = 0; pid < particles.
ntotal; pid++) {
496 double win[order][3];
499 for (
int iaxis = 0; iaxis < 3; iaxis++) {
504 int idx_grid = int(loc_grid);
505 if (loc_grid - idx_grid >= 0.5) {
506 idx_grid = (idx_grid == this->
params.
ngrid[iaxis] - 1)
510 ijk[0][iaxis] = idx_grid;
516 for (
int iloc = 0; iloc < order; iloc++) {
517 for (
int jloc = 0; jloc < order; jloc++) {
518 for (
int kloc = 0; kloc < order; kloc++) {
519 gid = this->ret_grid_index(
520 ijk[iloc][0], ijk[jloc][1], ijk[kloc][2]
524 this->
field[gid][0] += inv_vol_cell
525 * weight[pid][0] * win[iloc][0] * win[jloc][1] * win[kloc][2];
527 this->field[gid][1] += inv_vol_cell
528 * weight[pid][1] * win[iloc][0] * win[jloc][1] * win[kloc][2];
538#pragma omp parallel for
540 for (
int pid = 0; pid < particles.
ntotal; pid++) {
542 double win[order][3];
545 for (
int iaxis = 0; iaxis < 3; iaxis++) {
548 * particles[pid].pos[iaxis] / this->
params.
boxsize[iaxis] + 0.5;
554 int idx_grid = int(loc_grid);
555 if (loc_grid - idx_grid >= 0.5) {
556 idx_grid = (idx_grid == this->
params.
ngrid[iaxis] - 1)
560 ijk[0][iaxis] = idx_grid;
565 for (
int iloc = 0; iloc < order; iloc++) {
566 for (
int jloc = 0; jloc < order; jloc++) {
567 for (
int kloc = 0; kloc < order; kloc++) {
568 gid = this->ret_grid_index(
569 ijk[iloc][0], ijk[jloc][1], ijk[kloc][2]
573 this->field_s[gid][0] += inv_vol_cell
574 * weight[pid][0] * win[iloc][0] * win[jloc][1] * win[kloc][2];
576 this->field_s[gid][1] += inv_vol_cell
577 * weight[pid][1] * win[iloc][0] * win[jloc][1] * win[kloc][2];
586void MeshField::assign_weighted_field_to_mesh_cic(
587 ParticleCatalogue& particles, fftw_complex* weight
595 const double inv_vol_cell = 1 / this->
vol_cell;
602#pragma omp parallel for
604 for (
int pid = 0; pid < particles.ntotal; pid++) {
606 double win[order][3];
609 for (
int iaxis = 0; iaxis < 3; iaxis++) {
614 int idx_grid = int(loc_grid);
616 ijk[0][iaxis] = idx_grid;
617 ijk[1][iaxis] = (idx_grid == this->
params.
ngrid[iaxis] - 1)
621 double s = loc_grid - idx_grid;
623 win[0][iaxis] = 1. - s;
627 for (
int iloc = 0; iloc < order; iloc++) {
628 for (
int jloc = 0; jloc < order; jloc++) {
629 for (
int kloc = 0; kloc < order; kloc++) {
630 gid = this->ret_grid_index(
631 ijk[iloc][0], ijk[jloc][1], ijk[kloc][2]
635 this->
field[gid][0] += inv_vol_cell
636 * weight[pid][0] * win[iloc][0] * win[jloc][1] * win[kloc][2];
638 this->field[gid][1] += inv_vol_cell
639 * weight[pid][1] * win[iloc][0] * win[jloc][1] * win[kloc][2];
649#pragma omp parallel for
651 for (
int pid = 0; pid < particles.ntotal; pid++) {
653 double win[order][3];
656 for (
int iaxis = 0; iaxis < 3; iaxis++) {
659 * particles[pid].pos[iaxis] / this->
params.
boxsize[iaxis] + 0.5;
665 int idx_grid = int(loc_grid);
667 ijk[0][iaxis] = idx_grid;
668 ijk[1][iaxis] = (idx_grid == this->
params.
ngrid[iaxis] - 1)
671 double s = loc_grid - idx_grid;
672 win[0][iaxis] = 1. - s;
676 for (
int iloc = 0; iloc < order; iloc++) {
677 for (
int jloc = 0; jloc < order; jloc++) {
678 for (
int kloc = 0; kloc < order; kloc++) {
679 gid = this->ret_grid_index(
680 ijk[iloc][0], ijk[jloc][1], ijk[kloc][2]
684 this->field_s[gid][0] += inv_vol_cell
685 * weight[pid][0] * win[iloc][0] * win[jloc][1] * win[kloc][2];
687 this->field_s[gid][1] += inv_vol_cell
688 * weight[pid][1] * win[iloc][0] * win[jloc][1] * win[kloc][2];
697void MeshField::assign_weighted_field_to_mesh_tsc(
698 ParticleCatalogue& particles, fftw_complex* weight
706 const double inv_vol_cell = 1 / this->
vol_cell;
713#pragma omp parallel for
715 for (
int pid = 0; pid < particles.ntotal; pid++) {
717 double win[order][3];
720 for (
int iaxis = 0; iaxis < 3; iaxis++) {
725 int idx_grid = int(loc_grid);
727 if (loc_grid - idx_grid < 0.5) {
728 ijk[0][iaxis] = (idx_grid == 0)
730 ijk[1][iaxis] = idx_grid;
731 ijk[2][iaxis] = (idx_grid == this->
params.
ngrid[iaxis] - 1)
734 ijk[0][iaxis] = idx_grid;
735 ijk[1][iaxis] = (idx_grid == this->
params.
ngrid[iaxis] - 1)
737 ijk[2][iaxis] = (ijk[1][iaxis] == this->
params.
ngrid[iaxis] - 1)
738 ? 0 : ijk[1][iaxis] + 1;
742 double s = loc_grid - idx_grid;
745 win[0][iaxis] = 1./2 * (1./2 - s) * (1./2 - s);
746 win[1][iaxis] = 3./4 - s * s;
747 win[2][iaxis] = 1./2 * (1./2 + s) * (1./2 + s);
750 win[0][iaxis] = 1./2 * (1./2 + s) * (1./2 + s);
751 win[1][iaxis] = 3./4 - s * s;
752 win[2][iaxis] = 1./2 * (1./2 - s) * (1./2 - s);
756 for (
int iloc = 0; iloc < order; iloc++) {
757 for (
int jloc = 0; jloc < order; jloc++) {
758 for (
int kloc = 0; kloc < order; kloc++) {
759 gid = this->ret_grid_index(
760 ijk[iloc][0], ijk[jloc][1], ijk[kloc][2]
764 this->
field[gid][0] += inv_vol_cell
765 * weight[pid][0] * win[iloc][0] * win[jloc][1] * win[kloc][2];
767 this->field[gid][1] += inv_vol_cell
768 * weight[pid][1] * win[iloc][0] * win[jloc][1] * win[kloc][2];
778#pragma omp parallel for
780 for (
int pid = 0; pid < particles.ntotal; pid++) {
782 double win[order][3];
785 for (
int iaxis = 0; iaxis < 3; iaxis++) {
788 * particles[pid].pos[iaxis] / this->
params.
boxsize[iaxis] + 0.5;
794 int idx_grid = int(loc_grid);
796 if (loc_grid - idx_grid < 0.5) {
797 ijk[0][iaxis] = (idx_grid == 0)
799 ijk[1][iaxis] = idx_grid;
800 ijk[2][iaxis] = (idx_grid == this->
params.
ngrid[iaxis] - 1)
803 ijk[0][iaxis] = idx_grid;
804 ijk[1][iaxis] = (idx_grid == this->
params.
ngrid[iaxis] - 1)
805 ? 0 : ijk[0][iaxis] + 1;
806 ijk[2][iaxis] = (idx_grid == this->
params.
ngrid[iaxis] - 1)
807 ? 0 : ijk[1][iaxis] + 1;
810 double s = loc_grid - idx_grid;
813 win[0][iaxis] = 1./2 * (1./2 - s) * (1./2 - s);
814 win[1][iaxis] = 3./4 - s * s;
815 win[2][iaxis] = 1./2 * (1./2 + s) * (1./2 + s);
818 win[0][iaxis] = 1./2 * (1./2 + s) * (1./2 + s);
819 win[1][iaxis] = 3./4 - s * s;
820 win[2][iaxis] = 1./2 * (1./2 - s) * (1./2 - s);
824 for (
int iloc = 0; iloc < order; iloc++) {
825 for (
int jloc = 0; jloc < order; jloc++) {
826 for (
int kloc = 0; kloc < order; kloc++) {
827 gid = this->ret_grid_index(
828 ijk[iloc][0], ijk[jloc][1], ijk[kloc][2]
832 this->field_s[gid][0] += inv_vol_cell
833 * weight[pid][0] * win[iloc][0] * win[jloc][1] * win[kloc][2];
835 this->field_s[gid][1] += inv_vol_cell
836 * weight[pid][1] * win[iloc][0] * win[jloc][1] * win[kloc][2];
845void MeshField::assign_weighted_field_to_mesh_pcs(
846 ParticleCatalogue& particles, fftw_complex* weight
854 const double inv_vol_cell = 1 / this->
vol_cell;
861#pragma omp parallel for
863 for (
int pid = 0; pid < particles.ntotal; pid++) {
865 double win[order][3];
868 for (
int iaxis = 0; iaxis < 3; iaxis++) {
873 int idx_grid = int(loc_grid);
875 ijk[0][iaxis] = (idx_grid == 0)
877 ijk[1][iaxis] = idx_grid;
878 ijk[2][iaxis] = (idx_grid == this->
params.
ngrid[iaxis] - 1)
880 ijk[3][iaxis] = (ijk[2][iaxis] == this->
params.
ngrid[iaxis] - 1)
881 ? 0 : ijk[2][iaxis] + 1;
884 double s = loc_grid - idx_grid;
886 win[0][iaxis] = 1./6 * (1. - s) * (1. - s) * (1. - s);
887 win[1][iaxis] = 1./6 * (4. - 6. * s * s + 3. * s * s * s);
888 win[2][iaxis] = 1./6 * (
889 4. - 6. * (1. - s) * (1. - s) + 3. * (1. - s) * (1. - s) * (1. - s)
891 win[3][iaxis] = 1./6 * s * s * s;
894 for (
int iloc = 0; iloc < order; iloc++) {
895 for (
int jloc = 0; jloc < order; jloc++) {
896 for (
int kloc = 0; kloc < order; kloc++) {
897 gid = this->ret_grid_index(
898 ijk[iloc][0], ijk[jloc][1], ijk[kloc][2]
902 this->
field[gid][0] += inv_vol_cell
903 * weight[pid][0] * win[iloc][0] * win[jloc][1] * win[kloc][2];
905 this->field[gid][1] += inv_vol_cell
906 * weight[pid][1] * win[iloc][0] * win[jloc][1] * win[kloc][2];
916#pragma omp parallel for
918 for (
int pid = 0; pid < particles.ntotal; pid++) {
920 double win[order][3];
923 for (
int iaxis = 0; iaxis < 3; iaxis++) {
926 * particles[pid].pos[iaxis] / this->
params.
boxsize[iaxis] + 0.5;
932 int idx_grid = int(loc_grid);
934 ijk[0][iaxis] = (idx_grid == 0)
936 ijk[1][iaxis] = idx_grid;
937 ijk[2][iaxis] = (idx_grid == this->
params.
ngrid[iaxis] - 1)
939 ijk[3][iaxis] = (ijk[2][iaxis] == this->
params.
ngrid[iaxis] - 1)
940 ? 0 : ijk[2][iaxis] + 1;
941 double s = loc_grid - idx_grid;
943 win[0][iaxis] = 1./6 * (1. - s) * (1. - s) * (1. - s);
944 win[1][iaxis] = 1./6 * (4. - 6. * s * s + 3. * s * s * s);
945 win[2][iaxis] = 1./6 * (
946 4. - 6. * (1. - s) * (1. - s) + 3. * (1. - s) * (1. - s) * (1. - s)
948 win[3][iaxis] = 1./6 * s * s * s;
951 for (
int iloc = 0; iloc < order; iloc++) {
952 for (
int jloc = 0; jloc < order; jloc++) {
953 for (
int kloc = 0; kloc < order; kloc++) {
954 gid = this->ret_grid_index(
955 ijk[iloc][0], ijk[jloc][1], ijk[kloc][2]
959 this->field_s[gid][0] += inv_vol_cell
960 * weight[pid][0] * win[iloc][0] * win[jloc][1] * win[kloc][2];
962 this->field_s[gid][1] += inv_vol_cell
963 * weight[pid][1] * win[iloc][0] * win[jloc][1] * win[kloc][2];
972double MeshField::calc_assignment_window_in_fourier(
973 int i,
int j,
int k,
int order
978 "Invalid window assignment order: %d. Must be non-negative.",
983 "Invalid window assignment order: %d. Must be non-negative.\n",
989 if (order == this->window_assign_order) {
990 long long idx_grid = this->ret_grid_index(i, j, k);
991 return this->window[idx_grid];
994 this->shift_grid_indices_fourier(i, j, k);
996 double u_x = M_PI * i / double(this->
params.
ngrid[0]);
997 double u_y = M_PI * j / double(this->
params.
ngrid[1]);
998 double u_z = M_PI * k / double(this->
params.
ngrid[2]);
1001 double wk_x = (i != 0) ? std::sin(u_x) / u_x : 1.;
1002 double wk_y = (j != 0) ? std::sin(u_y) / u_y : 1.;
1003 double wk_z = (k != 0) ? std::sin(u_z) / u_z : 1.;
1005 double wk = wk_x * wk_y * wk_z;
1007 return std::pow(wk, order);
1010void MeshField::compute_assignment_window_in_fourier(
int order) {
1014 "Invalid window assignment order: %d. Must be non-negative.",
1019 "Invalid window assignment order: %d. Must be non-negative.\n",
1024 if (this->window_assign_order == order) {
return;}
1028 "Computing interpolation window in Fourier space "
1029 "for assignment order %d.",
1034 if (this->window_assign_order == -1) {
1045#pragma omp parallel for collapse(3)
1050 long long idx_grid = this->ret_grid_index(i, j, k);
1051 this->window[idx_grid] = this->calc_assignment_window_in_fourier(
1058 this->window_assign_order = order;
1067 fftw_complex* unit_weight =
nullptr;
1069 unit_weight = fftw_alloc_complex(particles.
ntotal);
1075#pragma omp parallel for simd
1077 for (
int pid = 0; pid < particles.
ntotal; pid++) {
1078 unit_weight[pid][0] = 1.;
1079 unit_weight[pid][1] = 0.;
1084 fftw_free(unit_weight); unit_weight =
nullptr;
1095 double nbar = double(particles.
ntotal) / this->
vol;
1098#pragma omp parallel for simd
1100 for (
long long gid = 0; gid < this->
params.
nmesh; gid++) {
1101 this->
field[gid][0] -= nbar;
1109 double alpha,
int ell,
int m
1111 fftw_complex* weight_kern =
nullptr;
1114 weight_kern = fftw_alloc_complex(particles_data.
ntotal);
1120#pragma omp parallel for
1122 for (
int pid = 0; pid < particles_data.
ntotal; pid++) {
1124 los_data[pid].
pos[0], los_data[pid].
pos[1], los_data[pid].
pos[2]
1127 std::complex<double> ylm = trvm::SphericalHarmonicCalculator::
1128 calc_reduced_spherical_harmonic(ell, m, los_);
1130 weight_kern[pid][0] = ylm.real() * particles_data[pid].w;
1131 weight_kern[pid][1] = ylm.imag() * particles_data[pid].w;
1136 fftw_free(weight_kern); weight_kern =
nullptr;
1141 weight_kern = fftw_alloc_complex(particles_rand.
ntotal);
1147#pragma omp parallel for
1149 for (
int pid = 0; pid < particles_rand.
ntotal; pid++) {
1151 los_rand[pid].
pos[0], los_rand[pid].
pos[1], los_rand[pid].
pos[2]
1154 std::complex<double> ylm = trvm::SphericalHarmonicCalculator::
1155 calc_reduced_spherical_harmonic(ell, m, los_);
1157 weight_kern[pid][0] = ylm.real() * particles_rand[pid].w;
1158 weight_kern[pid][1] = ylm.imag() * particles_rand[pid].w;
1164 fftw_free(weight_kern); weight_kern =
nullptr;
1170#pragma omp parallel for simd
1172 for (
long long gid = 0; gid < this->
params.
nmesh; gid++) {
1173 this->
field[gid][0] -= alpha * field_rand[gid][0];
1174 this->
field[gid][1] -= alpha * field_rand[gid][1];
1179#pragma omp parallel for simd
1181 for (
long long gid = 0; gid < this->
params.
nmesh; gid++) {
1182 this->field_s[gid][0] -= alpha * field_rand.field_s[gid][0];
1183 this->field_s[gid][1] -= alpha * field_rand.field_s[gid][1];
1190 double alpha,
int ell,
int m
1192 fftw_complex* weight_kern =
nullptr;
1195 weight_kern = fftw_alloc_complex(particles.
ntotal);
1201#pragma omp parallel for
1203 for (
int pid = 0; pid < particles.
ntotal; pid++) {
1204 double los_[3] = {los[pid].
pos[0], los[pid].
pos[1], los[pid].
pos[2]};
1206 std::complex<double> ylm = trvm::SphericalHarmonicCalculator::
1207 calc_reduced_spherical_harmonic(ell, m, los_);
1209 weight_kern[pid][0] = ylm.real() * particles[pid].w;
1210 weight_kern[pid][1] = ylm.imag() * particles[pid].w;
1215 fftw_free(weight_kern); weight_kern =
nullptr;
1221#pragma omp parallel for simd
1223 for (
long long gid = 0; gid < this->
params.
nmesh; gid++) {
1224 this->
field[gid][0] *= alpha;
1225 this->
field[gid][1] *= alpha;
1235 fftw_complex* weight_kern =
nullptr;
1238 weight_kern = fftw_alloc_complex(particles_data.
ntotal);
1244#pragma omp parallel for
1246 for (
int pid = 0; pid < particles_data.
ntotal; pid++) {
1248 los_data[pid].
pos[0], los_data[pid].
pos[1], los_data[pid].
pos[2]
1251 std::complex<double> ylm = trvm::SphericalHarmonicCalculator::
1252 calc_reduced_spherical_harmonic(ell, m, los_);
1254 ylm = std::conj(ylm);
1256 weight_kern[pid][0] = ylm.real() * std::pow(particles_data[pid].w, 2);
1257 weight_kern[pid][1] = ylm.imag() * std::pow(particles_data[pid].w, 2);
1262 fftw_free(weight_kern); weight_kern =
nullptr;
1267 weight_kern = fftw_alloc_complex(particles_rand.
ntotal);
1273#pragma omp parallel for
1275 for (
int pid = 0; pid < particles_rand.
ntotal; pid++) {
1277 los_rand[pid].
pos[0], los_rand[pid].
pos[1], los_rand[pid].
pos[2]
1280 std::complex<double> ylm = trvm::SphericalHarmonicCalculator::
1281 calc_reduced_spherical_harmonic(ell, m, los_);
1283 ylm = std::conj(ylm);
1285 weight_kern[pid][0] = ylm.real() * std::pow(particles_rand[pid].w, 2);
1286 weight_kern[pid][1] = ylm.imag() * std::pow(particles_rand[pid].w, 2);
1292 fftw_free(weight_kern); weight_kern =
nullptr;
1298#pragma omp parallel for simd
1300 for (
long long gid = 0; gid < this->
params.
nmesh; gid++) {
1301 this->
field[gid][0] += std::pow(alpha, 2) * field_rand[gid][0];
1302 this->
field[gid][1] += std::pow(alpha, 2) * field_rand[gid][1];
1307#pragma omp parallel for simd
1309 for (
long long gid = 0; gid < this->
params.
nmesh; gid++) {
1310 this->field_s[gid][0] += std::pow(alpha, 2) * field_rand.field_s[gid][0];
1311 this->field_s[gid][1] += std::pow(alpha, 2) * field_rand.field_s[gid][1];
1318 double alpha,
int ell,
int m
1320 fftw_complex* weight_kern =
nullptr;
1323 weight_kern = fftw_alloc_complex(particles.
ntotal);
1329#pragma omp parallel for
1331 for (
int pid = 0; pid < particles.
ntotal; pid++) {
1332 double los_[3] = {los[pid].
pos[0], los[pid].
pos[1], los[pid].
pos[2]};
1334 std::complex<double> ylm = trvm::SphericalHarmonicCalculator::
1335 calc_reduced_spherical_harmonic(ell, m, los_);
1337 ylm = std::conj(ylm);
1339 weight_kern[pid][0] = ylm.real() * std::pow(particles[pid].w, 2);
1340 weight_kern[pid][1] = ylm.imag() * std::pow(particles[pid].w, 2);
1345 fftw_free(weight_kern); weight_kern =
nullptr;
1352#pragma omp parallel for simd
1354 for (
long long gid = 0; gid < this->
params.
nmesh; gid++) {
1355 this->
field[gid][0] *= std::pow(alpha, 2);
1356 this->
field[gid][1] *= std::pow(alpha, 2);
1368 "Performing Fourier transform of '%s'.", this->
name.c_str()
1374#pragma omp parallel for simd
1376 for (
long long gid = 0; gid < this->
params.
nmesh; gid++) {
1382 if (this->plan_ext) {
1383 fftw_execute_dft(this->transform, this->
field, this->
field);
1385 fftw_execute(this->transform);
1392#pragma omp parallel for simd
1394 for (
long long gid = 0; gid < this->
params.
nmesh; gid++) {
1395 this->field_s[gid][0] *= this->
vol_cell;
1396 this->field_s[gid][1] *= this->
vol_cell;
1399 if (this->plan_ext) {
1400 fftw_execute_dft(this->transform_s, this->field_s, this->field_s);
1402 fftw_execute(this->transform_s);
1407#pragma omp parallel for collapse(3)
1412 long long idx_grid = this->ret_grid_index(i, j, k);
1429 double arg = M_PI * (m[0] + m[1] + m[2]);
1431 this->
field[idx_grid][0] +=
1432 std::cos(arg) * this->field_s[idx_grid][0]
1433 - std::sin(arg) * this->field_s[idx_grid][1]
1435 this->field[idx_grid][1] +=
1436 std::sin(arg) * this->field_s[idx_grid][0]
1437 + std::cos(arg) * this->field_s[idx_grid][1]
1440 this->field[idx_grid][0] /= 2.;
1441 this->field[idx_grid][1] /= 2.;
1451 "Performing inverse Fourier transform of '%s'.", this->
name.c_str()
1458#pragma omp parallel for simd
1460 for (
long long gid = 0; gid < this->
params.
nmesh; gid++) {
1466 if (this->plan_ext) {
1467 fftw_execute_dft(this->inv_transform, this->
field, this->
field);
1469 fftw_execute(this->inv_transform);
1482 "Applying wide-angle power-law kernel to '%s'.", this->
name.c_str()
1487 const double eps_r = 1.e-6;
1490#pragma omp parallel for collapse(3)
1495 long long idx_grid = this->ret_grid_index(i, j, k);
1498 this->get_grid_pos_vector(i, j, k, rv);
1506 this->
field[idx_grid][0] *=
1507 std::pow(r_, - this->
params.
i_wa - this->params.j_wa);
1508 this->
field[idx_grid][1] *=
1509 std::pow(r_, - this->
params.
i_wa - this->params.j_wa);
1519 "Applying assignment compensation to '%s'.", this->
name.c_str()
1526#pragma omp parallel for collapse(3)
1531 long long idx_grid = this->ret_grid_index(i, j, k);
1532 this->
field[idx_grid][0] /= this->window[idx_grid];
1533 this->
field[idx_grid][1] /= this->window[idx_grid];
1545 MeshField& field_fourier, std::vector< std::complex<double> >& ylm,
1546 double k_lower,
double k_upper,
1547 double& k_eff,
int& nmodes
1551 "Performing inverse Fourier transform to spherical harmonic weighted "
1552 "'%s' in wavenumber bands [%f, %f).",
1553 this->
name.c_str(), k_lower, k_upper
1570#pragma omp parallel for collapse(3) reduction(+:k_eff, nmodes)
1575 long long idx_grid = this->ret_grid_index(i, j, k);
1578 this->get_grid_wavevector(i, j, k, kv);
1583 if (k_lower <= k_ && k_ < k_upper) {
1584 std::complex<double> fk(
1585 field_fourier[idx_grid][0], field_fourier[idx_grid][1]
1589 fk /= this->window[idx_grid];
1592 this->
field[idx_grid][0] = (ylm[idx_grid] * fk).real();
1593 this->
field[idx_grid][1] = (ylm[idx_grid] * fk).imag();
1600 this->
field[idx_grid][0] = 0.;
1601 this->
field[idx_grid][1] = 0.;
1608 if (this->plan_ext) {
1609 fftw_execute_dft(this->inv_transform, this->
field, this->
field);
1611 fftw_execute(this->inv_transform);
1617#pragma omp parallel for simd
1619 for (
long long gid = 0; gid < this->
params.
nmesh; gid++) {
1620 this->
field[gid][0] /= double(nmodes);
1621 this->
field[gid][1] /= double(nmodes);
1624 k_eff /= double(nmodes);
1629 std::vector< std::complex<double> >& ylm,
1635 "Performing inverse Fourier transform to spherical Bessel weighted "
1636 "'%s' at separation r = %f.",
1637 this->
name.c_str(), r
1658#pragma omp for collapse(3)
1663 long long idx_grid = this->ret_grid_index(i, j, k);
1666 this->get_grid_wavevector(i, j, k, kv);
1671 std::complex<double> fk(
1672 field_fourier[idx_grid][0], field_fourier[idx_grid][1]
1675 fk /= this->window[idx_grid];
1679 this->
field[idx_grid][0] =
1680 sjl_thread.
eval(k_ * r) * (ylm[idx_grid] * fk).real() / this->
vol;
1681 this->
field[idx_grid][1] =
1682 sjl_thread.
eval(k_ * r) * (ylm[idx_grid] * fk).imag() / this->
vol;
1689 if (this->plan_ext) {
1690 fftw_execute_dft(this->inv_transform, this->
field, this->
field);
1692 fftw_execute(this->inv_transform);
1706 fftw_complex* weight =
nullptr;
1708 weight = fftw_alloc_complex(particles.
ntotal);
1714#if defined(__GNUC__) && !defined(__clang__)
1715#pragma omp parallel for simd
1717#pragma omp parallel for
1720 for (
int pid = 0; pid < particles.
ntotal; pid++) {
1721 weight[pid][0] = particles[pid].w;
1722 weight[pid][1] = 0.;
1728 fftw_free(weight); weight =
nullptr;
1734 double vol_int = 0.;
1737#if defined(__GNUC__) && !defined(__clang__)
1738#pragma omp parallel for simd reduction(+:vol_int)
1740#pragma omp parallel for reduction(+:vol_int)
1743 for (
long long gid = 0; gid < this->
params.
nmesh; gid++) {
1744 vol_int += std::pow(this->
field[gid][0], order);
1749 double norm_factor = 1. / vol_int;
1764 this->params = params;
1769 this->dr[0] = this->params.
boxsize[0] / this->params.
ngrid[0];
1770 this->dr[1] = this->params.
boxsize[1] / this->params.
ngrid[1];
1771 this->dr[2] = this->params.
boxsize[2] / this->params.
ngrid[2];
1774 this->dk[0] = 2.*M_PI / this->params.
boxsize[0];
1775 this->dk[1] = 2.*M_PI / this->params.
boxsize[1];
1776 this->dk[2] = 2.*M_PI / this->params.
boxsize[2];
1779 this->vol = this->params.
volume;
1780 this->vol_cell = this->vol / double(this->params.
nmesh);
1784 this->twopt_3d = fftw_alloc_complex(this->params.
nmesh);
1792#if defined(TRV_USE_OMP) && defined(TRV_USE_FFTWOMP)
1793 fftw_plan_with_nthreads(omp_get_max_threads());
1795 this->inv_transform = fftw_plan_dft_3d(
1796 this->params.
ngrid[0], this->params.ngrid[1], this->params.ngrid[2],
1797 this->twopt_3d, this->twopt_3d,
1798 FFTW_BACKWARD, this->params.fftw_planner_flag
1801 this->plan_ini =
true;
1806 if (this->alias_ini) {
1812 if (this->plan_ini) {
1813 fftw_destroy_plan(this->inv_transform);
1814 fftw_free(this->twopt_3d); this->twopt_3d =
nullptr;
1822 std::fill(this->
nmodes.begin(), this->nmodes.end(), 0);
1823 std::fill(this->
npairs.begin(), this->npairs.end(), 0);
1824 std::fill(this->k.begin(), this->k.end(), 0.);
1825 std::fill(this->
r.begin(), this->r.end(), 0.);
1826 std::fill(this->
sn.begin(), this->sn.end(), 0.);
1827 std::fill(this->
pk.begin(), this->pk.end(), 0.);
1828 std::fill(this->
xi.begin(), this->xi.end(), 0.);
1831void FieldStats::resize_stats(
int num_bins){
1832 this->
nmodes.resize(num_bins);
1833 this->
npairs.resize(num_bins);
1834 this->k.resize(num_bins);
1835 this->
r.resize(num_bins);
1836 this->
sn.resize(num_bins);
1837 this->
pk.resize(num_bins);
1838 this->
xi.resize(num_bins);
1846bool FieldStats::if_fields_compatible(
1847 MeshField& field_a, MeshField& field_b
1849 bool flag_compatible =
true;
1852 for (
int iaxis = 0; iaxis < 3; iaxis++) {
1854 this->params.
boxsize[iaxis] != field_a.params.boxsize[iaxis]
1855 || this->params.boxsize[iaxis] != field_b.params.boxsize[iaxis]
1856 || this->params.ngrid[iaxis] != field_a.params.ngrid[iaxis]
1857 || this->params.ngrid[iaxis] != field_b.params.ngrid[iaxis]
1859 flag_compatible =
false;
1866 this->params.
nmesh != field_a.params.nmesh
1867 || this->params.nmesh != field_b.params.nmesh
1868 || this->params.volume != field_b.params.volume
1869 || this->params.volume != field_b.params.volume
1871 flag_compatible =
false;
1874 return flag_compatible;
1877long long FieldStats::ret_grid_index(
int i,
int j,
int k) {
1878 long long idx_grid =
1879 (i *
static_cast<long long>(this->params.
ngrid[1]) + j)
1880 * this->params.
ngrid[2] +
k;
1884void FieldStats::shift_grid_indices_fourier(
int& i,
int& j,
int& k) {
1885 i = (i < this->params.
ngrid[0]/2) ? i : i - this->params.ngrid[0];
1886 j = (j < this->params.
ngrid[1]/2) ? j : j - this->params.ngrid[1];
1887 k = (
k < this->params.
ngrid[2]/2) ?
k :
k - this->params.ngrid[2];
1893 double cellsizes[3];
1894 if (binning.
space ==
"config") {
1895 cellsizes[0] = this->dr[0];
1896 cellsizes[1] = this->dr[1];
1897 cellsizes[2] = this->dr[2];
1899 if (binning.
space ==
"fourier") {
1900 cellsizes[0] = this->dk[0];
1901 cellsizes[1] = this->dk[1];
1902 cellsizes[2] = this->dk[2];
1906 "Invalid binning space: '%s'.", binning.
space.c_str()
1910 "Invalid binning space: '%s'.\n", binning.
space.c_str()
1917 int lrange_upper[3], rrange_lower[3];
1918 if (binning.
space ==
"config") {
1919 for (
int iaxis = 0; iaxis < 3; iaxis++) {
1920 lrange_upper[iaxis] = std::min(
1921 std::ceil(b * this->params.ngrid[iaxis] / this->params.boxsize[iaxis]),
1922 std::ceil(this->params.ngrid[iaxis]/2.)
1924 rrange_lower[iaxis] = std::max(
1926 this->params.ngrid[iaxis]
1927 - b * this->params.ngrid[iaxis] / this->params.boxsize[iaxis]
1929 std::floor(this->params.ngrid[iaxis]/2.)
1933 if (binning.
space ==
"fourier") {
1934 for (
int iaxis = 0; iaxis < 3; iaxis++) {
1935 lrange_upper[iaxis] = std::min(
1936 std::ceil(b * this->params.boxsize[iaxis] / (2*M_PI)),
1937 std::ceil(this->params.ngrid[iaxis]/2.)
1939 rrange_lower[iaxis] = std::max(
1941 this->params.ngrid[iaxis]
1942 - b * this->params.boxsize[iaxis] / (2*M_PI)
1944 std::floor(this->params.ngrid[iaxis]/2.)
1949 auto generate_range = [](
1950 int lrange_lower,
int lrange_upper,
int rrange_lower,
int rrange_upper
1952 std::vector<int> range_vector;
1953 if (lrange_upper < rrange_lower) {
1954 for (
int i = lrange_lower; i <= lrange_upper; i++) {
1955 range_vector.push_back(i);
1957 for (
int i = rrange_lower; i <= rrange_upper; i++) {
1958 range_vector.push_back(i);
1962 for (
int i = lrange_lower; i <= rrange_upper; i++) {
1963 range_vector.push_back(i);
1966 return range_vector;
1969 std::vector<int> i_range = generate_range(
1970 0, lrange_upper[0], rrange_lower[0], params.ngrid[0] - 1
1972 std::vector<int> j_range = generate_range(
1973 0, lrange_upper[1], rrange_lower[1], params.ngrid[1] - 1
1975 std::vector<int> k_range = generate_range(
1976 0, lrange_upper[2], rrange_lower[2], params.ngrid[2] - 1
1985#pragma omp parallel for collapse(3)
1987 for (
int i: i_range) {
1988 for (
int j: j_range) {
1989 for (
int k: k_range) {
1990 double vx = (i < this->params.ngrid[0]/2) ?
1991 i * cellsizes[0] : (i - this->params.ngrid[0]) * cellsizes[0];
1992 double vy = (j < this->params.ngrid[1]/2) ?
1993 j * cellsizes[1] : (j - this->params.ngrid[1]) * cellsizes[1];
1994 double vz = (k < this->params.ngrid[2]/2) ?
1995 k * cellsizes[2] : (k - this->params.ngrid[2]) * cellsizes[2];
2001 for (
int ibin = 0; ibin < binning.
num_bins; ibin++) {
2002 double bin_lower = binning.
bin_edges[ibin];
2003 double bin_upper = binning.
bin_edges[ibin + 1];
2004 if (bin_lower <= scale && scale < bin_upper) {
2005 binned_vectors.
indices.push_back(ibin);
2008 binned_vectors.
vecx.push_back(vx);
2009 binned_vectors.
vecy.push_back(vy);
2010 binned_vectors.
vecz.push_back(vz);
2011 binned_vectors.
count++;
2026 binned_vectors_sorted.
count = binned_vectors.
count;
2029 binned_vectors_sorted.
indices.resize(binned_vectors.
count);
2032 binned_vectors_sorted.
vecx.resize(binned_vectors.
count);
2033 binned_vectors_sorted.
vecy.resize(binned_vectors.
count);
2034 binned_vectors_sorted.
vecz.resize(binned_vectors.
count);
2039 std::vector<int> indices_sorted =
2046#pragma omp parallel for
2048 for (
int i = 0; i < binned_vectors.
count; i++) {
2049 int idx = indices_sorted[i];
2053 binned_vectors_sorted.
vecx[i] = binned_vectors.
vecx[idx];
2054 binned_vectors_sorted.
vecy[i] = binned_vectors.
vecy[idx];
2055 binned_vectors_sorted.
vecz[i] = binned_vectors.
vecz[idx];
2059 if (save_file !=
"") {
2060 std::FILE* save_fileptr = std::fopen(save_file.c_str(),
"w");
2062 save_fileptr, this->params, binned_vectors_sorted
2064 std::fclose(save_fileptr);
2068 "Check binned-vectors file for reference: %s", save_file.c_str()
2076 return binned_vectors_sorted;
2088 this->resize_stats(kbinning.
num_bins);
2091 if (!this->if_fields_compatible(field_a, field_b)) {
2094 "Input mesh fields have incompatible physical properties."
2098 "Input mesh fields have incompatible physical properties.\n"
2106 auto ret_grid_wavevector = [&field_a](
int i,
int j,
int k,
double kvec[3]) {
2107 field_a.get_grid_wavevector(i, j,
k, kvec);
2110 this->compute_shotnoise_aliasing();
2112 std::function<double(
int,
int,
int)> calc_shotnoise_aliasing = [
this](
2115 long long idx_grid = ret_grid_index(i, j,
k);
2116 return this->alias_sn[idx_grid];
2119 std::function<double(
int,
int,
int)> calc_win_pk, calc_win_sn;
2122 calc_win_pk = [&field_a, &field_b, &assignment_order](
2126 field_a.calc_assignment_window_in_fourier(i, j,
k, assignment_order)
2127 * field_b.calc_assignment_window_in_fourier(i, j,
k, assignment_order);
2129 calc_win_sn = calc_win_pk;
2131 if (this->params.
interlace ==
"false") {
2132#ifndef DBG_FLAG_NOAC
2133 calc_win_sn = calc_shotnoise_aliasing;
2134 calc_win_pk = calc_win_sn;
2136 calc_win_pk = [&field_a, &field_b, &assignment_order](
2140 field_a.calc_assignment_window_in_fourier(i, j,
k, assignment_order)
2141 * field_b.calc_assignment_window_in_fourier(i, j,
k, assignment_order);
2143 calc_win_sn = calc_shotnoise_aliasing;
2150 const int n_sample = 1e6;
2151 const double dk_sample = 1.e-5;
2152 if (kbinning.
bin_max > n_sample * dk_sample) {
2155 "Input bin range exceeds sampled range. "
2156 "Statistics in bins beyond sampled range are uncomputed."
2161 int* nmodes_sample =
new int[n_sample];
2162 double* k_sample =
new double[n_sample];
2163 double* pk_sample_real =
new double[n_sample];
2164 double* pk_sample_imag =
new double[n_sample];
2165 double* sn_sample_real =
new double[n_sample];
2166 double* sn_sample_imag =
new double[n_sample];
2167 std::complex<double>* pk_sample =
new std::complex<double>[n_sample];
2168 std::complex<double>* sn_sample =
new std::complex<double>[n_sample];
2169 for (
int i = 0; i < n_sample; i++) {
2170 nmodes_sample[i] = 0;
2172 pk_sample_real[i] = 0.;
2173 pk_sample_imag[i] = 0.;
2174 sn_sample_real[i] = 0.;
2175 sn_sample_imag[i] = 0.;
2181#pragma omp parallel for collapse(3)
2183 for (
int i = 0; i < this->params.
ngrid[0]; i++) {
2184 for (
int j = 0; j < this->params.
ngrid[1]; j++) {
2185 for (
int k = 0;
k < this->params.
ngrid[2];
k++) {
2186 long long idx_grid = ret_grid_index(i, j,
k);
2189 ret_grid_wavevector(i, j,
k, kv);
2193 int idx_k = int(k_ / dk_sample);
2194 if (0 <= idx_k && idx_k < n_sample) {
2195 std::complex<double> fa(field_a[idx_grid][0], field_a[idx_grid][1]);
2196 std::complex<double> fb(field_b[idx_grid][0], field_b[idx_grid][1]);
2198 std::complex<double> pk_mode = fa * std::conj(fb);
2199 std::complex<double> sn_mode =
2200 shotnoise_amp * calc_shotnoise_aliasing(i, j,
k);
2203 double win_pk = calc_win_pk(i, j,
k);
2204 double win_sn = calc_win_sn(i, j,
k);
2210 std::complex<double> ylm = trvm::SphericalHarmonicCalculator::
2211 calc_reduced_spherical_harmonic(ell, m, kv);
2216 double pk_mode_real = pk_mode.real();
2217 double pk_mode_imag = pk_mode.imag();
2218 double sn_mode_real = sn_mode.real();
2219 double sn_mode_imag = sn_mode.imag();
2223 nmodes_sample[idx_k]++;
2225 k_sample[idx_k] += k_;
2227 pk_sample_real[idx_k] += pk_mode_real;
2229 pk_sample_imag[idx_k] += pk_mode_imag;
2231 sn_sample_real[idx_k] += sn_mode_real;
2233 sn_sample_imag[idx_k] += sn_mode_imag;
2239 for (
int i = 0; i < n_sample; i++) {
2240 pk_sample[i] = pk_sample_real[i] +
trvm::M_I * pk_sample_imag[i];
2241 sn_sample[i] = sn_sample_real[i] +
trvm::M_I * sn_sample_imag[i];
2245 for (
int ibin = 0; ibin < kbinning.
num_bins; ibin++) {
2246 double k_lower = kbinning.
bin_edges[ibin];
2247 double k_upper = kbinning.
bin_edges[ibin + 1];
2248 for (
int i = 0; i < n_sample; i++) {
2249 double k_ = i * dk_sample;
2250 if (k_lower <= k_ && k_ < k_upper) {
2251 this->
nmodes[ibin] += nmodes_sample[i];
2252 this->k[ibin] += k_sample[i];
2253 this->
pk[ibin] += pk_sample[i];
2254 this->
sn[ibin] += sn_sample[i];
2258 if (this->
nmodes[ibin] != 0) {
2259 this->k[ibin] /= double(this->
nmodes[ibin]);
2260 this->
pk[ibin] /= double(this->
nmodes[ibin]);
2261 this->
sn[ibin] /= double(this->
nmodes[ibin]);
2264 this->
pk[ibin] = 0.;
2265 this->
sn[ibin] = 0.;
2269 delete[] nmodes_sample;
2271 delete[] pk_sample_real;
2272 delete[] pk_sample_imag;
2273 delete[] sn_sample_real;
2274 delete[] sn_sample_imag;
2283 this->resize_stats(rbinning.
num_bins);
2287 if (!this->if_fields_compatible(field_a, field_b)) {
2290 "Input mesh fields have incompatible physical properties."
2294 "Input mesh fields have incompatible physical properties.\n"
2302 auto ret_grid_pos_vector = [&field_a](
int i,
int j,
int k,
double rvec[3]) {
2303 field_a.get_grid_pos_vector(i, j,
k, rvec);
2306 this->compute_shotnoise_aliasing();
2308 std::function<double(
int,
int,
int)> calc_shotnoise_aliasing = [
this](
2311 long long idx_grid = ret_grid_index(i, j,
k);
2312 return this->alias_sn[idx_grid];
2315 std::function<double(
int,
int,
int)> calc_win_pk, calc_win_sn;
2318 calc_win_pk = [&field_a, &field_b, &assignment_order](
2322 field_a.calc_assignment_window_in_fourier(i, j,
k, assignment_order)
2323 * field_b.calc_assignment_window_in_fourier(i, j,
k, assignment_order);
2325 calc_win_sn = calc_win_pk;
2327 if (this->params.
interlace ==
"false") {
2328#ifndef DBG_FLAG_NOAC
2329 calc_win_sn = calc_shotnoise_aliasing;
2330 calc_win_pk = calc_win_sn;
2332 calc_win_pk = [&field_a, &field_b, &assignment_order](
2336 field_a.calc_assignment_window_in_fourier(i, j,
k, assignment_order)
2337 * field_b.calc_assignment_window_in_fourier(i, j,
k, assignment_order);
2339 calc_win_sn = calc_shotnoise_aliasing;
2356 for (
int i = 0; i < this->params.
ngrid[0]; i++) {
2357 for (
int j = 0; j < this->params.
ngrid[1]; j++) {
2358 for (
int k = 0;
k < this->params.
ngrid[2];
k++) {
2359 long long idx_grid = ret_grid_index(i, j,
k);
2361 std::complex<double> fa(field_a[idx_grid][0], field_a[idx_grid][1]);
2362 std::complex<double> fb(field_b[idx_grid][0], field_b[idx_grid][1]);
2364 std::complex<double> pk_mode = fa * std::conj(fb);
2365 std::complex<double> sn_mode =
2366 shotnoise_amp * calc_shotnoise_aliasing(i, j,
k);
2369 double win_pk = calc_win_pk(i, j,
k);
2370 double win_sn = calc_win_sn(i, j,
k);
2377 this->twopt_3d[idx_grid][0] = pk_mode.real() / this->vol;
2378 this->twopt_3d[idx_grid][1] = pk_mode.imag() / this->vol;
2384 if (this->plan_ini) {
2385 fftw_execute(this->inv_transform);
2387 fftw_execute_dft(field_a.inv_transform, twopt_3d, twopt_3d);
2394 const int n_sample = 1e6;
2395 const double dr_sample = 1.e-1;
2396 if (rbinning.
bin_max > n_sample * dr_sample) {
2399 "Input bin range exceeds sampled range. "
2400 "Statistics in bins beyond sampled range are uncomputed."
2405 int* npairs_sample =
new int[n_sample];
2406 double* r_sample =
new double[n_sample];
2407 double* xi_sample_real =
new double[n_sample];
2408 double* xi_sample_imag =
new double[n_sample];
2409 std::complex<double>* xi_sample =
new std::complex<double>[n_sample];
2410 for (
int i = 0; i < n_sample; i++) {
2411 npairs_sample[i] = 0;
2413 xi_sample_real[i] = 0.;
2414 xi_sample_imag[i] = 0.;
2420#pragma omp parallel for collapse(3)
2422 for (
int i = 0; i < this->params.
ngrid[0]; i++) {
2423 for (
int j = 0; j < this->params.
ngrid[1]; j++) {
2424 for (
int k = 0;
k < this->params.
ngrid[2];
k++) {
2425 long long idx_grid = ret_grid_index(i, j,
k);
2428 ret_grid_pos_vector(i, j,
k, rv);
2432 int idx_r = int(r_ / dr_sample);
2433 if (0 <= idx_r && idx_r < n_sample) {
2434 std::complex<double> xi_pair(
2435 this->twopt_3d[idx_grid][0], this->twopt_3d[idx_grid][1]
2439 std::complex<double> ylm = trvm::SphericalHarmonicCalculator::
2440 calc_reduced_spherical_harmonic(ell, m, rv);
2444 double xi_pair_real = xi_pair.real();
2445 double xi_pair_imag = xi_pair.imag();
2449 npairs_sample[idx_r]++;
2451 r_sample[idx_r] += r_;
2453 xi_sample_real[idx_r] += xi_pair_real;
2455 xi_sample_imag[idx_r] += xi_pair_imag;
2461 for (
int i = 0; i < n_sample; i++) {
2462 xi_sample[i] = xi_sample_real[i] +
trvm::M_I * xi_sample_imag[i];
2466 for (
int ibin = 0; ibin < rbinning.
num_bins; ibin++) {
2467 double r_lower = rbinning.
bin_edges[ibin];
2468 double r_upper = rbinning.
bin_edges[ibin + 1];
2469 for (
int i = 0; i < n_sample; i++) {
2470 double r_ = i * dr_sample;
2471 if (r_lower <= r_ && r_ < r_upper) {
2472 this->
npairs[ibin] += npairs_sample[i];
2473 this->
r[ibin] += r_sample[i];
2474 this->
xi[ibin] += xi_sample[i];
2478 if (this->
npairs[ibin] != 0) {
2479 this->
r[ibin] /= double(this->
npairs[ibin]);
2480 this->
xi[ibin] /= double(this->
npairs[ibin]);
2484 this->
xi[ibin] = 0.;
2488 delete[] npairs_sample;
2490 delete[] xi_sample_real;
2491 delete[] xi_sample_imag;
2497 std::vector< std::complex<double> >& ylm_a,
2498 std::vector< std::complex<double> >& ylm_b,
2499 std::complex<double> shotnoise_amp,
2506 this->resize_stats(rbinning.
num_bins);
2510 if (!this->if_fields_compatible(field_a, field_b)) {
2513 "Input mesh fields have incompatible physical properties."
2517 "Input mesh fields have incompatible physical properties.\n"
2525 auto ret_grid_pos_vector = [&field_a](
int i,
int j,
int k,
double rvec[3]) {
2526 field_a.get_grid_pos_vector(i, j,
k, rvec);
2529 this->compute_shotnoise_aliasing();
2531 std::function<double(
int,
int,
int)> calc_shotnoise_aliasing = [
this](
2534 long long idx_grid = ret_grid_index(i, j,
k);
2535 return this->alias_sn[idx_grid];
2538 std::function<double(
int,
int,
int)> calc_win_pk, calc_win_sn;
2541 calc_win_pk = [&field_a, &field_b, &assignment_order](
2545 field_a.calc_assignment_window_in_fourier(i, j,
k, assignment_order)
2546 * field_b.calc_assignment_window_in_fourier(i, j,
k, assignment_order);
2548 calc_win_sn = calc_win_pk;
2550 if (this->params.
interlace ==
"false") {
2551#ifndef DBG_FLAG_NOAC
2552 calc_win_sn = calc_shotnoise_aliasing;
2553 calc_win_pk = calc_win_sn;
2555 calc_win_pk = [&field_a, &field_b, &assignment_order](
2559 field_a.calc_assignment_window_in_fourier(i, j,
k, assignment_order)
2560 * field_b.calc_assignment_window_in_fourier(i, j,
k, assignment_order);
2562 calc_win_sn = calc_shotnoise_aliasing;
2580#pragma omp parallel for collapse(3)
2582 for (
int i = 0; i < this->params.
ngrid[0]; i++) {
2583 for (
int j = 0; j < this->params.
ngrid[1]; j++) {
2584 for (
int k = 0;
k < this->params.
ngrid[2];
k++) {
2585 long long idx_grid = ret_grid_index(i, j,
k);
2587 std::complex<double> fa(field_a[idx_grid][0], field_a[idx_grid][1]);
2588 std::complex<double> fb(field_b[idx_grid][0], field_b[idx_grid][1]);
2590 std::complex<double> pk_mode = fa * std::conj(fb);
2591 std::complex<double> sn_mode =
2592 shotnoise_amp * calc_shotnoise_aliasing(i, j,
k);
2595 double win_pk = calc_win_pk(i, j,
k);
2596 double win_sn = calc_win_sn(i, j,
k);
2603 this->twopt_3d[idx_grid][0] = pk_mode.real() / this->vol;
2604 this->twopt_3d[idx_grid][1] = pk_mode.imag() / this->vol;
2610 if (this->plan_ini) {
2611 fftw_execute(this->inv_transform);
2613 fftw_execute_dft(field_a.inv_transform, twopt_3d, twopt_3d);
2620 const int n_sample = 1e5;
2621 const double dr_sample = 1.;
2623 int* npairs_sample =
new int[n_sample];
2624 double* r_sample =
new double[n_sample];
2625 double* xi_sample_real =
new double[n_sample];
2626 double* xi_sample_imag =
new double[n_sample];
2627 std::complex<double>* xi_sample =
new std::complex<double>[n_sample];
2628 for (
int i = 0; i < n_sample; i++) {
2629 npairs_sample[i] = 0;
2631 xi_sample_real[i] = 0.;
2632 xi_sample_imag[i] = 0.;
2638#pragma omp parallel for collapse(3)
2640 for (
int i = 0; i < this->params.
ngrid[0]; i++) {
2641 for (
int j = 0; j < this->params.
ngrid[1]; j++) {
2642 for (
int k = 0;
k < this->params.
ngrid[2];
k++) {
2643 long long idx_grid = ret_grid_index(i, j,
k);
2646 ret_grid_pos_vector(i, j,
k, rv);
2650 int idx_r = int(r_ / dr_sample);
2651 if (0 <= idx_r && idx_r < n_sample) {
2652 std::complex<double> xi_pair(
2653 this->twopt_3d[idx_grid][0], this->twopt_3d[idx_grid][1]
2657 xi_pair *= ylm_a[idx_grid] * ylm_b[idx_grid];
2659 double xi_pair_real = xi_pair.real();
2660 double xi_pair_imag = xi_pair.imag();
2664 npairs_sample[idx_r]++;
2666 r_sample[idx_r] += r_;
2668 xi_sample_real[idx_r] += xi_pair_real;
2670 xi_sample_imag[idx_r] += xi_pair_imag;
2676 for (
int i = 0; i < n_sample; i++) {
2677 xi_sample[i] = xi_sample_real[i] +
trvm::M_I * xi_sample_imag[i];
2681 for (
int ibin = 0; ibin < rbinning.
num_bins; ibin++) {
2682 double r_lower = rbinning.
bin_edges[ibin];
2683 double r_upper = rbinning.
bin_edges[ibin + 1];
2684 for (
int i = 0; i < n_sample; i++) {
2685 double r_ = i * dr_sample;
2686 if (r_lower <= r_ && r_ < r_upper) {
2687 this->
npairs[ibin] += npairs_sample[i];
2688 this->
r[ibin] += r_sample[i];
2689 this->
xi[ibin] += xi_sample[i];
2693 if (this->
npairs[ibin] != 0) {
2694 this->
r[ibin] /= double(this->
npairs[ibin]);
2695 this->
xi[ibin] /= double(this->
npairs[ibin]);
2698 this->
xi[ibin] = 0.;
2703 double norm_factors = 1 / this->vol_cell
2704 * std::pow(-1, this->params.
ell1 + this->params.ell2);
2706 for (
int ibin = 0; ibin < rbinning.
num_bins; ibin++) {
2707 if (this->
npairs[ibin] != 0) {
2708 this->
xi[ibin] *= norm_factors / double(this->
npairs[ibin]);
2713 delete[] npairs_sample;
2715 delete[] xi_sample_real;
2716 delete[] xi_sample_imag;
2720std::complex<double> \
2721FieldStats::compute_uncoupled_shotnoise_for_bispec_per_bin(
2723 std::vector< std::complex<double> >& ylm_a,
2724 std::vector< std::complex<double> >& ylm_b,
2726 std::complex<double> shotnoise_amp,
2727 double k_a,
double k_b
2731 "Computing uncoupled shot noise for bispectrum "
2732 "in wavenumber bin [%f, %f).",
2739 if (!this->if_fields_compatible(field_a, field_b)) {
2742 "Input mesh fields have incompatible physical properties."
2746 "Input mesh fields have incompatible physical properties.\n"
2754 auto ret_grid_pos_vector = [&field_a](
int i,
int j,
int k,
double rvec[3]) {
2755 field_a.get_grid_pos_vector(i, j, k, rvec);
2758 this->compute_shotnoise_aliasing();
2760 std::function<double(
int,
int,
int)> calc_shotnoise_aliasing = [
this](
2763 long long idx_grid = ret_grid_index(i, j, k);
2764 return this->alias_sn[idx_grid];
2767 std::function<double(
int,
int,
int)> calc_win_pk, calc_win_sn;
2768 int assignment_order = this->params.assignment_order;
2769 if (this->params.interlace ==
"true") {
2770 calc_win_pk = [&field_a, &field_b, &assignment_order](
2774 field_a.calc_assignment_window_in_fourier(i, j, k, assignment_order)
2775 * field_b.calc_assignment_window_in_fourier(i, j, k, assignment_order);
2777 calc_win_sn = calc_win_pk;
2779 if (this->params.interlace ==
"false") {
2780#ifndef DBG_FLAG_NOAC
2781 calc_win_sn = calc_shotnoise_aliasing;
2782 calc_win_pk = calc_win_sn;
2784 calc_win_pk = [&field_a, &field_b, &assignment_order](
2788 field_a.calc_assignment_window_in_fourier(i, j, k, assignment_order)
2789 * field_b.calc_assignment_window_in_fourier(i, j, k, assignment_order);
2791 calc_win_sn = calc_shotnoise_aliasing;
2809#pragma omp parallel for collapse(3)
2811 for (
int i = 0; i < this->params.ngrid[0]; i++) {
2812 for (
int j = 0; j < this->params.ngrid[1]; j++) {
2813 for (
int k = 0; k < this->params.ngrid[2]; k++) {
2814 long long idx_grid = ret_grid_index(i, j, k);
2816 std::complex<double> fa(field_a[idx_grid][0], field_a[idx_grid][1]);
2817 std::complex<double> fb(field_b[idx_grid][0], field_b[idx_grid][1]);
2819 std::complex<double> pk_mode = fa * std::conj(fb);
2820 std::complex<double> sn_mode =
2821 shotnoise_amp * calc_shotnoise_aliasing(i, j, k);
2824 double win_pk = calc_win_pk(i, j, k);
2825 double win_sn = calc_win_sn(i, j, k);
2832 this->twopt_3d[idx_grid][0] = pk_mode.real() / this->vol;
2833 this->twopt_3d[idx_grid][1] = pk_mode.imag() / this->vol;
2839 if (this->plan_ini) {
2840 fftw_execute(this->inv_transform);
2842 fftw_execute_dft(field_a.inv_transform, twopt_3d, twopt_3d);
2848 double S_ij_k_real = 0., S_ij_k_imag = 0.;
2851#pragma omp parallel reduction(+:S_ij_k_real, S_ij_k_imag)
2859#pragma omp for collapse(3)
2861 for (
int i = 0; i < this->params.ngrid[0]; i++) {
2862 for (
int j = 0; j < this->params.ngrid[1]; j++) {
2863 for (
int k = 0; k < this->params.ngrid[2]; k++) {
2864 long long idx_grid = ret_grid_index(i, j, k);
2867 ret_grid_pos_vector(i, j, k, rv);
2871 double ja = sj_a_thread.
eval(k_a * r_);
2872 double jb = sj_b_thread.
eval(k_b * r_);
2874 std::complex<double> S_ij_k_3d(
2875 this->twopt_3d[idx_grid][0], this->twopt_3d[idx_grid][1]
2878 S_ij_k_3d *= ja * jb * ylm_a[idx_grid] * ylm_b[idx_grid];
2880 double S_ij_k_3d_real = S_ij_k_3d.real();
2881 double S_ij_k_3d_imag = S_ij_k_3d.imag();
2883 S_ij_k_real += S_ij_k_3d_real;
2884 S_ij_k_imag += S_ij_k_3d_imag;
2890 std::complex<double> S_ij_k(S_ij_k_real, S_ij_k_imag);
2892 S_ij_k *= this->vol_cell;
2902std::function<double(
int,
int,
int)> FieldStats::ret_calc_shotnoise_aliasing()
2905 return [
this](
int i,
int j,
int k) {
2906 return calc_shotnoise_aliasing_ngp(i, j,
k);
2910 return [
this](
int i,
int j,
int k) {
2911 return calc_shotnoise_aliasing_cic(i, j,
k);
2915 return [
this](
int i,
int j,
int k) {
2916 return calc_shotnoise_aliasing_tsc(i, j,
k);
2920 return [
this](
int i,
int j,
int k) {
2921 return calc_shotnoise_aliasing_pcs(i, j,
k);
2927 "Invalid assignment scheme: '%s'.", this->params.
assignment.c_str()
2931 "Invalid assignment scheme: '%s'.\n", this->params.
assignment.c_str()
2935void FieldStats::get_shotnoise_aliasing_sin2(
2936 int i,
int j,
int k,
double& cx2,
double& cy2,
double& cz2
2938 this->shift_grid_indices_fourier(i, j,
k);
2940 double u_x = M_PI * i / double(this->params.
ngrid[0]);
2941 double u_y = M_PI * j / double(this->params.
ngrid[1]);
2942 double u_z = M_PI *
k / double(this->params.
ngrid[2]);
2944 cx2 = (i != 0) ? std::sin(u_x) * std::sin(u_x) : 0.;
2945 cy2 = (j != 0) ? std::sin(u_y) * std::sin(u_y) : 0.;
2946 cz2 = (
k != 0) ? std::sin(u_z) * std::sin(u_z) : 0.;
2949double FieldStats::calc_shotnoise_aliasing_ngp(
int i,
int j,
int k) {
2953double FieldStats::calc_shotnoise_aliasing_cic(
int i,
int j,
int k) {
2954 double cx2, cy2, cz2;
2955 this->get_shotnoise_aliasing_sin2(i, j,
k, cx2, cy2, cz2);
2957 return (1. - 2./3. * cx2) * (1. - 2./3. * cy2) * (1. - 2./3. * cz2);
2960double FieldStats::calc_shotnoise_aliasing_tsc(
int i,
int j,
int k) {
2961 double cx2, cy2, cz2;
2962 this->get_shotnoise_aliasing_sin2(i, j,
k, cx2, cy2, cz2);
2964 return (1. - cx2 + 2./15. * cx2 * cx2)
2965 * (1. - cy2 + 2./15. * cy2 * cy2)
2966 * (1. - cz2 + 2./15. * cz2 * cz2);
2969double FieldStats::calc_shotnoise_aliasing_pcs(
int i,
int j,
int k) {
2970 double cx2, cy2, cz2;
2971 this->get_shotnoise_aliasing_sin2(i, j,
k, cx2, cy2, cz2);
2973 return (1. - 4./3. * cx2 + 2./5. * cx2 * cx2 - 4./315. * cx2 * cx2 * cx2)
2974 * (1. - 4./3. * cy2 + 2./5. * cy2 * cy2 - 4./315. * cy2 * cy2 * cy2)
2975 * (1. - 4./3. * cz2 + 2./5. * cz2 * cz2 - 4./315. * cz2 * cz2 * cz2);
2978void FieldStats::compute_shotnoise_aliasing() {
2979 if (this->alias_ini) {
return;}
2983 "Computing shot noise aliasing function in Fourier space "
2984 "for assignment order %d.",
2989 this->alias_sn.resize(this->params.
nmesh, 0.);
2997 std::function<double(
int,
int,
int)> calc_shotnoise_aliasing =
2998 this->ret_calc_shotnoise_aliasing();
3001#pragma omp parallel for collapse(3)
3003 for (
int i = 0; i < this->params.
ngrid[0]; i++) {
3004 for (
int j = 0; j < this->params.
ngrid[1]; j++) {
3005 for (
int k = 0;
k < this->params.
ngrid[2];
k++) {
3006 long long idx_grid = ret_grid_index(i, j,
k);
3007 this->alias_sn[idx_grid] = calc_shotnoise_aliasing(i, j,
k);
3012 this->alias_ini =
true;
Isotropic coordinate binning.
std::vector< double > bin_edges
bin edges
std::vector< double > bin_centres
bin centres
std::string space
coordinate space
double bin_max
highest bin edge
int num_bins
number of bins
std::vector< std::complex< double > > sn
shot-noise power in bins
void reset_stats()
Reset two-point statistics.
std::vector< std::complex< double > > pk
pseudo power spectrum in bins
void compute_uncoupled_shotnoise_for_3pcf(MeshField &field_a, MeshField &field_b, std::vector< std::complex< double > > &ylm_a, std::vector< std::complex< double > > &ylm_b, std::complex< double > shotnoise_amp, trv::Binning &rbinning)
Compute binned uncoupled three-point correlation function shot noise.
~FieldStats()
Destruct two-point statistics.
std::vector< int > nmodes
number of wavevector modes in bins
FieldStats(trv::ParameterSet ¶ms, bool plan_ini=true)
Construct pseudo two-point statistics.
std::vector< double > k
average wavenumber in bins
std::vector< std::complex< double > > xi
pseudo two-point correlation function in bins
std::vector< int > npairs
number of separation pairs in bins
void compute_ylm_wgtd_2pt_stats_in_fourier(MeshField &field_a, MeshField &field_b, std::complex< double > shotnoise_amp, int ell, int m, trv::Binning &kbinning)
Compute binned two-point statistics in Fourier space.
trv::BinnedVectors record_binned_vectors(trv::Binning &binning, const std::string &save_file)
Record binned vectors given a binning scheme.
void compute_ylm_wgtd_2pt_stats_in_config(MeshField &field_a, MeshField &field_b, std::complex< double > shotnoise_amp, int ell, int m, trv::Binning &rbinning)
Compute binned two-point statistics in configuration space.
Discretely sampled field on a mesh grid from particle catalogues.
trv::ParameterSet params
parameter set
void inv_fourier_transform()
Inverse Fourier transform the (FFT-transformed) field.
void inv_fourier_transform_sjl_ylm_wgtd_field(MeshField &field_fourier, std::vector< std::complex< double > > &ylm, trvm::SphericalBesselCalculator &sjl, double r)
Inverse Fourier transform a field weighted by the spherical Bessel function and reduced spherical ha...
std::string name
field name
void assign_weighted_field_to_mesh(ParticleCatalogue &particles, fftw_complex *weights)
Assign a weighted field to a mesh by interpolation scheme.
double calc_grid_based_powlaw_norm(ParticleCatalogue &particles, int order)
void reset_density_field()
(Re-)initialise the complex field (and its shadow) on mesh.
void inv_fourier_transform_ylm_wgtd_field_band_limited(MeshField &field_fourier, std::vector< std::complex< double > > &ylm, double k_band, double dk_band, double &k_eff, int &nmodes)
Inverse Fourier transform a field weighted by the reduced spherical harmonics restricted to a wavenu...
double dr[3]
grid size in each dimension
void fourier_transform()
Fourier transform the field.
void compute_ylm_wgtd_field(ParticleCatalogue &particles_data, ParticleCatalogue &particles_rand, LineOfSight *los_data, LineOfSight *los_rand, double alpha, int ell, int m)
Compute the weighted field (fluctuations) further weighted by the reduced spherical harmonics.
void compute_unweighted_field(ParticleCatalogue &particles)
Compute the unweighted field.
void apply_wide_angle_pow_law_kernel()
Apply wide-angle correction kernel in configuration space.
void compute_ylm_wgtd_quad_field(ParticleCatalogue &particles_data, ParticleCatalogue &particles_rand, LineOfSight *los_data, LineOfSight *los_rand, double alpha, int ell, int m)
Compute the quadratic weighted field (fluctuations) further weighted by the reduced spherical harmoni...
fftw_complex * field
complex field on mesh
MeshField(trv::ParameterSet ¶ms, bool plan_ini=true, const std::string &name="mesh-field")
Construct the mesh field.
void compute_unweighted_field_fluctuations_insitu(ParticleCatalogue &particles)
Compute the unweighted field fluctuations.
double dk[3]
fundamental wavenumber in each dimension
~MeshField()
Destruct the mesh field.
double vol_cell
mesh grid cell volume
void apply_assignment_compensation()
Apply compensation needed after Fourier transform for assignment schemes.
const fftw_complex & operator[](long long gid)
Return mesh field grid cell value.
long long nmesh
number of mesh grid cells
std::string assignment
mesh assignment scheme: {"ngp", "cic", "tsc" (default), "pcs"}
int i_wa
first order of the wide-angle correction term
std::string fftw_scheme
FFTW scheme: {"estimate", "measure" (default), "patient"}.
double volume
box volume (in Mpc^3/h^3)
int ell1
spherical degree associated with the first wavevector
std::string fftw_wisdom_file_b
backward-transform wisdom file path
std::string interlace
interlacing switch: {"true"/"on", "false"/"off" (default)}
int assignment_order
order of the assignment scheme
std::string fftw_wisdom_file_f
derived FFTW wisdom file paths
int ngrid[3]
grid number in each dimension
double boxsize[3]
box size (in Mpc/h) in each dimension
std::string use_fftw_wisdom
use FFTW wisdom: {"false" (default), <path-to-dir>}
double pos_min[3]
minimum values of particle coordinates
int ntotal
total number of particles
double pos_max[3]
maximum values of particle coordinates
Interpolated spherical Bessel function of the first kind.
double eval(double x)
Evaluate the interpolated function.
Exception raised when the data to be operated on are invalid.
Exception raised when parameters are invalid.
void info(const char *fmt_string,...)
Emit a information-level message.
void debug(const char *fmt_string,...)
Emit a debugging-level message.
void error(const char *fmt_string,...)
Emit a warning-level message.
void warn(const char *fmt_string,...)
Emit a warning-level message.
void reset_level(LogLevel level)
Reset the logger threshold level.
Mesh field (with one-point statistics) and pseudo two-point statistics.
std::vector< int > get_sorted_indices(std::vector< int > sorting_vector)
Get the sorted indices.
void print_binned_vectors_to_file(std::FILE *fileptr, trv::ParameterSet ¶ms, trv::BinnedVectors &binned_vectors)
Print binned vectors to a file.
double get_vec3d_magnitude(std::vector< double > vec)
Return the magnitude of a 3-d vector.
const std::complex< double > M_I
imaginary unit
double gbytesMem
current memory usage in gibibytes
double size_in_gb(long long num)
Return size in gibibytes.
void update_maxmem()
Update the maximum memory usage estimate.
bool fftw_wisdom_b_imported
wisdom import status for backward transform
bool fftw_wisdom_f_imported
wisdom import status for forward transform
Logger logger
default logger (at NSET logging level)
float count_grid
number of grids
int count_fft
number of FFTs
int count_cgrid
number of 3-d complex grids
int count_rgrid
number of 3-d real grids
int count_ifft
number of IFFTs
void update_maxcntgrid()
Update the maximum 3-d grid counts.
std::vector< double > vecy
y-components
std::vector< double > vecz
z-components
int count
number of vectors
int num_bins
number of bins
std::vector< double > upper_edges
upper bin edges
std::vector< double > lower_edges
lower bin edges
std::vector< double > vecx
x-components
std::vector< int > indices
bin indices
double pos[3]
3-d position vector