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