Shamrock 2025.10.0
Astrophysical Code
Loading...
Searching...
No Matches
pySPHModel.cpp
Go to the documentation of this file.
1// -------------------------------------------------------//
2//
3// SHAMROCK code for hydrodynamics
4// Copyright (c) 2021-2026 Timothée David--Cléris <tim.shamrock@proton.me>
5// SPDX-License-Identifier: CeCILL Free Software License Agreement v2.1
6// Shamrock is licensed under the CeCILL 2.1 License, see LICENSE for more information
7//
8// -------------------------------------------------------//
9
17
20#include "shambase/memory.hpp"
23#include "shamcomm/logs.hpp"
40#include "shamphys/SodTube.hpp"
42#include <pybind11/cast.h>
43#include <pybind11/numpy.h>
44#include <pybind11/pytypes.h>
45#include <memory>
46#include <optional>
47#include <random>
48#include <utility>
49
50template<class Tvec, template<class> class SPHKernel>
51void add_instance(py::module &m, std::string name_config, std::string name_model) {
52 using namespace shammodels::sph;
53
54 using Tscal = shambase::VecComponent<Tvec>;
55
56 using T = Model<Tvec, SPHKernel>;
57
61 using TConfig = typename T::Solver::Config;
62
63 using custom_getter_t = std::function<pybind11::array_t<f64>(size_t, pybind11::dict &)>;
64
65 shamlog_debug_ln("[Py]", "registering class :", name_config, typeid(T).name());
66 shamlog_debug_ln("[Py]", "registering class :", name_model, typeid(T).name());
67
68 py::class_<TConfig> config_cls(m, name_config.c_str());
69
70 shammodels::common::add_json_defs<TConfig>(config_cls);
71
72 config_cls.def("print_status", &TConfig::print_status)
73 .def("set_particle_tracking", &TConfig::set_particle_tracking)
74 .def(
75 "set_scheduler_config",
76 [](TConfig &self, u64 split_crit, u64 merge_crit) {
77 self.scheduler_conf.split_load_value = split_crit;
78 self.scheduler_conf.merge_load_value = merge_crit;
79 },
80 py::kw_only(),
81 py::arg("split_load_value"),
82 py::arg("merge_load_value"))
83 .def("set_tree_reduction_level", &TConfig::set_tree_reduction_level)
84 .def("set_two_stage_search", &TConfig::set_two_stage_search)
85 .def("set_show_neigh_stats", &TConfig::set_show_neigh_stats)
86 .def(
87 "set_max_neigh_cache_size",
88 [](TConfig &self, const py::object &max_neigh_cache_size) {
89 ON_RANK_0(shamlog_warn_ln(
90 "SPH",
91 ".set_max_neigh_cache_size() is deprecated,\n"
92 " -> calling this is a no-op,\n"
93 " -> you can remove the call to that function"););
94 })
95 .def("set_smoothing_length_density_based", &TConfig::set_smoothing_length_density_based)
96 .def(
97 "set_smoothing_length_density_based_neigh_lim",
98 &TConfig::set_smoothing_length_density_based_neigh_lim)
99 .def("set_enable_particle_reordering", &TConfig::set_enable_particle_reordering)
100 .def("set_particle_reordering_step_freq", &TConfig::set_particle_reordering_step_freq)
101 .def("set_show_ghost_zone_graph", &TConfig::set_show_ghost_zone_graph)
102 .def("use_luminosity", &TConfig::use_luminosity)
103 .def("set_save_dt_to_fields", &TConfig::set_save_dt_to_fields)
104 .def("should_save_dt_to_fields", &TConfig::should_save_dt_to_fields)
105 .def("set_eos_isothermal", &TConfig::set_eos_isothermal)
106 .def("set_eos_adiabatic", &TConfig::set_eos_adiabatic)
107 .def("set_eos_polytropic", &TConfig::set_eos_polytropic)
108 .def("set_eos_locally_isothermal", &TConfig::set_eos_locally_isothermal)
109 .def(
110 "set_eos_locally_isothermalLP07",
111 [](TConfig &self, Tscal cs0, Tscal q, Tscal r0) {
112 self.set_eos_locally_isothermalLP07(cs0, q, r0);
113 },
114 py::kw_only(),
115 py::arg("cs0"),
116 py::arg("q"),
117 py::arg("r0"))
118 .def(
119 "set_eos_locally_isothermalFA2014",
120 [](TConfig &self, Tscal h_over_r) {
121 self.set_eos_locally_isothermalFA2014(h_over_r);
122 },
123 py::kw_only(),
124 py::arg("h_over_r"))
125 .def(
126 "set_eos_locally_isothermalFA2014_extended",
127 [](TConfig &self, Tscal cs0, Tscal q, Tscal r0, u32 n_sinks) {
128 self.set_eos_locally_isothermalFA2014_extended(cs0, q, r0, n_sinks);
129 },
130 py::kw_only(),
131 py::arg("cs0"),
132 py::arg("q"),
133 py::arg("r0"),
134 py::arg("n_sinks"))
135 .def(
136 "set_eos_fermi",
137 [](TConfig &self, Tscal mu_e) {
138 self.set_eos_fermi(mu_e);
139 },
140 py::kw_only(),
141 py::arg("mu_e"))
142 .def("set_artif_viscosity_None", &TConfig::set_artif_viscosity_None)
143 .def(
144 "set_artif_viscosity_Constant",
145 [](TConfig &self, Tscal alpha_u, Tscal alpha_AV, Tscal beta_AV) {
146 self.set_artif_viscosity_Constant({alpha_u, alpha_AV, beta_AV});
147 },
148 py::kw_only(),
149 py::arg("alpha_u"),
150 py::arg("alpha_AV"),
151 py::arg("beta_AV"))
152 .def(
153 "set_artif_viscosity_VaryingMM97",
154 [](TConfig &self,
155 Tscal alpha_min,
156 Tscal alpha_max,
157 Tscal sigma_decay,
158 Tscal alpha_u,
159 Tscal beta_AV) {
160 self.set_artif_viscosity_VaryingMM97(
161 {alpha_min, alpha_max, sigma_decay, alpha_u, beta_AV});
162 },
163 py::kw_only(),
164 py::arg("alpha_min"),
165 py::arg("alpha_max"),
166 py::arg("sigma_decay"),
167 py::arg("alpha_u"),
168 py::arg("beta_AV"))
169 .def(
170 "set_artif_viscosity_VaryingCD10",
171 [](TConfig &self,
172 Tscal alpha_min,
173 Tscal alpha_max,
174 Tscal sigma_decay,
175 Tscal alpha_u,
176 Tscal beta_AV) {
177 self.set_artif_viscosity_VaryingCD10(
178 {alpha_min, alpha_max, sigma_decay, alpha_u, beta_AV});
179 },
180 py::kw_only(),
181 py::arg("alpha_min"),
182 py::arg("alpha_max"),
183 py::arg("sigma_decay"),
184 py::arg("alpha_u"),
185 py::arg("beta_AV"))
186 .def(
187 "set_artif_viscosity_ConstantDisc",
188 [](TConfig &self, Tscal alpha_AV, Tscal alpha_u, Tscal beta_AV) {
189 self.set_artif_viscosity_ConstantDisc({alpha_AV, alpha_u, beta_AV});
190 },
191 py::kw_only(),
192 py::arg("alpha_AV"),
193 py::arg("alpha_u"),
194 py::arg("beta_AV"))
195 .def(
196 "set_IdealMHD",
197 [](TConfig &self, Tscal sigma_mhd, Tscal sigma_u) {
198 self.set_IdealMHD({sigma_mhd, sigma_u});
199 },
200 py::kw_only(),
201 py::arg("sigma_mhd"),
202 py::arg("sigma_u"))
203 .def(
204 "set_self_gravity_none",
205 [](TConfig &self) {
206 self.self_grav_config.set_none();
207 })
208 .def(
209 "set_self_gravity_direct",
210 [](TConfig &self, bool reference_mode = false) {
211 self.self_grav_config.set_direct(reference_mode);
212 },
213 py::kw_only(),
214 py::arg("reference_mode") = false)
215 .def(
216 "set_self_gravity_mm",
217 [](TConfig &self, u32 mm_order, f64 opening_angle, u32 reduction_level) {
218 self.self_grav_config.set_mm(mm_order, opening_angle, reduction_level);
219 },
220 py::kw_only(),
221 py::arg("order"),
222 py::arg("opening_angle"),
223 py::arg("reduction_level") = 3)
224 .def(
225 "set_self_gravity_fmm",
226 [](TConfig &self, u32 order, f64 opening_angle, u32 reduction_level) {
227 self.self_grav_config.set_fmm(order, opening_angle, reduction_level);
228 },
229 py::kw_only(),
230 py::arg("order"),
231 py::arg("opening_angle"),
232 py::arg("reduction_level") = 3)
233 .def(
234 "set_self_gravity_sfmm",
235 [](TConfig &self,
236 u32 sfmm_order,
237 f64 opening_angle,
238 bool leaf_lowering,
239 u32 reduction_level) {
240 self.self_grav_config.set_sfmm(
241 sfmm_order, opening_angle, leaf_lowering, reduction_level);
242 },
243 py::kw_only(),
244 py::arg("order"),
245 py::arg("opening_angle"),
246 py::arg("leaf_lowering") = true,
247 py::arg("reduction_level") = 3)
248 .def(
249 "set_softening_plummer",
250 [](TConfig &self, f64 epsilon) {
251 self.self_grav_config.set_softening_plummer(epsilon);
252 },
253 py::kw_only(),
254 py::arg("epsilon"))
255 .def(
256 "set_softening_none",
257 [](TConfig &self) {
258 self.self_grav_config.set_softening_none();
259 })
260 .def("set_boundary_free", &TConfig::set_boundary_free)
261 .def("set_boundary_periodic", &TConfig::set_boundary_periodic)
262 .def("set_boundary_shearing_periodic", &TConfig::set_boundary_shearing_periodic)
263 .def(
264 "set_dust_mode_none",
265 [](TConfig &self) {
266 self.dust_config.set_none();
267 })
268 .def(
269 "set_dust_mode_monofluid_tvi",
270 [](TConfig &self, u32 nvar, bool pure_diffusion_mode) {
271 self.dust_config.set_monofluid_tvi(nvar, pure_diffusion_mode);
272 },
273 py::kw_only(),
274 py::arg("nvar"),
275 py::arg("pure_diffusion_mode") = false)
276 .def(
277 "set_dust_mode_monofluid_complete",
278 [](TConfig &self, u32 ndust) {
279 self.dust_config.set_monofluid_complete(ndust);
280 },
281 py::kw_only(),
282 py::arg("ndust"))
283 .def(
284 "set_dust_drag_constant",
285 [](TConfig &self, std::vector<Tscal> ts) {
286 self.dust_config.set_drag_constant({.stopping_times = std::move(ts)});
287 })
288 .def(
289 "set_dust_drag_epstein",
290 [](TConfig &self,
291 Tscal gamma,
292 std::vector<Tscal> grain_sizes,
293 std::vector<Tscal> grain_densities) {
294 self.dust_config.set_drag_epstein(
295 {.gamma = gamma,
296 .grains_sizes = std::move(grain_sizes),
297 .grains_densities = std::move(grain_densities)});
298 },
299 py::arg("gamma"),
300 py::arg("grain_sizes"),
301 py::arg("grain_densities"))
302 .def("add_ext_force_point_mass", &TConfig::add_ext_force_point_mass)
303 .def("add_ext_force_paczynski_wiita", &TConfig::add_ext_force_paczynski_wiita)
304 .def(
305 "add_ext_force_lense_thirring",
306 [](TConfig &self, Tscal central_mass, Tscal Racc, Tscal a_spin, Tvec dir_spin) {
307 self.add_ext_force_lense_thirring(central_mass, Racc, a_spin, dir_spin);
308 },
309 py::kw_only(),
310 py::arg("central_mass"),
311 py::arg("Racc"),
312 py::arg("a_spin"),
313 py::arg("dir_spin"))
314 .def(
315 "add_ext_force_shearing_box",
316 [](TConfig &self, Tscal Omega_0, Tscal eta, Tscal q) {
317 self.add_ext_force_shearing_box(Omega_0, eta, q);
318 },
319 py::kw_only(),
320 py::arg("Omega_0"),
321 py::arg("eta"),
322 py::arg("q"))
323 .def(
324 "add_ext_force_velocity_dissipation",
325 [](TConfig &self, Tscal eta) {
326 self.ext_force_config.add_velocity_dissipation(eta);
327 },
328 py::kw_only(),
329 py::arg("eta"))
330 .def(
331 "add_ext_force_vertical_disc_potential",
332 [](TConfig &self, Tscal central_mass, Tscal R0) {
333 self.ext_force_config.add_vertical_disc_potential(central_mass, R0);
334 },
335 py::kw_only(),
336 py::arg("central_mass"),
337 py::arg("R0"))
338 .def("set_units", &TConfig::set_units)
339 .def(
340 "get_units",
341 [](TConfig &self) {
342 return self.unit_sys;
343 })
344 .def(
345 "set_cfl_cour",
346 [](TConfig &self, Tscal cfl_cour) {
347 self.cfl_config.cfl_cour = cfl_cour;
348 })
349 .def(
350 "set_cfl_force",
351 [](TConfig &self, Tscal cfl_force) {
352 self.cfl_config.cfl_force = cfl_force;
353 })
354 .def(
355 "set_eta_sink",
356 [](TConfig &self, Tscal eta_sink) {
357 self.cfl_config.eta_sink = eta_sink;
358 })
359 .def("set_cfl_multipler", &TConfig::set_cfl_multipler)
360 .def("set_cfl_mult_stiffness", &TConfig::set_cfl_mult_stiffness)
361 .def(
362 "set_show_cfl_detail",
363 [](TConfig &self, bool show_cfl_detail) {
364 self.show_cfl_detail = show_cfl_detail;
365 },
366 py::arg("show_cfl_detail"))
367 .def(
368 "set_particle_mass",
369 [](TConfig &self, Tscal gpart_mass) {
370 self.gpart_mass = gpart_mass;
371 })
372 .def(
373 "add_kill_sphere",
374 [](TConfig &self, const Tvec &center, Tscal radius) {
375 self.particle_killing.add_kill_sphere(center, radius);
376 },
377 py::kw_only(),
378 py::arg("center"),
379 py::arg("radius"));
380
381 std::string sod_tube_analysis_name = name_model + "_AnalysisSodTube";
382 py::class_<TAnalysisSodTube>(m, sod_tube_analysis_name.c_str())
383 .def("compute_L2_dist", [](TAnalysisSodTube &self) -> std::tuple<Tscal, Tvec, Tscal> {
384 auto ret = self.compute_L2_dist();
385 return {ret.rho, ret.v, ret.P};
386 });
387
388 std::string disc_analysis_name = name_model + "_AnalysisDisc";
389 py::class_<TAnalysisDisc>(m, disc_analysis_name.c_str())
390 .def(
391 "collect_data",
392 [](TAnalysisDisc &self, Tscal Rmin, Tscal Rmax, u32 Nbin, ShamrockCtx &ctx) {
393 auto anal = self.compute_analysis(Rmin, Rmax, Nbin, ctx);
394 py::dict dic_out;
395
396 auto radius = anal.radius.copy_to_stdvec();
397 auto counter = anal.counter.copy_to_stdvec();
398 auto Sigma = anal.Sigma.copy_to_stdvec();
399 auto lx = anal.lx.copy_to_stdvec();
400 auto ly = anal.ly.copy_to_stdvec();
401 auto lz = anal.lz.copy_to_stdvec();
402 auto tilt = anal.tilt.copy_to_stdvec();
403 auto twist = anal.twist.copy_to_stdvec();
404 auto psi = anal.psi.copy_to_stdvec();
405 auto Hsq = anal.Hsq.copy_to_stdvec();
406
407 dic_out["radius"] = radius;
408 dic_out["counter"] = counter;
409 dic_out["Sigma"] = Sigma;
410 dic_out["lx"] = lx;
411 dic_out["ly"] = ly;
412 dic_out["lz"] = lz;
413 dic_out["tilt"] = tilt;
414 dic_out["twist"] = twist;
415 dic_out["psi"] = psi;
416 dic_out["Hsq"] = Hsq;
417
418 return dic_out;
419 });
420
421 std::string setup_name = name_model + "_SPHSetup";
422 py::class_<TSPHSetup>(m, setup_name.c_str())
423 .def(
424 "make_generator_lattice_hcp",
425 [](TSPHSetup &self, Tscal dr, Tvec box_min, Tvec box_max, bool discontinuous) {
426 return self.make_generator_lattice_hcp(dr, {box_min, box_max}, discontinuous);
427 },
428 py::arg("dr"),
429 py::arg("box_min"),
430 py::arg("box_max"),
431 py::arg("discontinuous") = true)
432 .def(
433 "make_generator_lattice_cubic",
434 [](TSPHSetup &self, Tscal dr, Tvec box_min, Tvec box_max) {
435 return self.make_generator_lattice_cubic(dr, {box_min, box_max});
436 })
437 .def(
438 "make_generator_disc_mc",
439 [](TSPHSetup &self,
440 Tscal part_mass,
441 Tscal disc_mass,
442 Tscal r_in,
443 Tscal r_out,
444 std::function<Tscal(Tscal)> sigma_profile,
445 std::function<Tscal(Tscal)> H_profile,
446 std::function<Tscal(Tscal)> rot_profile,
447 std::function<Tscal(Tscal)> cs_profile,
448 std::function<Tvec(Tvec)> velocity_field,
449 std::function<Tscal(Tvec)> cs_field,
450 u64 random_seed,
451 Tscal init_h_factor) {
452 auto build_vel_lambda = [&]() -> std::function<Tvec(Tvec)> {
453 if (!velocity_field && !rot_profile) {
455 "make_generator_disc_mc: either velocity_field or rot_profile must be "
456 "provided, you must provide one of them");
457 }
458
459 if (velocity_field && rot_profile) {
461 "make_generator_disc_mc: either velocity_field or rot_profile must be "
462 "provided, you cannot provide both");
463 }
464
465 if (velocity_field) {
466 return std::move(velocity_field);
467 }
468 return [vth_r = std::move(rot_profile)](Tvec pos) {
469 pos[2] = 0; // to get the cylindrical radius
470 Tscal r = sycl::length(pos);
471
472 auto etheta = sycl::vec<Tscal, 3>{-pos.y(), pos.x(), 0};
473 etheta /= sycl::length(etheta);
474
475 return vth_r(r) * etheta;
476 };
477 };
478
479 auto build_cs_lambda = [&]() -> std::function<Tscal(Tvec)> {
480 bool need_cs = self.solver_config.is_eos_locally_isothermal();
481
482 if (!need_cs) {
483 if (cs_field) {
484 if (shamcomm::world_rank() == 0) {
486 "SPHSetup",
487 "make_generator_disc_mc: with the current EOS, cs_field is "
488 "ignored");
489 }
490 }
491 if (cs_profile) {
492 if (shamcomm::world_rank() == 0) {
494 "SPHSetup",
495 "make_generator_disc_mc: with the current EOS, cs_profile is "
496 "ignored");
497 }
498 }
499 return std::function<Tscal(Tvec)>{};
500 }
501
502 if (!cs_field && !cs_profile) {
504 "make_generator_disc_mc: either cs_field or cs_profile must be "
505 "provided, you must provide one of them");
506 }
507
508 if (cs_field && cs_profile) {
510 "make_generator_disc_mc: either cs_field or cs_profile must be "
511 "provided, you cannot provide both");
512 }
513
514 if (cs_field) {
515 return std::move(cs_field);
516 }
517
518 return [cs_r = std::move(cs_profile)](Tvec pos) {
519 pos[2] = 0; // to get the cylindrical radius
520 Tscal r = sycl::length(pos);
521 return cs_r(r);
522 };
523 };
524
525 return self.make_generator_disc_mc(
526 part_mass,
527 disc_mass,
528 r_in,
529 r_out,
530 std::move(sigma_profile),
531 std::move(H_profile),
532 build_vel_lambda(),
533 build_cs_lambda(),
534 std::mt19937_64(random_seed),
535 init_h_factor);
536 },
537 py::kw_only(),
538 py::arg("part_mass"),
539 py::arg("disc_mass"),
540 py::arg("r_in"),
541 py::arg("r_out"),
542 py::arg("sigma_profile"),
543 py::arg("H_profile"),
544 py::arg("rot_profile") = std::function<Tscal(Tscal)>{},
545 py::arg("cs_profile") = std::function<Tscal(Tscal)>{},
546 py::arg("velocity_field") = std::function<Tvec(Tvec)>{},
547 py::arg("cs_field") = std::function<Tscal(Tvec)>{},
548 py::arg("random_seed"),
549 py::arg("init_h_factor") = 0.8,
550 R"pbdoc(
551 Create a Monte Carlo disc particle generator.
552
553 Particles are sampled in cylindrical coordinates: the radius is drawn
554 with rejection sampling from ``sigma_profile``, the azimuth is uniform,
555 and the vertical coordinate follows a Gaussian with scale ``H_profile(r)``.
556 The initial density is extrapolated from the surface density profile, and
557 smoothing lengths are set from that density.
558
559 Args:
560 part_mass: Mass of each SPH particle.
561 disc_mass: Total disc mass. The particle count is ``disc_mass / part_mass``.
562 r_in: Inner disc radius.
563 r_out: Outer disc radius.
564 sigma_profile: Surface density profile ``sigma(r)``.
565 H_profile: Disc scale height profile ``H(r)``.
566 rot_profile: Azimuthal speed profile ``v_theta(r)``. The velocity is
567 projected along the cylindrical azimuthal direction at each
568 particle position. Mutually exclusive with ``velocity_field``.
569 cs_profile: Sound speed profile ``c_s(r)``. Evaluated at the cylindrical
570 radius of each particle. Required when the solver uses a locally
571 isothermal EOS. Mutually exclusive with ``cs_field``.
572 velocity_field: Velocity profile ``v(x, y, z)``. Mutually exclusive
573 with ``rot_profile``.
574 cs_field: Sound speed profile ``c_s(x, y, z)``. Required when the solver
575 uses a locally isothermal EOS. Mutually exclusive with ``cs_profile``.
576 random_seed: Seed for the Monte Carlo sampler.
577 init_h_factor: Multiplier applied to the smoothing length inferred from
578 the generated density. Defaults to ``0.8``.
579
580 Notes:
581 Exactly one of ``velocity_field`` or ``rot_profile`` must be provided.
582
583 If the solver uses a locally isothermal EOS, exactly one of ``cs_field``
584 or ``cs_profile`` must be provided. Otherwise both sound-speed profiles
585 are ignored and a warning is emitted if either is supplied.
586
587 Returns:
588 A setup node to pass to :py:meth:`apply_setup`.
589 )pbdoc")
590 .def(
591 "make_generator_from_context",
592 [](TSPHSetup &self, ShamrockCtx &context_other) {
593 return self.make_generator_from_context(context_other);
594 })
595 .def(
596 "make_combiner_add",
597 [](TSPHSetup &self,
600 return self.make_combiner_add(parent1, parent2);
601 })
602 .def(
603 "make_modifier_warp_disc",
604 [](TSPHSetup &self,
606 Tscal Rwarp,
607 Tscal Hwarp,
608 Tscal inclination,
609 Tscal posangle) {
610 return self.make_modifier_warp_disc(parent, Rwarp, Hwarp, inclination, posangle);
611 },
612 py::kw_only(),
613 py::arg("parent"),
614 py::arg("Rwarp"),
615 py::arg("Hwarp"),
616 py::arg("inclination"),
617 py::arg("posangle") = 0.)
618 .def(
619 "make_modifier_custom_warp",
620 [](TSPHSetup &self,
622 std::function<Tscal(Tscal)> inc_profile,
623 std::function<Tscal(Tscal)> psi_profile,
624 std::function<Tvec(Tscal)> k_profile) {
625 return self.make_modifier_custom_warp(parent, inc_profile, psi_profile, k_profile);
626 },
627 py::kw_only(),
628 py::arg("parent"),
629 py::arg("inc_profile"),
630 py::arg("psi_profile"),
631 py::arg("k_profile"))
632 .def(
633 "make_modifier_offset",
634 [](TSPHSetup &self,
636 Tvec offset_postion,
637 Tvec offset_velocity) {
638 return self.make_modifier_add_offset(parent, offset_postion, offset_velocity);
639 },
640 py::kw_only(),
641 py::arg("parent"),
642 py::arg("offset_position"),
643 py::arg("offset_velocity"))
644 .def(
645 "make_modifier_filter",
646 [](TSPHSetup &self,
648 std::function<bool(Tvec)> filter) {
649 return self.make_modifier_filter(parent, filter);
650 },
651 py::kw_only(),
652 py::arg("parent"),
653 py::arg("filter"))
654 .def(
655 "make_modifier_split_part",
656 [](TSPHSetup &self,
658 u64 n_split,
659 u64 seed,
660 Tscal h_scaling) {
661 return self.make_modifier_split_part(parent, n_split, seed, h_scaling);
662 },
663 py::kw_only(),
664 py::arg("parent"),
665 py::arg("n_split"),
666 py::arg("seed"),
667 py::arg("h_scaling") = 0.6)
668 .def(
669 "apply_setup",
670 [](TSPHSetup &self,
672 bool part_reordering,
673 std::optional<u32> gen_step,
674 std::optional<u32> insert_step,
675 std::optional<u64> msg_count_limit,
676 std::optional<u64> msg_size_limit,
677 std::optional<u64> max_msg_size,
678 bool do_setup_log,
679 bool use_new_setup,
680 bool speculative_balancing) {
681 if (use_new_setup) {
682 return self.apply_setup_new(
683 setup,
684 part_reordering,
685 gen_step,
686 insert_step,
687 msg_count_limit,
688 msg_size_limit,
689 max_msg_size,
690 do_setup_log,
691 speculative_balancing);
692 } else {
693 if (bool(gen_step)) {
694 ON_RANK_0(
696 "SPHSetup", "gen_step is ignored when using old setup"));
697 }
698 if (bool(msg_count_limit)) {
699 ON_RANK_0(
701 "SPHSetup", "msg_count_limit is ignored when using old setup"));
702 }
703 if (bool(msg_size_limit)) {
704 ON_RANK_0(
706 "SPHSetup", "msg_size_limit is ignored when using old setup"));
707 }
708 if (bool(max_msg_size)) {
709 ON_RANK_0(
711 "SPHSetup", "max_msg_size is ignored when using old setup"));
712 }
713 if (bool(do_setup_log)) {
714 ON_RANK_0(
716 "SPHSetup", "do_setup_log is ignored when using old setup"));
717 }
718 return self.apply_setup(setup, part_reordering, insert_step);
719 }
720 },
721 py::arg("setup"),
722 py::kw_only(),
723 py::arg("part_reordering") = true,
724 py::arg("gen_step") = std::nullopt,
725 py::arg("insert_step") = std::nullopt,
726 py::arg("msg_count_limit") = std::nullopt,
727 py::arg("rank_comm_size_limit") = std::nullopt,
728 py::arg("max_msg_size") = std::nullopt,
729 py::arg("do_setup_log") = false,
730 py::arg("use_new_setup") = true,
731 py::arg("speculative_balancing") = false);
732
733 py::class_<T>(m, name_model.c_str())
734 .def(py::init([](ShamrockCtx &ctx) {
735 return std::make_unique<T>(ctx);
736 }))
737 .def("init", &T::init)
738 .def("init_scheduler", &T::init_scheduler)
739
740 .def(
741 "evolve_once_override_time",
742 &T::evolve_once_time_expl,
743 py::arg("t_curr"),
744 py::arg("dt_input"))
745 .def("evolve_once", &T::evolve_once)
746 .def(
747 "evolve_until",
748 [](T &self, f64 target_time, i32 niter_max, f64 max_walltime) {
749 return self.evolve_until(target_time, niter_max, max_walltime);
750 },
751 py::arg("target_time"),
752 py::kw_only(),
753 py::arg("niter_max") = -1,
754 py::arg("max_walltime") = -1)
755 .def(
756 "set_dt",
757 [](T &self, f64 dt) {
758 self.solver.solver_config.set_next_dt(dt);
759 })
760 .def("timestep", &T::timestep)
761 .def("set_cfl_cour", &T::set_cfl_cour, py::arg("cfl_cour"))
762 .def("set_cfl_force", &T::set_cfl_force, py::arg("cfl_force"))
763 .def("set_eta_sink", &T::set_eta_sink, py::arg("eta_sink"))
764 .def("set_particle_mass", &T::set_particle_mass, py::arg("gpart_mass"))
765 .def("get_particle_mass", &T::get_particle_mass)
766 .def("rho_h", &T::rho_h)
767 .def("get_hfact", &T::get_hfact)
768 .def(
769 "get_solver_tex",
770 [](T &self) {
771 return shambase::get_check_ref(self.solver.storage.solver_sequence).get_tex();
772 })
773 .def(
774 "get_solver_dot_graph",
775 [](T &self) {
776 return shambase::get_check_ref(self.solver.storage.solver_sequence).get_dot_graph();
777 })
778 .def(
779 "get_box_dim_fcc_3d",
780 [](T &self, f64 dr, u32 xcnt, u32 ycnt, u32 zcnt) {
781 return self.get_box_dim_fcc_3d(dr, xcnt, ycnt, zcnt);
782 })
783 .def(
784 "get_ideal_fcc_box",
785 [](T &self, f64 dr, f64_3 box_min, f64_3 box_max) {
786 ON_RANK_0(
788 "SPH",
789 "The python function get_ideal_fcc_box is deprecated in the SPH model and "
790 "will be removed at some point, replace it by "
791 "shamrock.math.get_ideal_hcp_box"));
792 return shammath::LatticeHCP<f64_3>::get_ideal_hcp_box(dr, {box_min, box_max});
793 })
794 .def(
795 "get_ideal_hcp_box",
796 [](T &self, f64 dr, f64_3 box_min, f64_3 box_max) {
797 ON_RANK_0(
799 "SPH",
800 "The python function get_ideal_hcp_box is deprecated in the SPH model and "
801 "will be removed at some point, replace it by "
802 "shamrock.math.get_ideal_hcp_box"));
803 return shammath::LatticeHCP<f64_3>::get_ideal_hcp_box(dr, {box_min, box_max});
804 })
805 .def(
806 "resize_simulation_box",
807 [](T &self, f64_3 box_min, f64_3 box_max) {
808 return self.resize_simulation_box({box_min, box_max});
809 })
810 .def(
811 "push_particle",
812 [](T &self, std::vector<f64_3> pos, std::vector<f64> hpart, std::vector<f64> upart) {
813 return self.push_particle(pos, hpart, upart);
814 })
815 .def(
816 "push_particle_mhd",
817 [](T &self,
818 std::vector<f64_3> pos,
819 std::vector<f64> hpart,
820 std::vector<f64> upart,
821 std::vector<f64_3> B_on_rho,
822 std::vector<f64> psi_on_ch) {
823 return self.push_particle_mhd(pos, hpart, upart, B_on_rho, psi_on_ch);
824 })
825 .def(
826 "add_cube_fcc_3d",
827 [](T &self, f64 dr, f64_3 box_min, f64_3 box_max) {
828 return self.add_cube_fcc_3d(dr, {box_min, box_max});
829 })
830 .def(
831 "add_cube_hcp_3d",
832 [](T &self, f64 dr, f64_3 box_min, f64_3 box_max) {
833 return self.add_cube_hcp_3d(dr, {box_min, box_max});
834 })
835 .def(
836 "add_cube_hcp_3d_v2",
837 [](T &self, f64 dr, f64_3 box_min, f64_3 box_max) {
838 return self.add_cube_hcp_3d_v2(dr, {box_min, box_max});
839 })
840 .def(
841 "add_disc_3d_keplerian",
842 [](T &self,
843 Tvec center,
844 u32 Npart,
845 Tscal p,
846 Tscal rho_0,
847 Tscal m,
848 Tscal r_in,
849 Tscal r_out,
850 Tscal q,
851 Tscal cmass) {
852 return self.add_cube_disc_3d(center, Npart, p, rho_0, m, r_in, r_out, q, cmass);
853 })
854 .def(
855 "add_disc_3d",
856 [](T &self,
857 Tvec center,
858 Tscal central_mass,
859 u32 Npart,
860 Tscal r_in,
861 Tscal r_out,
862 Tscal disc_mass,
863 Tscal p,
864 Tscal H_r_in,
865 Tscal q) {
866 return self.add_disc_3d(
867 center, central_mass, Npart, r_in, r_out, disc_mass, p, H_r_in, q);
868 })
869 .def(
870 "add_big_disc_3d",
871 [](T &self,
872 Tvec center,
873 Tscal central_mass,
874 u32 Npart,
875 Tscal r_in,
876 Tscal r_out,
877 Tscal disc_mass,
878 Tscal p,
879 Tscal H_r_in,
880 Tscal q,
881 u16 seed) {
882 self.add_big_disc_3d(
883 center,
884 central_mass,
885 Npart,
886 r_in,
887 r_out,
888 disc_mass,
889 p,
890 H_r_in,
891 q,
892 std::mt19937{seed});
893 return disc_mass / Npart;
894 })
895 .def("get_total_part_count", &T::get_total_part_count)
896 .def("total_mass_to_part_mass", &T::total_mass_to_part_mass)
897 .def(
898 "set_value_in_a_box",
899 [](T &self,
900 const std::string &field_name,
901 const std::string &field_type,
902 const pybind11::object &value,
903 f64_3 box_min,
904 f64_3 box_max,
905 u32 ivar) {
906 if (field_type == "f64") {
907 f64 val = value.cast<f64>();
908 self.set_value_in_a_box(field_name, val, {box_min, box_max}, ivar);
909 } else if (field_type == "f64_3") {
910 f64_3 val = value.cast<f64_3>();
911 self.set_value_in_a_box(field_name, val, {box_min, box_max}, ivar);
912 } else {
914 "unknown field type");
915 }
916 },
917 py::arg("field_name"),
918 py::arg("field_type"),
919 py::arg("value"),
920 py::arg("box_min"),
921 py::arg("box_max"),
922 py::kw_only(),
923 py::arg("ivar") = 0)
924 .def(
925 "set_value_in_sphere",
926 [](T &self,
927 const std::string &field_name,
928 const std::string &field_type,
929 const pybind11::object &value,
930 f64_3 center,
931 f64 radius) {
932 if (field_type == "f64") {
933 f64 val = value.cast<f64>();
934 self.set_value_in_sphere(field_name, val, center, radius);
935 } else if (field_type == "f64_3") {
936 f64_3 val = value.cast<f64_3>();
937 self.set_value_in_sphere(field_name, val, center, radius);
938 } else {
940 "unknown field type");
941 }
942 })
943 .def(
944 "set_field_value_lambda_f64",
945 [](T &self,
946 std::string field_name,
947 const std::function<f64(Tvec)> pos_to_val,
948 const u32 offset) {
949 return self.template set_field_value_lambda<f64>(
950 std::move(field_name), pos_to_val, offset);
951 },
952 py::arg("field_name"),
953 py::arg("pos_to_val"),
954 py::arg("offset") = 0)
955 .def(
956 "set_field_value_lambda_f64_3",
957 [](T &self,
958 std::string field_name,
959 const std::function<f64_3(Tvec)> pos_to_val,
960 const u32 offset) {
961 return self.template set_field_value_lambda<f64_3>(
962 std::move(field_name), pos_to_val, offset);
963 },
964 py::arg("field_name"),
965 py::arg("pos_to_val"),
966 py::arg("offset") = 0)
967 .def("overwrite_field_value_f64", &T::template overwrite_field_value<f64>)
968 .def("overwrite_field_value_f64_3", &T::template overwrite_field_value<f64_3>)
969 .def("remap_positions", &T::remap_positions)
970 //.def("set_field_value_lambda_f64_3",[](T&self,std::string field_name, const
971 // std::function<f64_3 (Tscal, Tscal , Tscal)> pos_to_val){
972 // self.template set_field_value_lambda<f64_3>(field_name, [=](Tvec v){
973 // return pos_to_val(v.x(), v.y(),v.z());
974 // });
975 //})
976 .def(
977 "add_kernel_value",
978 [](T &self,
979 const std::string &field_name,
980 const std::string &field_type,
981 const pybind11::object &value,
982 f64_3 center,
983 f64 h_ker) {
984 if (field_type == "f64") {
985 f64 val = value.cast<f64>();
986 self.add_kernel_value(field_name, val, center, h_ker);
987 } else if (field_type == "f64_3") {
988 f64_3 val = value.cast<f64_3>();
989 self.add_kernel_value(field_name, val, center, h_ker);
990 } else {
992 "unknown field type");
993 }
994 })
995 .def(
996 "get_sum",
997 [](T &self, const std::string &field_name, const std::string &field_type) {
998 if (field_type == "f64") {
999 return py::cast(self.template get_sum<f64>(field_name));
1000 } else if (field_type == "f64_3") {
1001 return py::cast(self.template get_sum<f64_3>(field_name));
1002 } else {
1004 "unknown field type");
1005 }
1006 })
1007 .def(
1008 "get_closest_part_to",
1009 [](T &self, f64_3 pos) -> f64_3 {
1010 return self.get_closest_part_to(pos);
1011 })
1012 .def(
1013 "gen_default_config",
1014 [](T &self) {
1015 return typename T::Solver::Config{};
1016 })
1017 .def(
1018 "get_current_config",
1019 [](T &self) {
1020 return self.solver.solver_config;
1021 })
1022 .def("set_solver_config", &T::set_solver_config)
1023 .def("add_sink", &T::add_sink)
1024 .def(
1025 "get_sinks",
1026 [](T &self) {
1027 py::list list_out;
1028
1029 if (!self.solver.storage.sinks.is_empty()) {
1030 for (auto &sink : self.solver.storage.sinks.get()) {
1031 py::dict sink_dic;
1032 sink_dic["pos"] = sink.pos;
1033 sink_dic["velocity"] = sink.velocity;
1034 sink_dic["sph_acceleration"] = sink.sph_acceleration;
1035 sink_dic["ext_acceleration"] = sink.ext_acceleration;
1036 sink_dic["mass"] = sink.mass;
1037 sink_dic["angular_momentum"] = sink.angular_momentum;
1038 sink_dic["accretion_radius"] = sink.accretion_radius;
1039 list_out.append(sink_dic);
1040 }
1041 }
1042
1043 return list_out;
1044 })
1045 .def(
1046 "get_units",
1047 [](T &self) {
1048 return self.solver.solver_config.unit_sys;
1049 })
1050 .def(
1051 "render_slice",
1052 [](T &self,
1053 const std::string &name,
1054 const std::string &field_type,
1055 const std::vector<Tvec> &positions,
1056 const std::optional<custom_getter_t> &custom_getter)
1057 -> std::variant<std::vector<f64>, std::vector<f64_3>> {
1058 if (custom_getter.has_value()) {
1059 if (!(name == "custom" && field_type == "f64")) {
1061 "custom_getter only available for name=custom and field_type=f64");
1062 }
1063 }
1064
1065 if (field_type == "f64") {
1067 self.ctx, self.solver.solver_config, self.solver.storage);
1068 return render.compute_slice(name, positions, custom_getter).copy_to_stdvec();
1069 }
1070
1071 if (field_type == "f64_3") {
1073 self.ctx, self.solver.solver_config, self.solver.storage);
1074 return render.compute_slice(name, positions, std::nullopt).copy_to_stdvec();
1075 }
1076
1077 throw shambase::make_except_with_loc<std::runtime_error>("unknown field type");
1078 },
1079 py::arg("name"),
1080 py::arg("field_type"),
1081 py::arg("positions"),
1082 py::arg("custom_getter") = std::nullopt)
1083 .def(
1084 "render_column_integ",
1085 [](T &self,
1086 const std::string &name,
1087 const std::string &field_type,
1088 const std::vector<shammath::Ray<Tvec>> &rays,
1089 const std::optional<custom_getter_t> &custom_getter)
1090 -> std::variant<std::vector<f64>, std::vector<f64_3>> {
1091 if (custom_getter.has_value()) {
1092 if (!(name == "custom" && field_type == "f64")) {
1094 "custom_getter only available for name=custom and field_type=f64");
1095 }
1096 }
1097
1098 if (field_type == "f64") {
1100 self.ctx, self.solver.solver_config, self.solver.storage);
1101 return render.compute_column_integ(name, rays, custom_getter).copy_to_stdvec();
1102 }
1103
1104 if (field_type == "f64_3") {
1106 self.ctx, self.solver.solver_config, self.solver.storage);
1107 return render.compute_column_integ(name, rays, std::nullopt).copy_to_stdvec();
1108 }
1109
1110 throw shambase::make_except_with_loc<std::runtime_error>("unknown field type");
1111 },
1112 py::arg("name"),
1113 py::arg("field_type"),
1114 py::arg("rays"),
1115 py::arg("custom_getter") = std::nullopt)
1116 .def(
1117 "compute_field",
1118 [](T &self,
1119 const std::string &name,
1120 const std::string &field_type,
1121 const std::optional<custom_getter_t> &custom_getter)
1122 -> std::variant<
1125 if (custom_getter.has_value()) {
1126 if (!(name == "custom" && field_type == "f64")) {
1128 "custom_getter only available for name=custom and field_type=f64");
1129 }
1130 }
1131
1132 if (field_type == "f64") {
1134 self.ctx, self.solver.solver_config, self.solver.storage);
1135 return render_field_getter.build_field(name, custom_getter);
1136 }
1137
1138 if (field_type == "f64_3") {
1140 self.ctx, self.solver.solver_config, self.solver.storage);
1141 return render_field_getter.build_field(name, custom_getter);
1142 }
1143
1144 throw shambase::make_except_with_loc<std::runtime_error>("unknown field type");
1145 },
1146 py::arg("name"),
1147 py::arg("field_type"),
1148 py::arg("custom_getter") = std::nullopt)
1149 .def(
1150 "render_azymuthal_integ",
1151 [](T &self,
1152 const std::string &name,
1153 const std::string &field_type,
1154 const std::vector<shammath::RingRay<Tvec>> &ring_rays,
1155 const std::optional<custom_getter_t> &custom_getter)
1156 -> std::variant<std::vector<f64>, std::vector<f64_3>> {
1157 if (custom_getter.has_value()) {
1158 if (!(name == "custom" && field_type == "f64")) {
1160 "custom_getter only available for name=custom and field_type=f64");
1161 }
1162 }
1163
1164 if (field_type == "f64") {
1166 self.ctx, self.solver.solver_config, self.solver.storage);
1167 return render.compute_azymuthal_integ(name, ring_rays, custom_getter)
1168 .copy_to_stdvec();
1169 }
1170
1171 if (field_type == "f64_3") {
1173 self.ctx, self.solver.solver_config, self.solver.storage);
1174 return render.compute_azymuthal_integ(name, ring_rays, std::nullopt)
1175 .copy_to_stdvec();
1176 }
1177
1178 throw shambase::make_except_with_loc<std::runtime_error>("unknown field type");
1179 },
1180 py::arg("name"),
1181 py::arg("field_type"),
1182 py::arg("ring_rays"),
1183 py::arg("custom_getter") = std::nullopt)
1184 .def(
1185 "render_cartesian_slice",
1186 [](T &self,
1187 const std::string &name,
1188 const std::string &field_type,
1189 Tvec center,
1190 Tvec delta_x,
1191 Tvec delta_y,
1192 u32 nx,
1193 u32 ny,
1194 const std::optional<custom_getter_t> &custom_getter)
1195 -> std::variant<py::array_t<Tscal>> {
1196 if (custom_getter.has_value()) {
1197 if (!(name == "custom" && field_type == "f64")) {
1199 "custom_getter only available for name=custom and field_type=f64");
1200 }
1201 }
1202
1203 if (field_type == "f64") {
1204 py::array_t<Tscal> ret({ny, nx});
1205
1207 self.ctx, self.solver.solver_config, self.solver.storage);
1208
1209 std::vector<f64> slice
1210 = render
1211 .compute_slice(name, center, delta_x, delta_y, nx, ny, custom_getter)
1212 .copy_to_stdvec();
1213
1214 for (u32 iy = 0; iy < ny; iy++) {
1215 for (u32 ix = 0; ix < nx; ix++) {
1216 ret.mutable_at(iy, ix) = slice[ix + nx * iy];
1217 }
1218 }
1219
1220 return ret;
1221 }
1222
1223 if (field_type == "f64_3") {
1224 py::array_t<Tscal> ret({ny, nx, 3_u32});
1225
1227 self.ctx, self.solver.solver_config, self.solver.storage);
1228
1229 std::vector<f64_3> slice
1230 = render.compute_slice(name, center, delta_x, delta_y, nx, ny, std::nullopt)
1231 .copy_to_stdvec();
1232
1233 for (u32 iy = 0; iy < ny; iy++) {
1234 for (u32 ix = 0; ix < nx; ix++) {
1235 ret.mutable_at(iy, ix, 0) = slice[ix + nx * iy][0];
1236 ret.mutable_at(iy, ix, 1) = slice[ix + nx * iy][1];
1237 ret.mutable_at(iy, ix, 2) = slice[ix + nx * iy][2];
1238 }
1239 }
1240
1241 return ret;
1242 }
1243
1245 return py::array_t<Tscal>({nx, ny});
1246 },
1247 py::arg("name"),
1248 py::arg("field_type"),
1249 py::arg("center"),
1250 py::arg("delta_x"),
1251 py::arg("delta_y"),
1252 py::arg("nx"),
1253 py::arg("ny"),
1254 py::arg("custom_getter") = std::nullopt)
1255 .def(
1256 "render_cartesian_column_integ",
1257 [](T &self,
1258 const std::string &name,
1259 const std::string &field_type,
1260 Tvec center,
1261 Tvec delta_x,
1262 Tvec delta_y,
1263 u32 nx,
1264 u32 ny,
1265 const std::optional<custom_getter_t> &custom_getter)
1266 -> std::variant<py::array_t<Tscal>> {
1267 if (custom_getter.has_value()) {
1268 if (!(name == "custom" && field_type == "f64")) {
1270 "custom_getter only available for name=custom and field_type=f64");
1271 }
1272 }
1273
1274 if (field_type == "f64") {
1275 py::array_t<Tscal> ret({ny, nx});
1276
1278 self.ctx, self.solver.solver_config, self.solver.storage);
1279
1280 std::vector<f64> slice
1281 = render
1282 .compute_column_integ(
1283 name, center, delta_x, delta_y, nx, ny, custom_getter)
1284 .copy_to_stdvec();
1285
1286 for (u32 iy = 0; iy < ny; iy++) {
1287 for (u32 ix = 0; ix < nx; ix++) {
1288 ret.mutable_at(iy, ix) = slice[ix + nx * iy];
1289 }
1290 }
1291
1292 return ret;
1293 }
1294
1295 if (field_type == "f64_3") {
1296 py::array_t<Tscal> ret({ny, nx, 3_u32});
1297
1299 self.ctx, self.solver.solver_config, self.solver.storage);
1300
1301 std::vector<f64_3> slice
1302 = render
1303 .compute_column_integ(
1304 name, center, delta_x, delta_y, nx, ny, std::nullopt)
1305 .copy_to_stdvec();
1306
1307 for (u32 iy = 0; iy < ny; iy++) {
1308 for (u32 ix = 0; ix < nx; ix++) {
1309 ret.mutable_at(iy, ix, 0) = slice[ix + nx * iy][0];
1310 ret.mutable_at(iy, ix, 1) = slice[ix + nx * iy][1];
1311 ret.mutable_at(iy, ix, 2) = slice[ix + nx * iy][2];
1312 }
1313 }
1314
1315 return ret;
1316 }
1317
1319 return py::array_t<Tscal>({nx, ny});
1320 },
1321 py::arg("name"),
1322 py::arg("field_type"),
1323 py::arg("center"),
1324 py::arg("delta_x"),
1325 py::arg("delta_y"),
1326 py::arg("nx"),
1327 py::arg("ny"),
1328 py::arg("custom_getter") = std::nullopt)
1329 .def(
1330 "gen_config_from_phantom_dump",
1331 [](T &self, PhantomDump &dump, bool bypass_error) {
1332 return self.gen_config_from_phantom_dump(dump, bypass_error);
1333 },
1334 py::arg("dump"),
1335 py::arg("bypass_error") = false,
1336 R"==(
1337 This function generate a shamrock sph solver config from a phantom dump
1338
1339 Parameters
1340 ----------
1341 PhantomDump dump
1342 bypass_error = false (default) bypass any error in the config
1343)==")
1344 .def(
1345 "init_from_phantom_dump",
1346 [](T &self, PhantomDump &dump, Tscal hpart_fact_load) {
1347 self.init_from_phantom_dump(dump, hpart_fact_load);
1348 },
1349 py::arg("dump"),
1350 py::arg("hpart_fact_load") = 1.0)
1351 .def(
1352 "make_phantom_dump",
1353 [](T &self) {
1354 return self.make_phantom_dump();
1355 })
1356 .def("do_vtk_dump", &T::do_vtk_dump)
1357 .def("set_debug_dump", &T::set_debug_dump)
1358 .def("solver_logs_last_rate", &T::solver_logs_last_rate)
1359 .def("solver_logs_last_obj_count", &T::solver_logs_last_obj_count)
1360 .def(
1361 "solver_logs_last_system_metrics",
1362 [&](T &self) {
1363 auto system_metrics = self.solver.solve_logs.get_last_system_metrics();
1364 py::dict ret;
1365 ret["duration"] = system_metrics.wall_time;
1366 if (system_metrics.rank_energy_consummed.has_value()) {
1367 ret["rank_energy_consummed"] = system_metrics.rank_energy_consummed.value();
1368 }
1369 if (system_metrics.gpu_energy_consummed.has_value()) {
1370 ret["gpu_energy_consummed"] = system_metrics.gpu_energy_consummed.value();
1371 }
1372 if (system_metrics.cpu_energy_consummed.has_value()) {
1373 ret["cpu_energy_consummed"] = system_metrics.cpu_energy_consummed.value();
1374 }
1375 if (system_metrics.dram_energy_consummed.has_value()) {
1376 ret["dram_energy_consummed"] = system_metrics.dram_energy_consummed.value();
1377 }
1378 return ret;
1379 })
1380 .def("solver_logs_cumulated_step_time", &T::solver_logs_cumulated_step_time)
1381 .def("solver_logs_reset_cumulated_step_time", &T::solver_logs_reset_cumulated_step_time)
1382 .def("solver_logs_step_count", &T::solver_logs_step_count)
1383 .def("solver_logs_reset_step_count", &T::solver_logs_reset_step_count)
1384 .def(
1385 "get_time",
1386 [](T &self) {
1387 return self.solver.solver_config.get_time();
1388 })
1389 .def(
1390 "get_dt",
1391 [](T &self) {
1392 return self.solver.solver_config.get_dt_sph();
1393 })
1394 .def(
1395 "set_time",
1396 [](T &self, Tscal t) {
1397 return self.solver.solver_config.set_time(t);
1398 })
1399 .def(
1400 "set_next_dt",
1401 [](T &self, Tscal dt) {
1402 return self.solver.solver_config.set_next_dt(dt);
1403 })
1404 .def(
1405 "set_cfl_multipler",
1406 [](T &self, Tscal lambda) {
1407 return self.solver.solver_config.set_cfl_multipler(lambda);
1408 },
1409 py::arg("lambda"))
1410 .def(
1411 "set_cfl_mult_stiffness",
1412 [](T &self, Tscal cstiff) {
1413 return self.solver.solver_config.set_cfl_mult_stiffness(cstiff);
1414 },
1415 py::arg("cstiff"))
1416 .def(
1417 "change_htolerance",
1418 [](T &self, Tscal in) {
1419 ON_RANK_0(shamlog_warn_ln(
1420 "SPH",
1421 ".change_htolerance(val) is deprecated,\n"
1422 " -> calling this is replaced internally by "
1423 ".change_htolerances(coarse=val, fine=min(val, 1.1))\n"
1424 " see: "
1425 "https://shamrock-code.github.io/Shamrock/mkdocs/models/sph/"
1426 "smoothing_length_tolerance"););
1427 self.change_htolerances(in, std::min(in, (Tscal) 1.1));
1428 })
1429 .def(
1430 "change_htolerances",
1431 [](T &self, Tscal coarse, Tscal fine) {
1432 self.change_htolerances(coarse, fine);
1433 },
1434 py::kw_only(),
1435 py::arg("coarse"),
1436 py::arg("fine"))
1437 .def(
1438 "make_analysis_sodtube",
1439 [](T &self,
1441 Tvec direction,
1442 Tscal time_val,
1443 Tscal x_ref,
1444 Tscal x_min,
1445 Tscal x_max) {
1446 return std::make_unique<TAnalysisSodTube>(
1447 self.ctx,
1448 self.solver.solver_config,
1449 self.solver.storage,
1450 sod,
1451 direction,
1452 time_val,
1453 x_ref,
1454 x_min,
1455 x_max);
1456 },
1457 py::arg("sod"),
1458 py::arg("direction"),
1459 py::arg("time_val"),
1460 py::arg("x_ref"),
1461 py::arg("x_min"),
1462 py::arg("x_max"))
1463 .def(
1464 "make_analysis_disc",
1465 [](T &self) {
1466 return std::make_unique<TAnalysisDisc>(
1467 self.ctx, self.solver.solver_config, self.solver.storage);
1468 })
1469 .def("load_from_dump", &T::load_from_dump)
1470 .def("dump", &T::dump)
1471 .def("get_setup", &T::get_setup)
1472 .def(
1473 "get_patch_transform",
1474 [](T &self) {
1475 PatchScheduler &sched = shambase::get_check_ref(self.ctx.sched);
1476 return sched.get_patch_transform<Tvec>();
1477 })
1478 .def("apply_momentum_offset", &T::apply_momentum_offset)
1479 .def("apply_position_offset", &T::apply_position_offset)
1480 .def(
1481 "add_timestep_callback",
1482 [](T &self,
1483 std::optional<std::function<void(void)>> step_begin_callback,
1484 std::optional<std::function<void(void)>> step_end_callback) {
1485 self.solver.timestep_callbacks.push_back(
1486 {std::move(step_begin_callback), std::move(step_end_callback)});
1487 },
1488 py::kw_only(),
1489 py::arg("step_begin") = std::nullopt,
1490 py::arg("step_end") = std::nullopt);
1491}
1492
1493template<class Tvec, template<class> class SPHKernel>
1494void add_analysisBarycenter_instance(py::module &m, const std::string &name_model) {
1495 using namespace shammodels::sph;
1496
1497 using Tscal = shambase::VecComponent<Tvec>;
1498
1499 using T = Model<Tvec, SPHKernel>;
1500
1501 py::class_<modules::AnalysisBarycenter<Tvec, SPHKernel>>(m, name_model.c_str())
1502 .def(py::init([](T &model) {
1503 return std::make_unique<modules::AnalysisBarycenter<Tvec, SPHKernel>>(model);
1504 }))
1505 .def("get_barycenter", [](modules::AnalysisBarycenter<Tvec, SPHKernel> &self) {
1506 auto result = self.get_barycenter();
1507 return py::make_tuple(result.barycenter, result.mass_disc);
1508 });
1509}
1510
1511template<class Tvec, template<class> class SPHKernel>
1512void add_analysisEnergyKinetic_instance(py::module &m, const std::string &name_model) {
1513 using namespace shammodels::sph;
1514
1515 using Tscal = shambase::VecComponent<Tvec>;
1516 using T = Model<Tvec, SPHKernel>;
1517
1518 py::class_<modules::AnalysisEnergyKinetic<Tvec, SPHKernel>>(m, name_model.c_str())
1519 .def(py::init([](T &model) {
1520 return std::make_unique<modules::AnalysisEnergyKinetic<Tvec, SPHKernel>>(model);
1521 }))
1522 .def("get_kinetic_energy", [](modules::AnalysisEnergyKinetic<Tvec, SPHKernel> &self) {
1523 return self.get_kinetic_energy();
1524 });
1525}
1526
1527template<class Tvec, template<class> class SPHKernel>
1528void add_analysisEnergyPotential_instance(py::module &m, const std::string &name_model) {
1529 using namespace shammodels::sph;
1530
1531 using Tscal = shambase::VecComponent<Tvec>;
1532 using T = Model<Tvec, SPHKernel>;
1533
1534 py::class_<modules::AnalysisEnergyPotential<Tvec, SPHKernel>>(m, name_model.c_str())
1535 .def(py::init([](T &model) {
1536 return std::make_unique<modules::AnalysisEnergyPotential<Tvec, SPHKernel>>(model);
1537 }))
1538 .def("get_potential_energy", [](modules::AnalysisEnergyPotential<Tvec, SPHKernel> &self) {
1539 return self.get_potential_energy();
1540 });
1541}
1542
1543template<class Tvec, template<class> class SPHKernel>
1544void add_analysisTotalMomentum_instance(py::module &m, const std::string &name_model) {
1545 using namespace shammodels::sph;
1546
1547 using Tscal = shambase::VecComponent<Tvec>;
1548 using T = Model<Tvec, SPHKernel>;
1549
1550 py::class_<modules::AnalysisTotalMomentum<Tvec, SPHKernel>>(m, name_model.c_str())
1551 .def(py::init([](T &model) {
1552 return std::make_unique<modules::AnalysisTotalMomentum<Tvec, SPHKernel>>(model);
1553 }))
1554 .def("get_total_momentum", [](modules::AnalysisTotalMomentum<Tvec, SPHKernel> &self) {
1555 return self.get_total_momentum();
1556 });
1557}
1558
1559template<class Tvec, template<class> class SPHKernel>
1560void add_analysisAngularMomentum_instance(py::module &m, const std::string &name_model) {
1561 using namespace shammodels::sph;
1562
1563 using Tscal = shambase::VecComponent<Tvec>;
1564 using T = Model<Tvec, SPHKernel>;
1565
1566 py::class_<modules::AnalysisAngularMomentum<Tvec, SPHKernel>>(m, name_model.c_str())
1567 .def(py::init([](T &model) {
1568 return std::make_unique<modules::AnalysisAngularMomentum<Tvec, SPHKernel>>(model);
1569 }))
1570 .def("get_angular_momentum", [](modules::AnalysisAngularMomentum<Tvec, SPHKernel> &self) {
1571 return self.get_angular_momentum();
1572 });
1573}
1574
1575template<class Tvec, template<class> class SPHKernel>
1576void add_analysisDustMass_instance(py::module &m, const std::string &name_model) {
1577 using namespace shammodels::sph;
1578
1579 using Tscal = shambase::VecComponent<Tvec>;
1580 using T = Model<Tvec, SPHKernel>;
1581
1582 py::class_<modules::AnalysisDustMass<Tvec, SPHKernel>>(m, name_model.c_str())
1583 .def(py::init([](T &model) {
1584 return std::make_unique<modules::AnalysisDustMass<Tvec, SPHKernel>>(model);
1585 }))
1586 .def("get_dust_mass", [](modules::AnalysisDustMass<Tvec, SPHKernel> &self) {
1587 return self.get_dust_mass();
1588 });
1589}
1590
1591using namespace shammodels::sph;
1592
1593template<class Analysis, typename Tvec, template<class> class SPHKernel>
1594auto analysis_impl(shammodels::sph::Model<Tvec, SPHKernel> &model) -> Analysis {
1595 return Analysis(model);
1596}
1597
1598template<template<class, template<class> class> class Analysis>
1599void register_analysis_impl_for_each_kernel(py::module &msph, const char *name_class) {
1600 using namespace shammodels::sph;
1601
1602 using SPHModel_f64_3_M4 = shammodels::sph::Model<f64_3, shammath::M4>;
1603 using SPHModel_f64_3_M6 = shammodels::sph::Model<f64_3, shammath::M6>;
1604 using SPHModel_f64_3_M8 = shammodels::sph::Model<f64_3, shammath::M8>;
1605
1606 using SPHModel_f64_3_C2 = shammodels::sph::Model<f64_3, shammath::C2>;
1607 using SPHModel_f64_3_C4 = shammodels::sph::Model<f64_3, shammath::C4>;
1608 using SPHModel_f64_3_C6 = shammodels::sph::Model<f64_3, shammath::C6>;
1609
1610 msph.def(
1611 name_class,
1612 [](SPHModel_f64_3_M4 &model) {
1613 return analysis_impl<Analysis<f64_3, shammath::M4>>(model);
1614 },
1615 py::kw_only(),
1616 py::arg("model"));
1617
1618 msph.def(
1619 name_class,
1620 [](SPHModel_f64_3_M6 &model) {
1621 return analysis_impl<Analysis<f64_3, shammath::M6>>(model);
1622 },
1623 py::kw_only(),
1624 py::arg("model"));
1625
1626 msph.def(
1627 name_class,
1628 [](SPHModel_f64_3_M8 &model) {
1629 return analysis_impl<Analysis<f64_3, shammath::M8>>(model);
1630 },
1631 py::kw_only(),
1632 py::arg("model"));
1633
1634 msph.def(
1635 name_class,
1636 [](SPHModel_f64_3_C2 &model) {
1637 return analysis_impl<Analysis<f64_3, shammath::C2>>(model);
1638 },
1639 py::kw_only(),
1640 py::arg("model"));
1641
1642 msph.def(
1643 name_class,
1644 [](SPHModel_f64_3_C4 &model) {
1645 return analysis_impl<Analysis<f64_3, shammath::C4>>(model);
1646 },
1647 py::kw_only(),
1648 py::arg("model"));
1649
1650 msph.def(
1651 name_class,
1652 [](SPHModel_f64_3_C6 &model) {
1653 return analysis_impl<Analysis<f64_3, shammath::C6>>(model);
1654 },
1655 py::kw_only(),
1656 py::arg("model"));
1657}
1658
1660 auto &m = root_module;
1661
1662 py::module msph = m.def_submodule("model_sph", "Shamrock sph solver");
1663
1664 py::class_<EvolveUntilResults>(m, "EvolveUntilResults")
1665 .def_readwrite("reach_target_time", &EvolveUntilResults::reach_target_time)
1666 .def_readwrite("reach_niter_max", &EvolveUntilResults::reach_niter_max)
1667 .def_readwrite("reach_max_walltime", &EvolveUntilResults::reach_max_walltime)
1668 .def_readwrite("iter_count", &EvolveUntilResults::iter_count)
1669 .def("__repr__", [](const EvolveUntilResults &self) {
1670 return shambase::format(
1671 "EvolveUntilResults(reach_target_time={}, reach_niter_max={}, "
1672 "reach_max_walltime={}, iter_count={})",
1673 self.reach_target_time,
1674 self.reach_niter_max,
1675 self.reach_max_walltime,
1676 self.iter_count);
1677 });
1678
1679 using namespace shammodels::sph;
1680
1681 add_instance<f64_3, shammath::M4>(msph, "SPHModel_f64_3_M4_SolverConfig", "SPHModel_f64_3_M4");
1682 add_instance<f64_3, shammath::M6>(msph, "SPHModel_f64_3_M6_SolverConfig", "SPHModel_f64_3_M6");
1683 add_instance<f64_3, shammath::M8>(msph, "SPHModel_f64_3_M8_SolverConfig", "SPHModel_f64_3_M8");
1684
1685 add_instance<f64_3, shammath::C2>(msph, "SPHModel_f64_3_C2_SolverConfig", "SPHModel_f64_3_C2");
1686 add_instance<f64_3, shammath::C4>(msph, "SPHModel_f64_3_C4_SolverConfig", "SPHModel_f64_3_C4");
1687 add_instance<f64_3, shammath::C6>(msph, "SPHModel_f64_3_C6_SolverConfig", "SPHModel_f64_3_C6");
1688
1689 using VariantSPHModelBind = std::variant<
1690 std::unique_ptr<Model<f64_3, shammath::M4>>,
1691 std::unique_ptr<Model<f64_3, shammath::M6>>,
1692 std::unique_ptr<Model<f64_3, shammath::M8>>,
1693 std::unique_ptr<Model<f64_3, shammath::C2>>,
1694 std::unique_ptr<Model<f64_3, shammath::C4>>,
1695 std::unique_ptr<Model<f64_3, shammath::C6>>>;
1696
1697 m.def(
1698 "get_Model_SPH",
1699 [](ShamrockCtx &ctx,
1700 const std::string &vector_type,
1701 const std::string &kernel) -> VariantSPHModelBind {
1702 VariantSPHModelBind ret;
1703
1704 if (vector_type == "f64_3" && kernel == "M4") {
1705 ret = std::make_unique<Model<f64_3, shammath::M4>>(ctx);
1706 } else if (vector_type == "f64_3" && kernel == "M6") {
1707 ret = std::make_unique<Model<f64_3, shammath::M6>>(ctx);
1708 } else if (vector_type == "f64_3" && kernel == "M8") {
1709 ret = std::make_unique<Model<f64_3, shammath::M8>>(ctx);
1710 } else if (vector_type == "f64_3" && kernel == "C2") {
1711 ret = std::make_unique<Model<f64_3, shammath::C2>>(ctx);
1712 } else if (vector_type == "f64_3" && kernel == "C4") {
1713 ret = std::make_unique<Model<f64_3, shammath::C4>>(ctx);
1714 } else if (vector_type == "f64_3" && kernel == "C6") {
1715 ret = std::make_unique<Model<f64_3, shammath::C6>>(ctx);
1716 } else {
1718 "unknown combination of representation and kernel");
1719 }
1720
1721 return ret;
1722 },
1723 py::kw_only(),
1724 py::arg("context"),
1725 py::arg("vector_type"),
1726 py::arg("sph_kernel"));
1727
1728 py::class_<
1730 std::shared_ptr<shammodels::sph::modules::ISPHSetupNode>>(msph, "ISPHSetupNode")
1731 .def("get_dot", [](std::shared_ptr<shammodels::sph::modules::ISPHSetupNode> &self) {
1732 return self->get_dot();
1733 });
1734
1735 py::class_<shammodels::sph::TimestepLog>(msph, "TimestepLog")
1736 .def(py::init<>())
1737 .def_readwrite("rank", &shammodels::sph::TimestepLog::rank)
1738 .def_readwrite("rate", &shammodels::sph::TimestepLog::rate)
1739 .def_readwrite("npart", &shammodels::sph::TimestepLog::npart)
1740 .def_readwrite("tcompute", &shammodels::sph::TimestepLog::tcompute)
1741 .def("rate_sum", &shammodels::sph::TimestepLog::rate_sum)
1742 .def("npart_sum", &shammodels::sph::TimestepLog::npart_sum);
1743
1744 add_analysisBarycenter_instance<f64_3, shammath::M4>(msph, "AnalysisBarycenter_f64_3_M4");
1745 add_analysisBarycenter_instance<f64_3, shammath::M6>(msph, "AnalysisBarycenter_f64_3_M6");
1746 add_analysisBarycenter_instance<f64_3, shammath::M8>(msph, "AnalysisBarycenter_f64_3_M8");
1747
1748 add_analysisBarycenter_instance<f64_3, shammath::C2>(msph, "AnalysisBarycenter_f64_3_C2");
1749 add_analysisBarycenter_instance<f64_3, shammath::C4>(msph, "AnalysisBarycenter_f64_3_C4");
1750 add_analysisBarycenter_instance<f64_3, shammath::C6>(msph, "AnalysisBarycenter_f64_3_C6");
1751
1752 add_analysisEnergyKinetic_instance<f64_3, shammath::M4>(msph, "AnalysisEnergyKinetic_f64_3_M4");
1753 add_analysisEnergyKinetic_instance<f64_3, shammath::M6>(msph, "AnalysisEnergyKinetic_f64_3_M6");
1754 add_analysisEnergyKinetic_instance<f64_3, shammath::M8>(msph, "AnalysisEnergyKinetic_f64_3_M8");
1755
1756 add_analysisEnergyKinetic_instance<f64_3, shammath::C2>(msph, "AnalysisEnergyKinetic_f64_3_C2");
1757 add_analysisEnergyKinetic_instance<f64_3, shammath::C4>(msph, "AnalysisEnergyKinetic_f64_3_C4");
1758 add_analysisEnergyKinetic_instance<f64_3, shammath::C6>(msph, "AnalysisEnergyKinetic_f64_3_C6");
1759
1760 add_analysisEnergyPotential_instance<f64_3, shammath::M4>(
1761 msph, "AnalysisEnergyPotential_f64_3_M4");
1762 add_analysisEnergyPotential_instance<f64_3, shammath::M6>(
1763 msph, "AnalysisEnergyPotential_f64_3_M6");
1764 add_analysisEnergyPotential_instance<f64_3, shammath::M8>(
1765 msph, "AnalysisEnergyPotential_f64_3_M8");
1766
1767 add_analysisEnergyPotential_instance<f64_3, shammath::C2>(
1768 msph, "AnalysisEnergyPotential_f64_3_C2");
1769 add_analysisEnergyPotential_instance<f64_3, shammath::C4>(
1770 msph, "AnalysisEnergyPotential_f64_3_C4");
1771 add_analysisEnergyPotential_instance<f64_3, shammath::C6>(
1772 msph, "AnalysisEnergyPotential_f64_3_C6");
1773
1774 add_analysisTotalMomentum_instance<f64_3, shammath::M4>(msph, "AnalysisTotalMomentum_f64_3_M4");
1775 add_analysisTotalMomentum_instance<f64_3, shammath::M6>(msph, "AnalysisTotalMomentum_f64_3_M6");
1776 add_analysisTotalMomentum_instance<f64_3, shammath::M8>(msph, "AnalysisTotalMomentum_f64_3_M8");
1777
1778 add_analysisTotalMomentum_instance<f64_3, shammath::C2>(msph, "AnalysisTotalMomentum_f64_3_C2");
1779 add_analysisTotalMomentum_instance<f64_3, shammath::C4>(msph, "AnalysisTotalMomentum_f64_3_C4");
1780 add_analysisTotalMomentum_instance<f64_3, shammath::C6>(msph, "AnalysisTotalMomentum_f64_3_C6");
1781
1782 add_analysisAngularMomentum_instance<f64_3, shammath::M4>(
1783 msph, "AnalysisAngularMomentum_f64_3_M4");
1784 add_analysisAngularMomentum_instance<f64_3, shammath::M6>(
1785 msph, "AnalysisAngularMomentum_f64_3_M6");
1786 add_analysisAngularMomentum_instance<f64_3, shammath::M8>(
1787 msph, "AnalysisAngularMomentum_f64_3_M8");
1788
1789 add_analysisAngularMomentum_instance<f64_3, shammath::C2>(
1790 msph, "AnalysisAngularMomentum_f64_3_C2");
1791 add_analysisAngularMomentum_instance<f64_3, shammath::C4>(
1792 msph, "AnalysisAngularMomentum_f64_3_C4");
1793 add_analysisAngularMomentum_instance<f64_3, shammath::C6>(
1794 msph, "AnalysisAngularMomentum_f64_3_C6");
1795
1796 register_analysis_impl_for_each_kernel<modules::AnalysisBarycenter>(msph, "analysisBarycenter");
1797 register_analysis_impl_for_each_kernel<modules::AnalysisEnergyKinetic>(
1798 msph, "analysisEnergyKinetic");
1799 register_analysis_impl_for_each_kernel<modules::AnalysisEnergyPotential>(
1800 msph, "analysisEnergyPotential");
1801 register_analysis_impl_for_each_kernel<modules::AnalysisTotalMomentum>(
1802 msph, "analysisTotalMomentum");
1803 register_analysis_impl_for_each_kernel<modules::AnalysisAngularMomentum>(
1804 msph, "analysisAngularMomentum");
1805
1806 add_analysisDustMass_instance<f64_3, shammath::M4>(msph, "AnalysisDustMass_f64_3_M4");
1807 add_analysisDustMass_instance<f64_3, shammath::M6>(msph, "AnalysisDustMass_f64_3_M6");
1808 add_analysisDustMass_instance<f64_3, shammath::M8>(msph, "AnalysisDustMass_f64_3_M8");
1809
1810 add_analysisDustMass_instance<f64_3, shammath::C2>(msph, "AnalysisDustMass_f64_3_C2");
1811 add_analysisDustMass_instance<f64_3, shammath::C4>(msph, "AnalysisDustMass_f64_3_C4");
1812 add_analysisDustMass_instance<f64_3, shammath::C6>(msph, "AnalysisDustMass_f64_3_C6");
1813
1814 register_analysis_impl_for_each_kernel<modules::AnalysisDustMass>(msph, "analysisDustMass");
1815}
AnalysisAngularMomentum class.
AnalysisBarycenter class with one method AnalysisBarycenter.get_barycenter().
AnalysisDustMass class.
AnalysisEnergyKinetic class with one method AnalysisEnergyKinetic.get_kinetic_energy().
AnalysisEnergyPotential class with one method AnalysisEnergyPotential.get_potential_energy().
AnalysisTotalMomentum class with one method AnalysisTotalMomentum.get_total_momentum().
MPI scheduler.
double f64
Alias for double.
std::uint32_t u32
32 bit unsigned integer
std::uint64_t u64
64 bit unsigned integer
std::uint16_t u16
16 bit unsigned integer
std::int32_t i32
32 bit integer
The MPI scheduler.
The shamrock SPH model.
Definition Model.hpp:55
This class is an interface that all SPH setup nodes must implement. It describe an operation associat...
This header file contains utility functions related to exception handling in the code.
void throw_with_loc(std::string message, SourceLocation loc=SourceLocation{})
Throw an exception and append the source location to it.
T & get_check_ref(const std::unique_ptr< T > &ptr, SourceLocation loc=SourceLocation())
Takes a std::unique_ptr and returns a reference to the object it holds. It throws a std::runtime_erro...
Definition memory.hpp:110
ExcptTypes make_except_with_loc(std::string message, SourceLocation loc=SourceLocation{})
Create an exception with a message and a location.
i32 world_rank()
Gives the rank of the current process in the MPI communicator.
Definition worldInfo.cpp:40
std::shared_ptr< ISPHSetupNode > SetupNodePtr
Alias for a shared pointer to an ISPHSetupNode.
namespace for the sph model
Pybind11 include and definitions.
#define ON_PYTHON_INIT
Register a Python module init function using static initialization.
void warn_ln(std::string module_name, Types... var2)
Prints a log message with multiple arguments followed by a newline.
Definition logs.hpp:133
Utilities to convert JSON objects to Python objects and vice versa. TODO: try to convert directly wit...
sph kernels
Ray representation for intersection testing.
Definition AABB.hpp:34
Ring ray representation for intersection testing.
Definition AABB.hpp:67
Class representing a Phantom dump file.
Functions related to the MPI communicator.
#define ON_RANK_0(x)
Macro to execute code only on rank 0.
Definition worldInfo.hpp:73