Triumvirate C++ API 0.5.0
Three-point clustering measurements in large-scale structure analyses.
Loading...
Searching...
No Matches
twopt.cpp
Go to the documentation of this file.
1// Copyright (C) [GPLv3 Licence]
2//
3// This file is part of the Triumvirate program. See the COPYRIGHT
4// and LICENCE files at the top-level directory of this distribution
5// for details of copyright and licensing.
6//
7// This program is free software: you can redistribute it and/or modify it
8// under the terms of the GNU General Public License as published by
9// the Free Software Foundation, either version 3 of the License, or
10// (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15// See the GNU General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License
18// along with this program. If not, see <https://www.gnu.org/licenses/>.
19
27#include "twopt.hpp"
28
31const double eps_norm = 1.e-5;
33
34namespace trvs = trv::sys;
35namespace trvm = trv::maths;
36
37namespace trv {
38
39// ***********************************************************************
40// Coupling coefficients
41// ***********************************************************************
42
43double calc_coupling_coeff_2pt(int ell, int ELL, int m, int M) {
44 return (2*ell + 1) * (2*ELL + 1)
45 * trvm::wigner_3j(ell, 0, ELL, 0, 0, 0)
46 * trvm::wigner_3j(ell, 0, ELL, m, 0, M);
47}
48
49
50// ***********************************************************************
51// Normalisation
52// ***********************************************************************
53
55 ParticleCatalogue& particles, double alpha
56) {
57 if (particles.pdata == nullptr) {
58 if (trvs::currTask == 0) {
59 trvs::logger.error("Particle data are uninitialised.");
60 }
61 throw trvs::InvalidDataError("Particle data are uninitialised.\n");
62 }
63
64 double norm = 0.; // I₂
65
66#ifdef TRV_USE_OMP
67#if defined(__GNUC__) && !defined(__clang__)
68#pragma omp parallel for simd reduction(+:norm)
69#else // !__GNUC__ || __clang__
70#pragma omp parallel for reduction(+:norm)
71#endif // __GNUC__ && !__clang__
72#endif // TRV_USE_OMP
73 for (int pid = 0; pid < particles.ntotal; pid++) {
74 norm += particles[pid].ws
75 * particles[pid].nz * std::pow(particles[pid].wc, 2);
76 }
77
78 if (norm == 0.) {
79 if (trvs::currTask == 0) {
81 "Particle 'nz' values appear to be all zeros. "
82 "Check the input catalogue contains valid 'nz' field."
83 );
84 }
86 "Particle 'nz' values appear to be all zeros. "
87 "Check the input catalogue contains valid 'nz' field.\n"
88 );
89 }
90
91 double norm_factor = 1. / (alpha * norm); // 1/I₂
92
93 return norm_factor;
94}
95
97 trv::ParticleCatalogue& particles, trv::ParameterSet& params, double alpha
98) {
99 trv::MeshField catalogue_mesh(params, false, "`catalogue_mesh`");
100
101 double norm_factor =
102 catalogue_mesh.calc_grid_based_powlaw_norm(particles, 2);
103
104 norm_factor /= std::pow(alpha, 2);
105
106 return norm_factor;
107}
108
110 trv::ParticleCatalogue& particles_data,
111 trv::ParticleCatalogue& particles_rand,
112 trv::ParameterSet& params, double alpha
113) {
114 // Assign particles to mesh.
115 trv::MeshField mesh_data(params, false, "`mesh_data`");
116 trv::MeshField mesh_rand(params, false, "`mesh_rand`");
117
118 fftw_complex* weight_data = nullptr;
119 weight_data = fftw_alloc_complex(particles_data.ntotal);
120
121 fftw_complex* weight_rand = nullptr;
122 weight_rand = fftw_alloc_complex(particles_rand.ntotal);
123
127
128#ifdef TRV_USE_OMP
129#if defined(__GNUC__) && !defined(__clang__)
130#pragma omp parallel for simd
131#else // !__GNUC__ || __clang__
132#pragma omp parallel for
133#endif // __GNUC__ && !__clang__
134#endif // TRV_USE_OMP
135 for (int pid = 0; pid < particles_data.ntotal; pid++) {
136 weight_data[pid][0] = particles_data[pid].w;
137 weight_data[pid][1] = 0.;
138 }
139
140#ifdef TRV_USE_OMP
141#if defined(__GNUC__) && !defined(__clang__)
142#pragma omp parallel for simd
143#else // !__GNUC__ || __clang__
144#pragma omp parallel for
145#endif // __GNUC__ && !__clang__
146#endif // TRV_USE_OMP
147 for (int pid = 0; pid < particles_rand.ntotal; pid++) {
148 weight_rand[pid][0] = particles_rand[pid].w;
149 weight_rand[pid][1] = 0.;
150 }
151
152 mesh_data.assign_weighted_field_to_mesh(particles_data, weight_data);
153 mesh_rand.assign_weighted_field_to_mesh(particles_rand, weight_rand);
154
155 fftw_free(weight_data); weight_data = nullptr;
156 fftw_free(weight_rand); weight_rand = nullptr;
157
160
161 // Calculate normalisation.
162 double norm = 0.;
163
164#ifdef TRV_USE_OMP
165#if defined(__GNUC__) && !defined(__clang__)
166#pragma omp parallel for simd reduction(+:norm)
167#else // !__GNUC__ || __clang__
168#pragma omp parallel for reduction(+:norm)
169#endif // __GNUC__ && !__clang__
170#endif // TRV_USE_OMP
171 for (long long gid = 0; gid < params.nmesh; gid++) {
172 norm += mesh_data.field[gid][0] * mesh_rand.field[gid][0];
173 }
174
175 double vol_cell = params.volume / double(params.nmesh);
176
177 double norm_factor = 1. / (alpha * vol_cell * norm); // 1/I₂
178
179 return norm_factor;
180}
181
183 trv::ParticleCatalogue& particles_data,
184 trv::ParticleCatalogue& particles_rand,
185 trv::ParameterSet& params, double alpha,
186 double padding, double cellsize, const std::string& assignment
187) {
188 // Modify parameter set for normalisation.
189 trv::ParameterSet params_norm(params);
190
191 double boxsize_norm = (1. + padding) * std::max(
192 *std::max_element(particles_data.pos_span, particles_data.pos_span + 3),
193 *std::max_element(particles_rand.pos_span, particles_rand.pos_span + 3)
194 );
195
196 int ngrid_norm = std::ceil(boxsize_norm / cellsize);
197
198 ngrid_norm += ngrid_norm % 2; // ensure even
199 boxsize_norm = ngrid_norm * cellsize; // enforce cell size
200
201 for (int iaxis = 0; iaxis < 3; iaxis++) {
202 params_norm.boxsize[iaxis] = boxsize_norm;
203 params_norm.ngrid[iaxis] = ngrid_norm;
204 }
205
206 params_norm.assignment = assignment;
207
208 params_norm.validate();
209
210 // Reuse existing overloaded method.
211 double norm_factor = calc_powspec_normalisation_from_meshes(
212 particles_data, particles_rand, params_norm, alpha
213 );
214
215 return norm_factor;
216}
217
218
219// ***********************************************************************
220// Shot noise
221// ***********************************************************************
222
224 ParticleCatalogue& particles, double alpha
225) {
226 if (particles.pdata == nullptr) {
227 if (trvs::currTask == 0) {
228 trvs::logger.error("Particle data are uninitialised.");
229 }
230 throw trvs::InvalidDataError("Particle data are uninitialised.\n");
231 }
232
233 double shotnoise = 0.;
234
235#ifdef TRV_USE_OMP
236#if defined(__GNUC__) && !defined(__clang__)
237#pragma omp parallel for simd reduction(+:shotnoise)
238#else // !__GNUC__ || __clang__
239#pragma omp parallel for reduction(+:shotnoise)
240#endif // __GNUC__ && !__clang__
241#endif // TRV_USE_OMP
242 for (int pid = 0; pid < particles.ntotal; pid++) {
243 shotnoise +=
244 std::pow(particles[pid].ws, 2) * std::pow(particles[pid].wc, 2);
245 }
246
247 return shotnoise;
248}
249
251 ParticleCatalogue& particles_data, ParticleCatalogue& particles_rand,
252 LineOfSight* los_data, LineOfSight* los_rand,
253 double alpha, int ell, int m
254) {
255 double sn_data_real = 0., sn_data_imag = 0.;
256
257#ifdef TRV_USE_OMP
258#pragma omp parallel for reduction(+:sn_data_real, sn_data_imag)
259#endif
260 for (int pid = 0; pid < particles_data.ntotal; pid++) {
261 double los_[3] = {
262 los_data[pid].pos[0], los_data[pid].pos[1], los_data[pid].pos[2]
263 };
264
265 std::complex<double> ylm = trvm::SphericalHarmonicCalculator::
266 calc_reduced_spherical_harmonic(ell, m, los_);
267
268 std::complex<double> sn_part = ylm * std::pow(particles_data[pid].w, 2);
269 double sn_part_real = sn_part.real();
270 double sn_part_imag = sn_part.imag();
271
272 sn_data_real += sn_part_real;
273 sn_data_imag += sn_part_imag;
274 }
275
276 std::complex<double> sn_data(sn_data_real, sn_data_imag);
277
278 double sn_rand_real = 0., sn_rand_imag = 0.;
279
280#ifdef TRV_USE_OMP
281#pragma omp parallel for reduction(+:sn_rand_real, sn_rand_imag)
282#endif
283 for (int pid = 0; pid < particles_rand.ntotal; pid++) {
284 double los_[3] = {
285 los_rand[pid].pos[0], los_rand[pid].pos[1], los_rand[pid].pos[2]
286 };
287
288 std::complex<double> ylm = trvm::SphericalHarmonicCalculator::
289 calc_reduced_spherical_harmonic(ell, m, los_);
290
291 std::complex<double> sn_part = ylm * std::pow(particles_rand[pid].w, 2);
292 double sn_part_real = sn_part.real();
293 double sn_part_imag = sn_part.imag();
294
295 sn_rand_real += sn_part_real;
296 sn_rand_imag += sn_part_imag;
297 }
298
299 std::complex<double> sn_rand(sn_rand_real, sn_rand_imag);
300
301 return sn_data + std::pow(alpha, 2) * sn_rand;
302}
303
305 ParticleCatalogue& particles, LineOfSight* los,
306 double alpha, int ell, int m
307) {
308 double sn_real = 0., sn_imag = 0.;
309
310#ifdef TRV_USE_OMP
311#pragma omp parallel for reduction(+:sn_real, sn_imag)
312#endif
313 for (int pid = 0; pid < particles.ntotal; pid++) {
314 double los_[3] = {los[pid].pos[0], los[pid].pos[1], los[pid].pos[2]};
315
316 std::complex<double> ylm = trvm::SphericalHarmonicCalculator::
317 calc_reduced_spherical_harmonic(ell, m, los_);
318
319 std::complex<double> sn_part = ylm * std::pow(particles[pid].w, 2);
320 double sn_part_real = sn_part.real();
321 double sn_part_imag = sn_part.imag();
322
323 sn_real += sn_part_real;
324 sn_imag += sn_part_imag;
325 }
326
327 std::complex<double> sn(sn_real, sn_imag);
328
329 return std::pow(alpha, 2) * sn;
330}
331
332
333// ***********************************************************************
334// Full statistics
335// ***********************************************************************
336
337// STYLE: Standard naming convention is not always followed for
338// intermediary quantities in the functions below.
339
341 ParticleCatalogue& catalogue_data, ParticleCatalogue& catalogue_rand,
342 LineOfSight* los_data, LineOfSight* los_rand,
343 trv::ParameterSet& params, trv::Binning& kbinning,
344 double norm_factor
345) {
347
348 if (trvs::currTask == 0) {
350 "Computing power spectrum from paired survey-type catalogues..."
351 );
352 }
353
354 // ---------------------------------------------------------------------
355 // Set-up
356 // ---------------------------------------------------------------------
357
358 // Set up input.
359 double alpha = catalogue_data.wstotal / catalogue_rand.wstotal;
360 int ell1 = params.ELL;
361
362 // Set up output.
363 int* nmodes_save = new int[kbinning.num_bins];
364 double* k_save = new double[kbinning.num_bins];
365 std::complex<double>* pk_save = new std::complex<double>[kbinning.num_bins];
366 std::complex<double>* sn_save = new std::complex<double>[kbinning.num_bins];
367 for (int ibin = 0; ibin < kbinning.num_bins; ibin++) {
368 nmodes_save[ibin] = 0;
369 k_save[ibin] = 0.;
370 pk_save[ibin] = 0.;
371 sn_save[ibin] = 0.;
372 } // likely redundant but safe
373
374 // ---------------------------------------------------------------------
375 // Measurement
376 // ---------------------------------------------------------------------
377
378#if defined(TRV_USE_OMP) && defined(TRV_USE_FFTWOMP)
379 fftw_init_threads();
380#endif // TRV_USE_OMP && TRV_USE_FFTWOMP
381
382 MeshField dn_00(params, true, "`dn_00`"); // δn_00(k)
384 catalogue_data, catalogue_rand, los_data, los_rand, alpha, 0, 0
385 );
386 dn_00.fourier_transform();
387
388 FieldStats stats_2pt(params);
389
390 for (int M_ = - params.ELL; M_ <= params.ELL; M_++) {
391 MeshField dn_LM(params, true, "`dn_LM`"); // δn_LM(k)
393 catalogue_data, catalogue_rand, los_data, los_rand, alpha, params.ELL, M_
394 );
395 dn_LM.fourier_transform();
396
397 std::complex<double> sn_amp = trv::calc_ylm_wgtd_shotnoise_amp_for_powspec(
398 catalogue_data, catalogue_rand, los_data, los_rand, alpha, params.ELL, M_
399 ); // \bar{N}_LM(k)
400
401 // Compute quantity equivalent to (-1)^m₁ δᴰ_{m₁, -M} which, after
402 // being summed over m₁, agrees with Hand et al. (2017) [1704.02357].
403 for (int m1 = - ell1; m1 <= ell1; m1++) {
404 double coupling = calc_coupling_coeff_2pt(ell1, params.ELL, m1, M_);
405 if (std::fabs(coupling) < trvm::eps_coupling) {continue;}
406
408 dn_LM, dn_00, sn_amp, ell1, m1, kbinning
409 );
410
411 for (int ibin = 0; ibin < kbinning.num_bins; ibin++) {
412 pk_save[ibin] += coupling * stats_2pt.pk[ibin];
413 sn_save[ibin] += coupling * stats_2pt.sn[ibin];
414 }
415
416 if (M_ == 0 && m1 == 0) {
417 for (int ibin = 0; ibin < kbinning.num_bins; ibin++) {
418 nmodes_save[ibin] = stats_2pt.nmodes[ibin];
419 k_save[ibin] = stats_2pt.k[ibin];
420 }
421 }
422 }
423
424 if (trvs::currTask == 0) {
425 trvs::logger.stat("Power spectrum term computed at order M = %d.", M_);
426 }
427 }
428
429 // ---------------------------------------------------------------------
430 // Results
431 // ---------------------------------------------------------------------
432
433 trv::PowspecMeasurements powspec_out;
434 for (int ibin = 0; ibin < kbinning.num_bins; ibin++) {
435 powspec_out.kbin.push_back(kbinning.bin_centres[ibin]);
436 powspec_out.keff.push_back(k_save[ibin]);
437 powspec_out.nmodes.push_back(nmodes_save[ibin]);
438 powspec_out.pk_raw.push_back(norm_factor * pk_save[ibin]);
439 powspec_out.pk_shot.push_back(norm_factor * sn_save[ibin]);
440 }
441 powspec_out.dim = kbinning.num_bins;
442
443 delete[] nmodes_save; delete[] k_save; delete[] pk_save; delete[] sn_save;
444
445 if (trvs::currTask == 0) {
447 "... computed power spectrum from paired survey-type catalogues."
448 );
449 }
450
451 return powspec_out;
452}
453
455 ParticleCatalogue& catalogue_data, ParticleCatalogue& catalogue_rand,
456 LineOfSight* los_data, LineOfSight* los_rand,
457 trv::ParameterSet& params, trv::Binning& rbinning,
458 double norm_factor
459) {
461
462 if (trvs::currTask == 0) {
464 "Computing two-point correlation function from "
465 "paired survey-type catalogues..."
466 );
467 }
468
469 // ---------------------------------------------------------------------
470 // Set-up
471 // ---------------------------------------------------------------------
472
473 // Set up input.
474 double alpha = catalogue_data.wstotal / catalogue_rand.wstotal;
475 int ell1 = params.ELL;
476
477 // Set up output.
478 int* npairs_save = new int[rbinning.num_bins];
479 double* r_save = new double[rbinning.num_bins];
480 std::complex<double>* xi_save = new std::complex<double>[rbinning.num_bins];
481 for (int ibin = 0; ibin < rbinning.num_bins; ibin++) {
482 npairs_save[ibin] = 0;
483 r_save[ibin] = 0.;
484 xi_save[ibin] = 0.;
485 } // likely redundant but safe
486
487 // ---------------------------------------------------------------------
488 // Measurement
489 // ---------------------------------------------------------------------
490
491#if defined(TRV_USE_OMP) && defined(TRV_USE_FFTWOMP)
492 fftw_init_threads();
493#endif // TRV_USE_OMP && TRV_USE_FFTWOMP
494
495 MeshField dn_00(params, true, "`dn_00`"); // δn_00(k)
497 catalogue_data, catalogue_rand, los_data, los_rand, alpha, 0, 0
498 );
499 dn_00.fourier_transform();
500
501 FieldStats stats_2pt(params);
502
503 for (int M_ = - params.ELL; M_ <= params.ELL; M_++) {
504 MeshField dn_LM(params, true, "`dn_LM`"); // δn_LM(k)
506 catalogue_data, catalogue_rand, los_data, los_rand, alpha, params.ELL, M_
507 );
508 dn_LM.fourier_transform();
509
510 std::complex<double> sn_amp = trv::calc_ylm_wgtd_shotnoise_amp_for_powspec(
511 catalogue_data, catalogue_rand, los_data, los_rand, alpha, params.ELL, M_
512 ); // \bar{N}_LM(k)
513
514 // Compute quantity equivalent to (-1)^m₁ δᴰ_{m₁, -M} which, after
515 // being summed over m₁, agrees with Hand et al. (2017) [1704.02357].
516 for (int m1 = - ell1; m1 <= ell1; m1++) {
517 double coupling = calc_coupling_coeff_2pt(ell1, params.ELL, m1, M_);
518 if (std::fabs(coupling) < trvm::eps_coupling) {continue;}
519
521 dn_LM, dn_00, sn_amp, ell1, m1, rbinning
522 );
523
524 for (int ibin = 0; ibin < rbinning.num_bins; ibin++) {
525 xi_save[ibin] += coupling * stats_2pt.xi[ibin];
526 }
527
528 if (M_ == 0 && m1 == 0) {
529 for (int ibin = 0; ibin < rbinning.num_bins; ibin++) {
530 npairs_save[ibin] = stats_2pt.npairs[ibin];
531 r_save[ibin] = stats_2pt.r[ibin];
532 }
533 }
534 }
535
536 if (trvs::currTask == 0) {
538 "Two-point correlation function term computed at order M = %d.", M_
539 );
540 }
541 }
542
543 // ---------------------------------------------------------------------
544 // Results
545 // ---------------------------------------------------------------------
546
547 trv::TwoPCFMeasurements corrfunc_out;
548 for (int ibin = 0; ibin < rbinning.num_bins; ibin++) {
549 corrfunc_out.rbin.push_back(rbinning.bin_centres[ibin]);
550 corrfunc_out.reff.push_back(r_save[ibin]);
551 corrfunc_out.npairs.push_back(npairs_save[ibin]);
552 corrfunc_out.xi.push_back(norm_factor * xi_save[ibin]);
553 }
554 corrfunc_out.dim = rbinning.num_bins;
555
556 delete[] npairs_save; delete[] r_save; delete[] xi_save;
557
558 if (trvs::currTask == 0) {
560 "... computed two-point correlation function "
561 "from paired survey-type catalogues."
562 );
563 }
564
565 return corrfunc_out;
566}
567
569 ParticleCatalogue& catalogue_data,
570 trv::ParameterSet& params, trv::Binning kbinning,
571 double norm_factor
572) {
574
575 if (trvs::currTask == 0) {
577 "Computing power spectrum from a periodic-box simulation-type catalogue "
578 "in the global plane-parallel approximation."
579 );
580 }
581
582 // ---------------------------------------------------------------------
583 // Set-up
584 // ---------------------------------------------------------------------
585
586 int* nmodes_save = new int[kbinning.num_bins];
587 double* k_save = new double[kbinning.num_bins];
588 std::complex<double>* pk_save = new std::complex<double>[kbinning.num_bins];
589 std::complex<double>* sn_save = new std::complex<double>[kbinning.num_bins];
590 for (int ibin = 0; ibin < kbinning.num_bins; ibin++) {
591 nmodes_save[ibin] = 0;
592 k_save[ibin] = 0.;
593 pk_save[ibin] = 0.;
594 sn_save[ibin] = 0.;
595 } // likely redundant but safe
596
597 // Check input normalisation matches expectation.
598 double norm = double(catalogue_data.ntotal) * double(catalogue_data.ntotal)
599 / params.volume;
600 if (std::fabs(1 - norm * norm_factor) > eps_norm) {
601 if (trvs::currTask == 0) {
603 "Power spectrum normalisation input differs from "
604 "expected value for an unweight field in a periodic box."
605 );
606 }
607 }
608
609 // ---------------------------------------------------------------------
610 // Measurement
611 // ---------------------------------------------------------------------
612
613#if defined(TRV_USE_OMP) && defined(TRV_USE_FFTWOMP)
614 fftw_init_threads();
615#endif // TRV_USE_OMP && TRV_USE_FFTWOMP
616
617 // Compute power spectrum.
618 MeshField dn(params, true, "`dn`"); // δn(k)
621
622 std::complex<double> sn_amp = double(catalogue_data.ntotal); // \bar{N}
623
624 // Under the global plane-parallel approximation, δᴰ_{M0} enforces
625 // M = 0 for any spherical-harmonic-weighted field fluctuations.
626 FieldStats stats_2pt(params);
628 dn, dn, sn_amp, params.ELL, 0, kbinning
629 );
630
631 for (int ibin = 0; ibin < kbinning.num_bins; ibin++) {
632 nmodes_save[ibin] = stats_2pt.nmodes[ibin];
633 k_save[ibin] = stats_2pt.k[ibin];
634 pk_save[ibin] += double(2*params.ELL + 1) * stats_2pt.pk[ibin];
635 sn_save[ibin] += double(2*params.ELL + 1) * stats_2pt.sn[ibin];
636 }
637
638 // ---------------------------------------------------------------------
639 // Results
640 // ---------------------------------------------------------------------
641
642 // Fill in output struct.
643 trv::PowspecMeasurements powspec_out;
644 for (int ibin = 0; ibin < kbinning.num_bins; ibin++) {
645 powspec_out.kbin.push_back(kbinning.bin_centres[ibin]);
646 powspec_out.keff.push_back(k_save[ibin]);
647 powspec_out.nmodes.push_back(nmodes_save[ibin]);
648 powspec_out.pk_raw.push_back(norm_factor * pk_save[ibin]);
649 powspec_out.pk_shot.push_back(norm_factor * sn_save[ibin]);
650 }
651 powspec_out.dim = kbinning.num_bins;
652
653 delete[] nmodes_save; delete[] k_save; delete[] pk_save; delete[] sn_save;
654
655 if (trvs::currTask == 0) {
657 "... computed power spectrum "
658 "from a periodic-box simulation-type catalogue "
659 "in the global plane-parallel approximation."
660 );
661 }
662
663 return powspec_out;
664}
665
667 ParticleCatalogue& catalogue_data,
668 trv::ParameterSet& params, trv::Binning& rbinning,
669 double norm_factor
670) {
672
673 if (trvs::currTask == 0) {
675 "Computing two-point correlation function "
676 "from a periodic-box simulation-type catalogue "
677 "in the global plane-parallel approximation."
678 );
679 }
680
681 // ---------------------------------------------------------------------
682 // Set-up
683 // ---------------------------------------------------------------------
684
685 int* npairs_save = new int[rbinning.num_bins];
686 double* r_save = new double[rbinning.num_bins];
687 std::complex<double>* xi_save = new std::complex<double>[rbinning.num_bins];
688 for (int ibin = 0; ibin < rbinning.num_bins; ibin++) {
689 npairs_save[ibin] = 0;
690 r_save[ibin] = 0.;
691 xi_save[ibin] = 0.;
692 } // likely redundant but safe
693
694 // Check input normalisation matches expectation.
695 double norm = double(catalogue_data.ntotal) * double(catalogue_data.ntotal)
696 / params.volume;
697 if (std::fabs(1 - norm * norm_factor) > eps_norm) {
698 if (trvs::currTask == 0) {
700 "Two-point correlation function normalisation input differs from "
701 "expected value for an unweight field in a periodic box."
702 );
703 }
704 }
705
706 // ---------------------------------------------------------------------
707 // Measurement
708 // ---------------------------------------------------------------------
709
710#if defined(TRV_USE_OMP) && defined(TRV_USE_FFTWOMP)
711 fftw_init_threads();
712#endif // TRV_USE_OMP && TRV_USE_FFTWOMP
713
714 // Compute 2PCF.
715 MeshField dn(params, true, "`dn`"); // δn(k)
718
719 std::complex<double> sn_amp = double(catalogue_data.ntotal); // \bar{N}
720
721 // Under the global plane-parallel approximation, δᴰ_{M0} enforces
722 // M = 0 for any spherical-harmonic-weighted field fluctuations.
723 FieldStats stats_2pt(params);
725 dn, dn, sn_amp, params.ELL, 0, rbinning
726 );
727
728 for (int ibin = 0; ibin < rbinning.num_bins; ibin++) {
729 npairs_save[ibin] = stats_2pt.npairs[ibin];
730 r_save[ibin] = stats_2pt.r[ibin];
731 xi_save[ibin] += double(2*params.ELL + 1) * stats_2pt.xi[ibin];
732 }
733
734 // ---------------------------------------------------------------------
735 // Results
736 // ---------------------------------------------------------------------
737
738 // Fill in output struct.
739 trv::TwoPCFMeasurements corrfunc_out;
740 for (int ibin = 0; ibin < rbinning.num_bins; ibin++) {
741 corrfunc_out.rbin.push_back(rbinning.bin_centres[ibin]);
742 corrfunc_out.reff.push_back(r_save[ibin]);
743 corrfunc_out.npairs.push_back(npairs_save[ibin]);
744 corrfunc_out.xi.push_back(norm_factor * xi_save[ibin]);
745 }
746 corrfunc_out.dim = rbinning.num_bins;
747
748 delete[] npairs_save; delete[] r_save; delete[] xi_save;
749
750 if (trvs::currTask == 0) {
752 "... computed two-point correlation function "
753 "from a periodic-box simulation-type catalogue "
754 "in the global plane-parallel approximation."
755 );
756 }
757
758 return corrfunc_out;
759}
760
762 trv::ParticleCatalogue& catalogue_rand, trv::LineOfSight* los_rand,
763 trv::ParameterSet& params, trv::Binning rbinning,
764 double alpha, double norm_factor
765) {
767
768 if (trvs::currTask == 0) {
770 "Computing two-point correlation function window "
771 "from a random catalogue..."
772 );
773 }
774
775 // ---------------------------------------------------------------------
776 // Set-up
777 // ---------------------------------------------------------------------
778
779 // Set up input.
780 int ell1 = params.ELL;
781
782 // Set up output.
783 int* npairs_save = new int[rbinning.num_bins];
784 double* r_save = new double[rbinning.num_bins];
785 std::complex<double>* xi_save = new std::complex<double>[rbinning.num_bins];
786 for (int ibin = 0; ibin < rbinning.num_bins; ibin++) {
787 npairs_save[ibin] = 0;
788 r_save[ibin] = 0.;
789 xi_save[ibin] = 0.;
790 } // likely redundant but safe
791
792 // ---------------------------------------------------------------------
793 // Measurement
794 // ---------------------------------------------------------------------
795
796#if defined(TRV_USE_OMP) && defined(TRV_USE_FFTWOMP)
797 fftw_init_threads();
798#endif // TRV_USE_OMP && TRV_USE_FFTWOMP
799
800 MeshField dn_00(params, true, "`dn_00`");
801 dn_00.compute_ylm_wgtd_field(catalogue_rand, los_rand, alpha, 0, 0);
802 dn_00.fourier_transform(); // δn_00(k)
803
804 FieldStats stats_2pt(params);
805
806 for (int M_ = - params.ELL; M_ <= params.ELL; M_++) {
807 MeshField dn_LM(params, true, "`dn_LM`");
809 catalogue_rand, los_rand, alpha, params.ELL, M_
810 );
811 dn_LM.fourier_transform(); // δn_LM(k)
812
813 std::complex<double> sn_amp = trv::calc_ylm_wgtd_shotnoise_amp_for_powspec(
814 catalogue_rand, los_rand, alpha, params.ELL, M_
815 ); // \bar{N}_LM(k)
816
817 // Calculate equivalent to (-1)^m₁ δᴰ_{m₁, -M} which, after being
818 // summed over m₁, agrees with Hand et al. (2017) [1704.02357].
819 for (int m1 = - ell1; m1 <= ell1; m1++) {
820 double coupling = trv::calc_coupling_coeff_2pt(ell1, params.ELL, m1, M_);
821 if (std::fabs(coupling) < trvm::eps_coupling) {continue;}
822
824 dn_LM, dn_00, sn_amp, ell1, m1, rbinning
825 );
826
827 for (int ibin = 0; ibin < rbinning.num_bins; ibin++) {
828 xi_save[ibin] += coupling * stats_2pt.xi[ibin];
829 }
830
831 if (M_ == 0 && m1 == 0) {
832 for (int ibin = 0; ibin < rbinning.num_bins; ibin++) {
833 npairs_save[ibin] = stats_2pt.npairs[ibin];
834 r_save[ibin] = stats_2pt.r[ibin];
835 }
836 }
837 }
838
839 if (trvs::currTask == 0) {
841 "Two-point correlation function window term computed at order M = %d.",
842 M_
843 );
844 }
845 }
846
847 // ---------------------------------------------------------------------
848 // Results
849 // ---------------------------------------------------------------------
850
851 trv::TwoPCFWindowMeasurements corrfunc_win_out;
852 for (int ibin = 0; ibin < rbinning.num_bins; ibin++) {
853 corrfunc_win_out.rbin.push_back(rbinning.bin_centres[ibin]);
854 corrfunc_win_out.reff.push_back(r_save[ibin]);
855 corrfunc_win_out.npairs.push_back(npairs_save[ibin]);
856 corrfunc_win_out.xi.push_back(norm_factor * xi_save[ibin]);
857 }
858 corrfunc_win_out.dim = rbinning.num_bins;
859
860 delete[] npairs_save; delete[] r_save; delete[] xi_save;
861
862 if (trvs::currTask == 0) {
864 "... computed two-point correlation function window "
865 "from a random catalogue."
866 );
867 }
868
869 return corrfunc_win_out;
870}
871
872} // namespace trv
Isotropic coordinate binning.
Definition dataobjs.hpp:56
std::vector< double > bin_centres
bin centres
Definition dataobjs.hpp:64
int num_bins
number of bins
Definition dataobjs.hpp:62
Field (pseudo-two-point) statistics.
Definition field.hpp:550
std::vector< std::complex< double > > sn
shot-noise power in bins
Definition field.hpp:557
std::vector< std::complex< double > > pk
pseudo power spectrum in bins
Definition field.hpp:559
std::vector< double > r
Definition field.hpp:555
std::vector< int > nmodes
number of wavevector modes in bins
Definition field.hpp:552
std::vector< double > k
average wavenumber in bins
Definition field.hpp:554
std::vector< std::complex< double > > xi
pseudo two-point correlation function in bins
Definition field.hpp:561
std::vector< int > npairs
number of separation pairs in bins
Definition field.hpp:553
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.
Definition field.cpp:2084
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.
Definition field.cpp:2279
Discretely sampled field on a mesh grid from particle catalogues.
Definition field.hpp:68
void assign_weighted_field_to_mesh(ParticleCatalogue &particles, fftw_complex *weights)
Assign a weighted field to a mesh by interpolation scheme.
Definition field.cpp:427
double calc_grid_based_powlaw_norm(ParticleCatalogue &particles, int order)
Definition field.cpp:1702
void fourier_transform()
Fourier transform the field.
Definition field.cpp:1365
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.
Definition field.cpp:1106
fftw_complex * field
complex field on mesh
Definition field.hpp:72
void compute_unweighted_field_fluctuations_insitu(ParticleCatalogue &particles)
Compute the unweighted field fluctuations.
Definition field.cpp:1089
Parameter set.
int validate()
Validate parameters.
long long nmesh
number of mesh grid cells
std::string assignment
mesh assignment scheme: {"ngp", "cic", "tsc" (default), "pcs"}
int ELL
spherical degree associated with the line of sight
double volume
box volume (in Mpc^3/h^3)
int ngrid[3]
grid number in each dimension
double boxsize[3]
box size (in Mpc/h) in each dimension
Particle catalogue.
Definition particles.hpp:68
double pos_span[3]
span of particle coordinates
Definition particles.hpp:80
ParticleData * pdata
particle data
Definition particles.hpp:72
int ntotal
total number of particles
Definition particles.hpp:74
double wstotal
total sample weight of particles
Definition particles.hpp:76
Exception raised when the data to be operated on are invalid.
Definition monitor.hpp:456
void error(const char *fmt_string,...)
Emit a warning-level message.
Definition monitor.cpp:282
void warn(const char *fmt_string,...)
Emit a warning-level message.
Definition monitor.cpp:273
void stat(const char *fmt_string,...)
Emit a status-level message.
Definition monitor.cpp:255
void reset_level(LogLevel level)
Reset the logger threshold level.
Definition monitor.cpp:156
const double eps_coupling
zero-tolerance for Wigner 3-j coupling coefficients
Definition maths.cpp:163
double wigner_3j(int j1, int j2, int j3, int m1, int m2, int m3)
Calculate Wigner 3-j symbol.
Definition maths.cpp:165
double gbytesMem
current memory usage in gibibytes
Definition monitor.cpp:46
double size_in_gb(long long num)
Return size in gibibytes.
Definition monitor.hpp:101
void update_maxmem()
Update the maximum memory usage estimate.
Definition monitor.cpp:68
Logger logger
default logger (at NSET logging level)
int currTask
current task
Definition monitor.cpp:44
double calc_powspec_shotnoise_from_particles(ParticleCatalogue &particles, double alpha=1.)
Calculate particle-based power spectrum shot noise level.
Definition twopt.cpp:223
trv::TwoPCFWindowMeasurements compute_corrfunc_window(trv::ParticleCatalogue &catalogue_rand, trv::LineOfSight *los_rand, trv::ParameterSet &params, trv::Binning rbinning, double alpha, double norm_factor)
Compute two-point correlation function window from a random catalogue and optionally save the results...
Definition twopt.cpp:761
trv::TwoPCFMeasurements compute_corrfunc(ParticleCatalogue &catalogue_data, ParticleCatalogue &catalogue_rand, LineOfSight *los_data, LineOfSight *los_rand, trv::ParameterSet &params, trv::Binning &rbinning, double norm_factor)
Compute two-point correlation function from paired survey-type catalogues.
Definition twopt.cpp:454
double calc_coupling_coeff_2pt(int ell, int ELL, int m, int M)
Calculate the coupling coefficient for spherical-harmonic components of full two-point statistics.
Definition twopt.cpp:43
std::complex< double > calc_ylm_wgtd_shotnoise_amp_for_powspec(ParticleCatalogue &particles_data, ParticleCatalogue &particles_rand, LineOfSight *los_data, LineOfSight *los_rand, double alpha, int ell, int m)
Calculate power spectrum shot noise weighted by reduced spherical harmonics.
Definition twopt.cpp:250
trv::PowspecMeasurements compute_powspec_in_gpp_box(ParticleCatalogue &catalogue_data, trv::ParameterSet &params, trv::Binning kbinning, double norm_factor)
Compute power spectrum in a periodic box in the global plane-parallel approximation.
Definition twopt.cpp:568
double calc_powspec_normalisation_from_particles(ParticleCatalogue &particles, double alpha=1.)
Calculate particle-based power spectrum normalisation.
Definition twopt.cpp:54
double calc_powspec_normalisation_from_mesh(trv::ParticleCatalogue &particles, trv::ParameterSet &params, double alpha=1.)
Calculate mesh-based power spectrum normalisation.
Definition twopt.cpp:96
trv::PowspecMeasurements compute_powspec(ParticleCatalogue &catalogue_data, ParticleCatalogue &catalogue_rand, LineOfSight *los_data, LineOfSight *los_rand, trv::ParameterSet &params, trv::Binning &kbinning, double norm_factor)
Compute power spectrum from paired survey-type catalogues.
Definition twopt.cpp:340
trv::TwoPCFMeasurements compute_corrfunc_in_gpp_box(ParticleCatalogue &catalogue_data, trv::ParameterSet &params, trv::Binning &rbinning, double norm_factor)
Compute two-point correlation function in a periodic box in the global plane-parallel approximation.
Definition twopt.cpp:666
double calc_powspec_normalisation_from_meshes(trv::ParticleCatalogue &particles_data, trv::ParticleCatalogue &particles_rand, trv::ParameterSet &params, double alpha)
Calculate power spectrum normalisation from mixed meshes.
Definition twopt.cpp:109
Line-of-sight vector.
Definition dataobjs.hpp:184
double pos[3]
3-d position vector
Definition dataobjs.hpp:185
Power spectrum measurements.
Definition dataobjs.hpp:200
int dim
dimension of data vector
Definition dataobjs.hpp:201
std::vector< double > kbin
central wavenumber in bins
Definition dataobjs.hpp:202
std::vector< int > nmodes
Definition dataobjs.hpp:204
std::vector< std::complex< double > > pk_shot
power spectrum shot noise
Definition dataobjs.hpp:208
std::vector< double > keff
effective wavenumber in bins
Definition dataobjs.hpp:203
std::vector< std::complex< double > > pk_raw
power spectrum raw measurements (with normalisation and shot noise)
Definition dataobjs.hpp:206
Two-point correlation function measurements.
Definition dataobjs.hpp:215
std::vector< double > rbin
central separation in bins
Definition dataobjs.hpp:217
std::vector< int > npairs
Definition dataobjs.hpp:219
int dim
dimension of data vector
Definition dataobjs.hpp:216
std::vector< std::complex< double > > xi
two-point correlation function measurements (with normalisation)
Definition dataobjs.hpp:221
std::vector< double > reff
effective separation in bins
Definition dataobjs.hpp:218
Two-point correlation function window measurements.
Definition dataobjs.hpp:228
std::vector< std::complex< double > > xi
Definition dataobjs.hpp:235
int dim
dimension of data vector
Definition dataobjs.hpp:229
std::vector< double > reff
effective separation in bins
Definition dataobjs.hpp:231
std::vector< double > rbin
central separation in bins
Definition dataobjs.hpp:230
std::vector< int > npairs
Definition dataobjs.hpp:232
Two-point statistic computations.