Triumvirate C++ API 0.5.0.post1.dev301+g026f21751
Three-point clustering measurements in large-scale structure analyses.
Loading...
Searching...
No Matches
particles.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 "particles.hpp"
30
31namespace trvs = trv::sys;
32
33namespace trv {
34
35// ***********************************************************************
36// Life cycle
37// ***********************************************************************
38
40 if (verbose >= 0) {
41 trvs::logger.reset_level(verbose);
42 }
43}
44
46
48 // Check the total number of particles.
49 if (num <= 0) {
50 if (trvs::currTask == 0) {
51 trvs::logger.error("Number of particles is non-positive.");
52 }
54 "Number of particles is non-positive."
55 );
56 }
57
58 this->ntotal = num;
59
60 // Renew particle data.
61 this->reset_particles();
62
63 this->pdata = new ParticleData[this->ntotal];
66}
67
71
73 // Free particle data.
74 if (this->pdata != nullptr) {
75 delete[] this->pdata; this->pdata = nullptr;
77 }
78}
79
80
81// ***********************************************************************
82// Operators & reserved methods
83// ***********************************************************************
84
86 return this->pdata[pid];
87}
88
89
90// ***********************************************************************
91// Data I/O
92// ***********************************************************************
93
95 const std::string& catalogue_filepath,
96 const std::string& catalogue_columns,
97 const std::string& catalogue_dataset,
98 double volume
99) {
100 if (!(this->source.empty())) {
101 if (trvs::currTask == 0) {
102 trvs::logger.error(
103 "Catalogue already loaded from another source: %s.", this->source.c_str()
104 );
105 }
107 "Catalogue already loaded from another source: %s.",
108 this->source.c_str()
109 );
110 }
111 this->source = "extfile:" + catalogue_filepath;
112
113 // Check for HDF5 file extension.
114 bool is_hdf5 = (
115 trvs::has_extension(catalogue_filepath, ".h5")
116 || trvs::has_extension(catalogue_filepath, ".hdf5")
117 );
118#ifndef TRV_USE_H5
119 if (is_hdf5) {
120 if (trvs::currTask == 0) {
121 trvs::logger.error(
122 "HDF5 file format is not supported in this build: %s",
123 catalogue_filepath.c_str()
124 );
125 }
127 "HDF5 file format is not supported in this build: %s",
128 catalogue_filepath.c_str()
129 );
130 }
131#endif // !TRV_USE_H5
132
133 // ---------------------------------------------------------------------
134 // Columns & fields
135 // ---------------------------------------------------------------------
136
137 // CAVEAT: Hard-coded ordered column names.
138 const std::vector<std::string> names_ordered = {
139 "x", "y", "z", "nz", "ws", "wc"
140 };
141
142 std::istringstream iss(catalogue_columns);
143 std::vector<std::string> colnames;
144 std::string name;
145 while (std::getline(iss, name, ',')) {
146 colnames.push_back(name);
147 }
148
149 // CAVEAT: Default -1 index as a flag for unfound column names.
150 std::vector<int> name_indices(names_ordered.size(), -1);
151 for (std::size_t iname = 0; iname < names_ordered.size(); iname++) {
152 std::size_t col_idx = static_cast<std::size_t>(std::distance(
153 colnames.begin(),
154 std::find(colnames.begin(), colnames.end(), names_ordered[iname])
155 ));
156 if (col_idx < colnames.size()) {
157 name_indices[iname] = col_idx;
158 }
159 }
160
161 // ---------------------------------------------------------------------
162 // Data reading
163 // ---------------------------------------------------------------------
164
165 std::vector<std::string> catalogue_subfilepaths = trvs::split_string(
166 catalogue_filepath, trvs::fn_delimiter
167 );
168
169 if (is_hdf5) {
170#ifdef TRV_USE_H5
171 try {
172 int nentry = 0;
173 std::vector<std::vector<double>> ctlg_data;
174 for (const auto& ctlg_subfilepath : catalogue_subfilepaths) {
175 HighFive::File ctlg_subfile(
176 ctlg_subfilepath, HighFive::File::ReadOnly
177 );
178
179 // Get dataset (assumed to be the first if unspecified)
180 // from the file.
181 std::vector<std::string> obj_names = ctlg_subfile.listObjectNames();
182 if (obj_names.empty()) {
183 if (trvs::currTask == 0) {
184 trvs::logger.error(
185 "No objects found in HDF5 file: %s",
186 catalogue_filepath.c_str()
187 );
188 }
190 "No objects found in HDF5 file: %s",
191 catalogue_filepath.c_str()
192 );
193 }
194
195 std::string ctlg_dset_name = catalogue_dataset;
196 if (ctlg_dset_name.empty()) {
197 for (const auto& obj_name : obj_names) {
198 HighFive::ObjectType obj_type =
199 ctlg_subfile.getObjectType(obj_name);
200 if (obj_type == HighFive::ObjectType::Dataset) {
201 ctlg_dset_name = obj_name;
202 trvs::logger.info(
203 "Catalogue dataset name inferred from HDF5 file: %s",
204 ctlg_dset_name.c_str()
205 );
206 break;
207 }
208 }
209 if (ctlg_dset_name.empty()) {
210 if (trvs::currTask == 0) {
211 trvs::logger.error(
212 "No datasets found in HDF5 file: %s",
213 catalogue_filepath.c_str()
214 );
215 }
217 "No datasets found in HDF5 file: %s",
218 catalogue_filepath.c_str()
219 );
220 }
221 }
222
223 HighFive::DataSet ctlg_dset = ctlg_subfile.getDataSet(ctlg_dset_name);
224
225 // Get dataset data type, which must be float or double and the same
226 // for all columns.
227 HighFive::DataType ctlg_dtype = ctlg_dset.getDataType();
228 if (
229 ctlg_dtype != HighFive::create_datatype<float>()
230 && ctlg_dtype != HighFive::create_datatype<double>()
231 ) {
232 if (trvs::currTask == 0) {
233 trvs::logger.error(
234 "Unsupported or mixed data type in HDF5 file: %s",
235 catalogue_filepath.c_str()
236 );
237 }
239 "Unsupported or mixed data type in HDF5 file: %s",
240 catalogue_filepath.c_str()
241 );
242 }
243
244 // Get dataset column names and indices.
245 std::vector<std::string> ctlg_colnames;
246 if (colnames.empty()) {
247 std::vector<std::string> dset_attrs = ctlg_dset.listAttributeNames();
248 if (dset_attrs.empty()) {
249 if (trvs::currTask == 0) {
250 trvs::logger.error(
251 "Catalogue column names are not specified, and "
252 "no attributes found in the dataset: %s",
253 ctlg_dset_name.c_str()
254 );
255 }
257 "Catalogue column names are not specified, and "
258 "no attributes found in the dataset: %s",
259 ctlg_dset_name.c_str()
260 );
261 }
262
263 ctlg_dset.getAttribute(dset_attrs[0]).read(ctlg_colnames);
264 if (trvs::currTask == 0) {
265 trvs::logger.info(
266 "Catalogue column names inferred from HDF5 file: %s",
267 catalogue_filepath.c_str()
268 );
269 }
270 for (std::size_t iname = 0; iname < names_ordered.size(); iname++) {
271 std::size_t col_idx = static_cast<std::size_t>(std::distance(
272 ctlg_colnames.begin(),
273 std::find(
274 ctlg_colnames.begin(), ctlg_colnames.end(), names_ordered[iname]
275 )
276 ));
277 if (col_idx < ctlg_colnames.size()) {
278 name_indices[iname] = col_idx;
279 } else {
280 name_indices[iname] = -1;
281 }
282 }
283 } else
284 if (colnames.size() == 1 and colnames[0].find("attr::") == 0) {
285 std::string attr_name = colnames[0].substr(6);
286 ctlg_dset.getAttribute(attr_name).read(ctlg_colnames);
287 if (trvs::currTask == 0) {
288 trvs::logger.info(
289 "Catalogue column names inferred from dataset attribute: %s",
290 attr_name.c_str()
291 );
292 }
293 for (std::size_t iname = 0; iname < names_ordered.size(); iname++) {
294 std::size_t col_idx = static_cast<std::size_t>(std::distance(
295 ctlg_colnames.begin(),
296 std::find(
297 ctlg_colnames.begin(), ctlg_colnames.end(), names_ordered[iname]
298 )
299 ));
300 if (col_idx < ctlg_colnames.size()) {
301 name_indices[iname] = col_idx;
302 } else {
303 name_indices[iname] = -1;
304 }
305 }
306 } else {
307 ctlg_colnames = colnames;
308 }
309
310 // Get dataset dimensions.
311 std::vector<std::size_t> ctlg_dims = ctlg_dset.getDimensions();
312 int num_rows = static_cast<int>(ctlg_dims[0]);
313
314 // Get dataset data.
315 std::vector<std::vector<double>> ctlg_subdata(num_rows);
316 ctlg_dset.read(ctlg_subdata);
317
318 ctlg_data.insert(
319 ctlg_data.end(), ctlg_subdata.begin(), ctlg_subdata.end()
320 );
321 nentry += num_rows;
322 }
323
324 // Initialise particle data.
325 this->initialise_particles(nentry);
326
327 // Set particle data.
328 // Check for the 'nz' column.
329 if (name_indices[3] == -1) {
330 if (trvs::currTask == 0) {
331 trvs::logger.info(
332 "Catalogue 'nz' field is unavailable and "
333 "will be set to the mean density in the bounding box (source=%s).",
334 this->source.c_str()
335 );
336 }
337 }
338
339 double nz_box_default = 0.;
340 if (volume > 0.) {
341 nz_box_default = this->ntotal / volume;
342 }
343
344 int idx_row = 0; // current row number
345 double nz, ws, wc; // placeholder variables
346 for (const auto& row : ctlg_data) {
347 this->pdata[idx_row].pos[0] = row[name_indices[0]]; // x
348 this->pdata[idx_row].pos[1] = row[name_indices[1]]; // y
349 this->pdata[idx_row].pos[2] = row[name_indices[2]]; // z
350
351 if (name_indices[3] != -1) {
352 nz = row[name_indices[3]];
353 } else {
354 nz = nz_box_default; // default value
355 }
356
357 if (name_indices[4] != -1) {
358 ws = row[name_indices[4]];
359 } else {
360 ws = 1.; // default value
361 }
362
363 if (name_indices[5] != -1) {
364 wc = row[name_indices[5]];
365 } else {
366 wc = 1.; // default value
367 }
368
369 this->pdata[idx_row].nz = nz;
370 this->pdata[idx_row].ws = ws;
371 this->pdata[idx_row].wc = wc;
372 this->pdata[idx_row].w = ws * wc;
373
374 idx_row++;
375 }
376 } catch (const HighFive::Exception& e) {
377 if (trvs::currTask == 0) {
378 trvs::logger.error("[HighFive] %s", e.what());
379 }
380 throw trvs::IOError("[HighFive] %s", e.what());
381 }
382#endif // TRV_USE_H5
383 } else {
384 int nentry = 0;
385 for (const auto& ctlg_subfilepath : catalogue_subfilepaths) {
386 std::ifstream fin;
387
388 fin.open(ctlg_subfilepath.c_str(), std::ios::in);
389
390 if (fin.fail()) {
391 fin.close();
392 if (trvs::currTask == 0) {
393 trvs::logger.error(
394 "Failed to open file: %s", ctlg_subfilepath.c_str()
395 );
396 }
397 throw trvs::IOError(
398 "Failed to open file: %s", ctlg_subfilepath.c_str()
399 );
400 }
401
402 // Initialise particle data.
403 int num_lines = 0;
404 std::string line_str;
405 while (std::getline(fin, line_str)) {
406 // Terminate at the end of file.
407 if (!fin) {break;}
408
409 // Skip empty lines or comment lines.
410 if (line_str.empty() || line_str[0] == '#') {continue;}
411
412 // Count the line as valid otherwise.
413 num_lines++;
414 }
415
416 fin.close();
417
418 nentry += num_lines;
419 }
420
421 this->initialise_particles(nentry);
422
423 // Set particle data.
424 // Check for the 'nz' column.
425 if (name_indices[3] == -1) {
426 if (trvs::currTask == 0) {
427 trvs::logger.info(
428 "Catalogue 'nz' field is unavailable and "
429 "will be set to the mean density in the bounding box (source=%s).",
430 this->source.c_str()
431 );
432 }
433 }
434
435 double nz_box_default = 0.;
436 if (volume > 0.) {
437 nz_box_default = this->ntotal / volume;
438 }
439
440 int idx_entry = 0; // current entry number
441 std::string entry_str; // current line string
442 for (const auto& ctlg_subfilepath : catalogue_subfilepaths) {
443 std::ifstream fin;
444 fin.open(ctlg_subfilepath.c_str(), std::ios::in);
445
446 double nz, ws, wc; // placeholder variables
447 double entry; // data entry (per column per row)
448 while (std::getline(fin, entry_str)) {
449 // Terminate at the end of file.
450 if (!fin) {break;}
451
452 // Skip empty lines or comment lines.
453 if (entry_str.empty() || entry_str[0] == '#') {continue;}
454
455 // Extract row entries.
456 std::vector<double> row;
457
458 std::stringstream ss(
459 entry_str,
460 std::ios_base::out | std::ios_base::in | std::ios_base::binary
461 );
462 while (ss >> entry) {row.push_back(entry);}
463
464 // Add the current line as a particle.
465 this->pdata[idx_entry].pos[0] = row[name_indices[0]]; // x
466 this->pdata[idx_entry].pos[1] = row[name_indices[1]]; // y
467 this->pdata[idx_entry].pos[2] = row[name_indices[2]]; // z
468
469 if (name_indices[3] != -1) {
470 nz = row[name_indices[3]];
471 } else {
472 nz = nz_box_default; // default value
473 }
474
475 if (name_indices[4] != -1) {
476 ws = row[name_indices[4]];
477 } else {
478 ws = 1.; // default value
479 }
480
481 if (name_indices[5] != -1) {
482 wc = row[name_indices[5]];
483 } else {
484 wc = 1.; // default value
485 }
486
487 this->pdata[idx_entry].nz = nz;
488 this->pdata[idx_entry].ws = ws;
489 this->pdata[idx_entry].wc = wc;
490 this->pdata[idx_entry].w = ws * wc;
491
492 idx_entry++;
493 }
494
495 fin.close();
496 }
497 }
498
499 // ---------------------------------------------------------------------
500 // Catalogue properties
501 // ---------------------------------------------------------------------
502
503 // Calculate total weights.
504 this->calc_total_weights();
505
506 // Calculate particle extents.
507 this->calc_pos_extents();
508
509 return 0;
510}
511
513 std::vector<double> x, std::vector<double> y, std::vector<double> z,
514 std::vector<double> nz, std::vector<double> ws, std::vector<double> wc
515) {
516 this->source = "extdata";
517
518 // Check data array sizes.
519 std::size_t ntotal_t = x.size();
520 int ntotal = static_cast<int>(ntotal_t);
521 if (!(
522 ntotal_t == y.size()
523 && ntotal_t == z.size()
524 && ntotal_t == nz.size()
525 && ntotal_t == ws.size()
526 && ntotal_t == wc.size()
527 )) {
528 if (trvs::currTask == 0) {
529 trvs::logger.error(
530 "Inconsistent particle data dimensions (source=%s).",
531 this->source.c_str()
532 );
533 }
535 "Inconsistent particle data dimensions (source=%s).",
536 this->source.c_str()
537 );
538 }
539
540 // Fill in particle data.
541 this->initialise_particles(ntotal);
542
543#ifdef TRV_USE_OMP
544#pragma omp parallel for simd
545#endif // TRV_USE_OMP
546 for (int pid = 0; pid < ntotal; pid++) {
547 this->pdata[pid].pos[0] = x[pid];
548 this->pdata[pid].pos[1] = y[pid];
549 this->pdata[pid].pos[2] = z[pid];
550 this->pdata[pid].nz = nz[pid];
551 this->pdata[pid].ws = ws[pid];
552 this->pdata[pid].wc = wc[pid];
553 this->pdata[pid].w = ws[pid] * wc[pid];
554 }
555
556 // Calculate sample weight sum.
557 this->calc_total_weights();
558
559 // Calculate the extents of particles.
560 this->calc_pos_extents();
561
562 return 0;
563}
564
565
566// ***********************************************************************
567// Catalogue properties
568// ***********************************************************************
569
571 if (this->pdata == nullptr) {
572 if (trvs::currTask == 0) {
573 trvs::logger.error("Particle data are uninitialised.");
574 }
575 throw trvs::InvalidDataError("Particle data are uninitialised.");
576 }
577
578 double wtotal = 0., wstotal = 0.;
579
580#ifdef TRV_USE_OMP
581#if defined(__GNUC__) && !defined(__clang__)
582#pragma omp parallel for simd reduction(+:wtotal, wstotal)
583#else // !__GNUC__ || __clang__
584#pragma omp parallel for reduction(+:wtotal, wstotal)
585#endif // __GNUC__ && !__clang__
586#endif // TRV_USE_OMP
587 for (int pid = 0; pid < this->ntotal; pid++) {
588 wtotal += this->pdata[pid].w;
589 wstotal += this->pdata[pid].ws;
590 }
591
592 this->wtotal = wtotal;
593 this->wstotal = wstotal;
594
595 if (trvs::currTask == 0) {
596 trvs::logger.info(
597 "Catalogue loaded: "
598 "ntotal = %d, wtotal = %.3f, wstotal = %.3f (source=%s).",
599 this->ntotal, this->wtotal, this->wstotal, this->source.c_str()
600 );
601 }
602}
603
605 if (this->pdata == nullptr) {
606 if (trvs::currTask == 0) {
607 trvs::logger.error("Particle data are uninitialised.");
608 }
609 throw trvs::InvalidDataError("Particle data are uninitialised.");
610 }
611
612 // Initialise minimum and maximum values with the 0th particle's.
613 double pos_min[3], pos_max[3];
614 for (int iaxis = 0; iaxis < 3; iaxis++) {
615 pos_min[iaxis] = this->pdata[0].pos[iaxis];
616 pos_max[iaxis] = this->pdata[0].pos[iaxis];
617 }
618
619 // Update minimum and maximum values partice by particle.
620#ifdef TRV_USE_OMP
621#pragma omp parallel for reduction(min:pos_min) reduction(max:pos_max)
622#endif // TRV_USE_OMP
623 for (int pid = 0; pid < this->ntotal; pid++) {
624 for (int iaxis = 0; iaxis < 3; iaxis++) {
625 pos_min[iaxis] = (pos_min[iaxis] < this->pdata[pid].pos[iaxis]) ?
626 pos_min[iaxis] : this->pdata[pid].pos[iaxis];
627 pos_max[iaxis] = (pos_max[iaxis] > this->pdata[pid].pos[iaxis]) ?
628 pos_max[iaxis] : this->pdata[pid].pos[iaxis];
629 }
630 }
631
632 for (int iaxis = 0; iaxis < 3; iaxis++) {
633 this->pos_min[iaxis] = pos_min[iaxis];
634 this->pos_max[iaxis] = pos_max[iaxis];
635 this->pos_span[iaxis] = pos_max[iaxis] - pos_min[iaxis];
636 }
637
638 if (trvs::currTask == 0 && init) {
639 trvs::logger.info(
640 "Extents of particle coordinates: "
641 "{'x': (%.3f, %.3f | %.3f),"
642 " 'y': (%.3f, %.3f | %.3f),"
643 " 'z': (%.3f, %.3f | %.3f)} "
644 "(source=%s).",
645 this->pos_min[0], this->pos_max[0], this->pos_span[0],
646 this->pos_min[1], this->pos_max[1], this->pos_span[1],
647 this->pos_min[2], this->pos_max[2], this->pos_span[2],
648 this->source.c_str()
649 );
650 }
651}
652
653
654// ***********************************************************************
655// Catalogue operations
656// ***********************************************************************
657
658void ParticleCatalogue::offset_coords(const double dpos[3]) {
659 if (this->pdata == nullptr) {
660 if (trvs::currTask == 0) {
661 trvs::logger.error("Particle data are uninitialised.");
662 }
663 throw trvs::InvalidDataError("Particle data are uninitialised.");
664 }
665
666#ifdef TRV_USE_OMP
667#pragma omp parallel for
668#endif // TRV_USE_OMP
669 for (int pid = 0; pid < this->ntotal; pid++) {
670 for (int iaxis = 0; iaxis < 3; iaxis++) {
671 this->pdata[pid].pos[iaxis] -= dpos[iaxis];
672 }
673 }
674
675 this->calc_pos_extents();
676}
677
678void ParticleCatalogue::\
679offset_coords_for_periodicity(const double boxsize[3]) {
680#ifdef TRV_USE_OMP
681#pragma omp parallel for
682#endif // TRV_USE_OMP
683 for (int pid = 0; pid < this->ntotal; pid++) {
684 for (int iaxis = 0; iaxis < 3; iaxis++) {
685 if (this->pdata[pid].pos[iaxis] >= boxsize[iaxis]) {
686 this->pdata[pid].pos[iaxis] = std::fmod(
687 this->pdata[pid].pos[iaxis], boxsize[iaxis]
688 );
689 } else
690 if (this->pdata[pid].pos[iaxis] < 0.) {
691 this->pdata[pid].pos[iaxis] = std::fmod(
692 this->pdata[pid].pos[iaxis], boxsize[iaxis]
693 ) + boxsize[iaxis];
694 }
695 }
696 }
697
698 this->calc_pos_extents();
699}
700
702 ParticleCatalogue& catalogue,
703 const double boxsize[3]
704) {
705 catalogue.calc_pos_extents(false); // likely redundant but safe
706 for (int iaxis = 0; iaxis < 3; iaxis++) {
707 if (catalogue.pos_span[iaxis] > boxsize[iaxis]) {
708 if (trvs::currTask == 0) {
709 trvs::logger.warn(
710 "Catalogue extent exceeds the box size along axis %d: "
711 "span = %.3f, boxsize = %.3f (source=%s). "
712 "Some particles may lie outside the box after centring.",
713 iaxis,
714 catalogue.pos_span[iaxis], boxsize[iaxis],
715 catalogue.source.c_str()
716 );
717 }
718 }
719 }
720
721 double xmid = (catalogue.pos_min[0] + catalogue.pos_max[0]) / 2.;
722 double ymid = (catalogue.pos_min[1] + catalogue.pos_max[1]) / 2.;
723 double zmid = (catalogue.pos_min[2] + catalogue.pos_max[2]) / 2.;
724
725 double dvec[3] = {
726 xmid - boxsize[0]/2., ymid - boxsize[1]/2., zmid - boxsize[2]/2.
727 };
728
729 catalogue.offset_coords(dvec);
730}
731
733 ParticleCatalogue& catalogue, ParticleCatalogue& catalogue_ref,
734 const double boxsize[3]
735) {
736 catalogue.calc_pos_extents(false); // likely redundant but safe
737 for (int iaxis = 0; iaxis < 3; iaxis++) {
738 if (catalogue.pos_span[iaxis] > boxsize[iaxis]) {
739 if (trvs::currTask == 0) {
740 trvs::logger.warn(
741 "Catalogue extent exceeds the box size along axis %d: "
742 "span = %.3f, boxsize = %.3f (source=%s). "
743 "Some particles may lie outside the box after centring.",
744 iaxis,
745 catalogue.pos_span[iaxis], boxsize[iaxis],
746 catalogue.source.c_str()
747 );
748 }
749 }
750 }
751
752 catalogue_ref.calc_pos_extents(false); // likely redundant but safe
753 for (int iaxis = 0; iaxis < 3; iaxis++) {
754 if (catalogue_ref.pos_span[iaxis] > boxsize[iaxis]) {
755 if (trvs::currTask == 0) {
756 trvs::logger.warn(
757 "Catalogue extent exceeds the box size along axis %d: "
758 "span = %.3f, boxsize = %.3f (source=%s). "
759 "Some particles may lie outside the box after centring.",
760 iaxis,
761 catalogue_ref.pos_span[iaxis], boxsize[iaxis],
762 catalogue_ref.source.c_str()
763 );
764 }
765 }
766 }
767
768 double xmid = (catalogue_ref.pos_min[0] + catalogue_ref.pos_max[0]) / 2.;
769 double ymid = (catalogue_ref.pos_min[1] + catalogue_ref.pos_max[1]) / 2.;
770 double zmid = (catalogue_ref.pos_min[2] + catalogue_ref.pos_max[2]) / 2.;
771
772 double dvec[3] = {
773 xmid - boxsize[0]/2., ymid - boxsize[1]/2., zmid - boxsize[2]/2.
774 };
775
776 catalogue_ref.offset_coords(dvec);
777 catalogue.offset_coords(dvec);
778}
779
781 ParticleCatalogue& catalogue,
782 const double boxsize[3], const double boxsize_pad[3]
783) {
784 catalogue.calc_pos_extents(false); // likely redundant but safe
785 for (int iaxis = 0; iaxis < 3; iaxis++) {
786 if (catalogue.pos_span[iaxis] > boxsize[iaxis]) {
787 if (trvs::currTask == 0) {
788 trvs::logger.warn(
789 "Catalogue extent exceeds the box size along axis %d: "
790 "span = %.3f, boxsize = %.3f (source=%s). "
791 "Some particles may lie outside the box after padding.",
792 iaxis,
793 catalogue.pos_span[iaxis], boxsize[iaxis],
794 catalogue.source.c_str()
795 );
796 }
797 }
798 }
799
800 double dvec[3] = {
801 catalogue.pos_min[0] - boxsize_pad[0] * boxsize[0],
802 catalogue.pos_min[1] - boxsize_pad[1] * boxsize[1],
803 catalogue.pos_min[2] - boxsize_pad[2] * boxsize[2]
804 };
805
806 catalogue.offset_coords(dvec);
807}
808
810 ParticleCatalogue& catalogue, ParticleCatalogue& catalogue_ref,
811 const double boxsize[3], const double boxsize_pad[3]
812) {
813 catalogue.calc_pos_extents(false); // likely redundant but safe
814 for (int iaxis = 0; iaxis < 3; iaxis++) {
815 if (catalogue.pos_span[iaxis] > boxsize[iaxis]) {
816 if (trvs::currTask == 0) {
817 trvs::logger.warn(
818 "Catalogue extent exceeds the box size along axis %d: "
819 "span = %.3f, boxsize = %.3f (source=%s). "
820 "Some particles may lie outside the box after padding.",
821 iaxis,
822 catalogue.pos_span[iaxis], boxsize[iaxis],
823 catalogue.source.c_str()
824 );
825 }
826 }
827 }
828
829 catalogue_ref.calc_pos_extents(false); // likely redundant but safe
830 for (int iaxis = 0; iaxis < 3; iaxis++) {
831 if (catalogue_ref.pos_span[iaxis] > boxsize[iaxis]) {
832 if (trvs::currTask == 0) {
833 trvs::logger.warn(
834 "Catalogue extent exceeds the box size along axis %d: "
835 "span = %.3f, boxsize = %.3f (source=%s). "
836 "Some particles may lie outside the box after padding.",
837 iaxis,
838 catalogue_ref.pos_span[iaxis], boxsize[iaxis],
839 catalogue_ref.source.c_str()
840 );
841 }
842 }
843 }
844
845 double dvec[3] = {
846 catalogue_ref.pos_min[0] - boxsize_pad[0] * boxsize[0],
847 catalogue_ref.pos_min[1] - boxsize_pad[1] * boxsize[1],
848 catalogue_ref.pos_min[2] - boxsize_pad[2] * boxsize[2]
849 };
850
851 catalogue_ref.offset_coords(dvec);
852 catalogue.offset_coords(dvec);
853}
854
856 ParticleCatalogue& catalogue,
857 const double boxsize[3], const int ngrid[3], const double ngrid_pad[3]
858) {
859 catalogue.calc_pos_extents(false); // likely redundant but safe
860
861 double dvec[3] = {
862 catalogue.pos_min[0], catalogue.pos_min[1], catalogue.pos_min[2]
863 };
864
865 dvec[0] -= ngrid_pad[0] * boxsize[0] / double(ngrid[0]);
866 dvec[1] -= ngrid_pad[1] * boxsize[1] / double(ngrid[1]);
867 dvec[2] -= ngrid_pad[2] * boxsize[2] / double(ngrid[2]);
868
869 catalogue.offset_coords(dvec);
870}
871
873 ParticleCatalogue& catalogue, ParticleCatalogue& catalogue_ref,
874 const double boxsize[3], const int ngrid[3], const double ngrid_pad[3]
875) {
876 catalogue_ref.calc_pos_extents(false); // likely redundant but safe
877
878 double dvec[3] = {
879 catalogue_ref.pos_min[0] - ngrid_pad[0] * boxsize[0] / double(ngrid[0]),
880 catalogue_ref.pos_min[1] - ngrid_pad[1] * boxsize[1] / double(ngrid[1]),
881 catalogue_ref.pos_min[2] - ngrid_pad[2] * boxsize[2] / double(ngrid[2])
882 };
883
884 catalogue_ref.offset_coords(dvec);
885 catalogue.offset_coords(dvec);
886}
887
888} // namespace trv
void initialise_particles(const int num)
Initialise particle data container.
Definition particles.cpp:47
static void centre_in_box(ParticleCatalogue &catalogue, const double boxsize[3])
Centre a catalogue in a box.
int load_particle_data(std::vector< double > x, std::vector< double > y, std::vector< double > z, std::vector< double > nz, std::vector< double > ws, std::vector< double > wc)
Read in particle data.
void calc_pos_extents(bool init=true)
Calculate the extents of particle positions.
~ParticleCatalogue()
Destruct the particle catalogue.
Definition particles.cpp:45
int load_catalogue_file(const std::string &catalogue_filepath, const std::string &catalogue_columns, const std::string &catalogue_dataset="", double volume=0.)
Read in a catalogue file.
Definition particles.cpp:94
ParticleCatalogue(int verbose=-1)
Construct the particle catalogue with initial values.
Definition particles.cpp:39
std::string source
catalogue source
Definition particles.hpp:80
void reset_particles()
Reset particle data container.
Definition particles.cpp:72
void calc_total_weights()
Calculate total overall weight of particles.
ParticleData & operator[](const int pid)
Return individual particle information.
Definition particles.cpp:85
void offset_coords(const double dpos[3])
Offset particle positions by a given vector.
void finalise_particles()
Finalise particle data container.
Definition particles.cpp:68
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 wtotal
total overall weight of particles
Definition particles.hpp:85
double wstotal
total sample weight of particles
Definition particles.hpp:86
double pos_max[3]
maximum particle coordinates
Definition particles.hpp:89
Exception raised when an input/output operation fails.
Definition monitor.hpp:707
Exception raised when the data to be operated on are invalid.
Definition monitor.hpp:755
Exception raised when parameters are invalid.
Definition monitor.hpp:731
double gbytesMem
current memory usage in gibibytes
Definition monitor.cpp:94
bool has_extension(const std::string &fname, const std::string &fext)
Check if a file has a given extension.
Definition monitor.cpp:38
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
Logger logger
default logger (at NSET logging level)
int currTask
current task
Definition monitor.cpp:92
std::vector< std::string > split_string(const std::string &str, const std::string &delimiter)
Split a string into a vector of strings.
Definition monitor.cpp:59
Particle containers with I/O methods and operations.
Particle data container.
Definition particles.hpp:63