45#include <experimental/mdspan>
46#include <pybind11/cast.h>
47#include <pybind11/numpy.h>
48#include <pybind11/pytypes.h>
54template<
class Tvec,
template<
class>
class SPHKernel>
55void add_instance(py::module &m, std::string name_config, std::string name_model) {
58 using Tscal = shambase::VecComponent<Tvec>;
65 using TConfig =
typename T::Solver::Config;
70 shamlog_debug_ln(
"[Py]",
"registering class :", name_config,
typeid(T).name());
71 shamlog_debug_ln(
"[Py]",
"registering class :", name_model,
typeid(T).name());
73 py::class_<TConfig> config_cls(m, name_config.c_str());
75 shammodels::common::add_json_defs<TConfig>(config_cls);
77 config_cls.def(
"print_status", &TConfig::print_status)
78 .def(
"set_particle_tracking", &TConfig::set_particle_tracking)
80 "set_scheduler_config",
81 [](TConfig &self,
u64 split_crit,
u64 merge_crit) {
82 self.scheduler_conf.split_load_value = split_crit;
83 self.scheduler_conf.merge_load_value = merge_crit;
86 py::arg(
"split_load_value"),
87 py::arg(
"merge_load_value"))
88 .def(
"set_tree_reduction_level", &TConfig::set_tree_reduction_level)
90 "set_neigh_cache_strategy",
91 &TConfig::set_neigh_cache_strategy,
93 Set the strategy used to build the neighbours cache.
97 strategy : NeighCacheStrategy
98 Either ``NeighCacheStrategy.SingleStage`` or ``NeighCacheStrategy.TwoStage``
99 (the default), as obtained from ``from shamrock import NeighCacheStrategy``.
102 "set_two_stage_search",
103 &TConfig::set_two_stage_search,
105 Set the neighbours cache strategy from a boolean.
108 Use :py:meth:`set_neigh_cache_strategy` instead.
110 .def("set_show_neigh_stats", &TConfig::set_show_neigh_stats)
112 "set_max_neigh_cache_size",
113 [](TConfig &self,
const py::object &max_neigh_cache_size) {
116 ".set_max_neigh_cache_size() is deprecated,\n"
117 " -> calling this is a no-op,\n"
118 " -> you can remove the call to that function"););
120 .def(
"set_smoothing_length_density_based", &TConfig::set_smoothing_length_density_based)
122 "set_smoothing_length_density_based_neigh_lim",
123 &TConfig::set_smoothing_length_density_based_neigh_lim)
124 .def(
"set_enable_particle_reordering", &TConfig::set_enable_particle_reordering)
125 .def(
"set_particle_reordering_step_freq", &TConfig::set_particle_reordering_step_freq)
126 .def(
"set_show_ghost_zone_graph", &TConfig::set_show_ghost_zone_graph)
127 .def(
"use_luminosity", &TConfig::use_luminosity)
128 .def(
"compute_GW", &TConfig::use_GW)
129 .def(
"set_save_dt_to_fields", &TConfig::set_save_dt_to_fields)
130 .def(
"should_save_dt_to_fields", &TConfig::should_save_dt_to_fields)
131 .def(
"set_eos_isothermal", &TConfig::set_eos_isothermal)
132 .def(
"set_eos_adiabatic", &TConfig::set_eos_adiabatic)
133 .def(
"set_eos_polytropic", &TConfig::set_eos_polytropic)
134 .def(
"set_eos_locally_isothermal", &TConfig::set_eos_locally_isothermal)
136 "set_eos_locally_isothermalLP07",
137 [](TConfig &self, Tscal cs0, Tscal q, Tscal r0) {
138 self.set_eos_locally_isothermalLP07(cs0, q, r0);
145 "set_eos_locally_isothermalFA2014",
146 [](TConfig &self, Tscal h_over_r) {
147 self.set_eos_locally_isothermalFA2014(h_over_r);
152 "set_eos_locally_isothermalFA2014_extended",
153 [](TConfig &self, Tscal cs0, Tscal q, Tscal r0,
u32 n_sinks) {
154 self.set_eos_locally_isothermalFA2014_extended(cs0, q, r0, n_sinks);
163 [](TConfig &self, Tscal mu_e) {
164 self.set_eos_fermi(mu_e);
168 .def(
"set_artif_viscosity_None", &TConfig::set_artif_viscosity_None)
170 "set_artif_viscosity_Constant",
171 [](TConfig &self, Tscal alpha_u, Tscal alpha_AV, Tscal beta_AV) {
172 self.set_artif_viscosity_Constant({alpha_u, alpha_AV, beta_AV});
179 "set_artif_viscosity_VaryingMM97",
186 self.set_artif_viscosity_VaryingMM97(
187 {alpha_min, alpha_max, sigma_decay, alpha_u, beta_AV});
190 py::arg(
"alpha_min"),
191 py::arg(
"alpha_max"),
192 py::arg(
"sigma_decay"),
196 "set_artif_viscosity_VaryingCD10",
203 self.set_artif_viscosity_VaryingCD10(
204 {alpha_min, alpha_max, sigma_decay, alpha_u, beta_AV});
207 py::arg(
"alpha_min"),
208 py::arg(
"alpha_max"),
209 py::arg(
"sigma_decay"),
213 "set_artif_viscosity_ConstantDisc",
214 [](TConfig &self, Tscal alpha_AV, Tscal alpha_u, Tscal beta_AV) {
215 self.set_artif_viscosity_ConstantDisc({alpha_AV, alpha_u, beta_AV});
223 [](TConfig &self, Tscal sigma_mhd, Tscal sigma_u) {
224 self.set_IdealMHD({sigma_mhd, sigma_u});
227 py::arg(
"sigma_mhd"),
230 "set_self_gravity_none",
232 self.self_grav_config.set_none();
235 "set_self_gravity_direct",
236 [](TConfig &self,
bool reference_mode =
false) {
237 self.self_grav_config.set_direct(reference_mode);
240 py::arg(
"reference_mode") =
false)
242 "set_self_gravity_mm",
243 [](TConfig &self,
u32 mm_order,
f64 opening_angle,
u32 reduction_level) {
244 self.self_grav_config.set_mm(mm_order, opening_angle, reduction_level);
248 py::arg(
"opening_angle"),
249 py::arg(
"reduction_level") = 3)
251 "set_self_gravity_fmm",
252 [](TConfig &self,
u32 order,
f64 opening_angle,
u32 reduction_level) {
253 self.self_grav_config.set_fmm(order, opening_angle, reduction_level);
257 py::arg(
"opening_angle"),
258 py::arg(
"reduction_level") = 3)
260 "set_self_gravity_sfmm",
265 u32 reduction_level) {
266 self.self_grav_config.set_sfmm(
267 sfmm_order, opening_angle, leaf_lowering, reduction_level);
271 py::arg(
"opening_angle"),
272 py::arg(
"leaf_lowering") =
true,
273 py::arg(
"reduction_level") = 3)
275 "set_softening_plummer",
276 [](TConfig &self,
f64 epsilon) {
277 self.self_grav_config.set_softening_plummer(epsilon);
282 "set_softening_none",
284 self.self_grav_config.set_softening_none();
286 .def(
"set_boundary_free", &TConfig::set_boundary_free)
287 .def(
"set_boundary_periodic", &TConfig::set_boundary_periodic)
288 .def(
"set_boundary_shearing_periodic", &TConfig::set_boundary_shearing_periodic)
290 "set_dust_mode_none",
292 self.dust_config.set_none();
295 "set_dust_mode_monofluid_tva",
298 bool pure_diffusion_mode,
301 Tscal cfl_density_threshold,
302 bool ensure_s_j_positivity,
303 bool smooth_s_positivity_limiter,
304 bool dust_corrected_av,
305 std::optional<Tscal> clamp_dust_frac) {
306 self.dust_config.set_monofluid_tva(
311 cfl_density_threshold,
312 ensure_s_j_positivity,
313 smooth_s_positivity_limiter,
319 py::arg(
"pure_diffusion_mode") =
false,
320 py::arg(
"C_1_fluid") = 0.1,
321 py::arg(
"C_drift") = 1.0,
322 py::arg(
"cfl_density_threshold") = shambase::get_epsilon<Tscal>(),
323 py::arg(
"ensure_s_j_positivity") =
true,
324 py::arg(
"smooth_s_positivity_limiter") =
false,
325 py::arg(
"dust_corrected_av") =
false,
326 py::arg(
"clamp_dust_frac") = std::nullopt)
328 "set_dust_mode_monofluid_complete",
329 [](TConfig &self,
u32 ndust) {
330 self.dust_config.set_monofluid_complete(ndust);
335 "set_dust_drag_constant",
336 [](TConfig &self, std::vector<Tscal> ts) {
337 self.dust_config.set_drag_constant({.stopping_times = std::move(ts)});
340 "set_dust_drag_epstein",
343 std::vector<Tscal> grain_sizes,
344 std::vector<Tscal> grain_densities) {
345 self.dust_config.set_drag_epstein(
347 .grains_sizes = std::move(grain_sizes),
348 .grains_densities = std::move(grain_densities)});
351 py::arg(
"grain_sizes"),
352 py::arg(
"grain_densities"))
354 "set_dust_evol_coala_coag",
358 std::vector<Tscal> massgrid,
359 py::array_t<Tscal> tabflux_coag) {
360 if (massgrid.size() == 0) {
362 "massgrid must not be empty");
365 u32 nbins = massgrid.size() - 1;
370 if (tabflux_coag.ndim() != 3) {
372 "tabflux_coag must be a 3D array, got ndim="
373 + std::to_string(tabflux_coag.ndim()));
377 if (tabflux_coag.shape(0) != nbins || tabflux_coag.shape(1) != nbins
378 || tabflux_coag.shape(2) != nbins) {
380 "tabflux_coag must be a 3D array of shape (nbins, nbins, nbins) with "
382 + std::to_string(nbins) +
" (massgrid.size() - 1), got shape ("
383 + std::to_string(tabflux_coag.shape(0)) +
", "
384 + std::to_string(tabflux_coag.shape(1)) +
", "
385 + std::to_string(tabflux_coag.shape(2)) +
")");
388 std::vector<Tscal> tabflux_coag_vec(nbins * nbins * nbins);
390 using mdspan_rank_3 = std::mdspan<Tscal, std::dextents<u32, 3>>;
391 mdspan_rank_3 tabflux_coag_mdspan(tabflux_coag_vec.data(), nbins, nbins, nbins);
393 for (
u32 i = 0; i < nbins; i++) {
394 for (
u32 j = 0; j < nbins; j++) {
395 for (
u32 k = 0; k < nbins; k++) {
396 tabflux_coag_mdspan(i, j, k) = tabflux_coag.mutable_at(i, j, k);
401 self.dust_config.set_dust_evol_coala(
402 {.rhodust_eps = rhodust_eps,
404 .massgrid = massgrid,
405 .tabflux_coag = tabflux_coag_vec});
407 py::arg(
"rhodust_eps"),
410 py::arg(
"tabflux_coag"))
412 "set_dust_ballabio_ts_limiter",
413 [](TConfig &self,
bool enabled) {
414 self.dust_config.ballabio_ts_limiter = enabled;
418 "add_ext_force_point_mass",
419 [](TConfig &self, Tscal central_mass, Tscal Racc, Tvec central_pos) {
420 self.add_ext_force_point_mass(central_mass, Racc, central_pos);
422 py::arg(
"central_mass"),
425 py::arg(
"central_pos") = Tvec{0, 0, 0})
426 .def(
"add_ext_force_paczynski_wiita", &TConfig::add_ext_force_paczynski_wiita)
428 "add_ext_force_lense_thirring",
435 self.add_ext_force_lense_thirring(
436 central_mass, Racc, a_spin, dir_spin, central_pos);
439 py::arg(
"central_mass"),
443 py::arg(
"central_pos") = Tvec{0, 0, 0})
445 "add_ext_force_shearing_box",
446 [](TConfig &self, Tscal Omega_0, Tscal eta, Tscal q) {
447 self.add_ext_force_shearing_box(Omega_0, eta, q);
454 "add_ext_force_velocity_dissipation",
455 [](TConfig &self, Tscal eta) {
456 self.ext_force_config.add_velocity_dissipation(eta);
461 "add_ext_force_vertical_disc_potential",
462 [](TConfig &self, Tscal central_mass, Tscal R0) {
463 self.ext_force_config.add_vertical_disc_potential(central_mass, R0);
466 py::arg(
"central_mass"),
468 .def(
"set_units", &TConfig::set_units)
472 return self.unit_sys;
476 [](TConfig &self, Tscal cfl_cour) {
477 self.cfl_config.cfl_cour = cfl_cour;
481 [](TConfig &self, Tscal cfl_force) {
482 self.cfl_config.cfl_force = cfl_force;
486 [](TConfig &self, Tscal eta_sink) {
487 self.cfl_config.eta_sink = eta_sink;
489 .def(
"set_cfl_mult_stiffness", &TConfig::set_cfl_mult_stiffness)
491 "set_show_cfl_detail",
492 [](TConfig &self,
bool show_cfl_detail) {
493 self.show_cfl_detail = show_cfl_detail;
495 py::arg(
"show_cfl_detail"))
498 [](TConfig &self, Tscal gpart_mass) {
499 self.gpart_mass = gpart_mass;
503 [](TConfig &self,
const Tvec ¢er, Tscal radius) {
504 self.particle_killing.add_kill_sphere(center, radius);
510 std::string sod_tube_analysis_name = name_model +
"_AnalysisSodTube";
511 py::class_<TAnalysisSodTube>(m, sod_tube_analysis_name.c_str())
512 .def(
"compute_L2_dist", [](TAnalysisSodTube &self) -> std::tuple<Tscal, Tvec, Tscal> {
513 auto ret = self.compute_L2_dist();
514 return {ret.rho, ret.v, ret.P};
517 std::string disc_analysis_name = name_model +
"_AnalysisDisc";
518 py::class_<TAnalysisDisc>(m, disc_analysis_name.c_str())
521 [](TAnalysisDisc &self, Tscal Rmin, Tscal Rmax,
u32 Nbin,
ShamrockCtx &ctx) {
522 auto anal = self.compute_analysis(Rmin, Rmax, Nbin, ctx);
525 auto radius = anal.radius.copy_to_stdvec();
526 auto counter = anal.counter.copy_to_stdvec();
527 auto Sigma = anal.Sigma.copy_to_stdvec();
528 auto lx = anal.lx.copy_to_stdvec();
529 auto ly = anal.ly.copy_to_stdvec();
530 auto lz = anal.lz.copy_to_stdvec();
531 auto tilt = anal.tilt.copy_to_stdvec();
532 auto twist = anal.twist.copy_to_stdvec();
533 auto psi = anal.psi.copy_to_stdvec();
534 auto Hsq = anal.Hsq.copy_to_stdvec();
536 dic_out[
"radius"] = radius;
537 dic_out[
"counter"] = counter;
538 dic_out[
"Sigma"] = Sigma;
542 dic_out[
"tilt"] = tilt;
543 dic_out[
"twist"] = twist;
544 dic_out[
"psi"] = psi;
545 dic_out[
"Hsq"] = Hsq;
550 std::string setup_name = name_model +
"_SPHSetup";
551 py::class_<TSPHSetup>(m, setup_name.c_str())
553 "make_generator_lattice_hcp",
554 [](TSPHSetup &self, Tscal dr, Tvec box_min, Tvec box_max,
bool discontinuous) {
555 return self.make_generator_lattice_hcp(dr, {box_min, box_max}, discontinuous);
560 py::arg(
"discontinuous") =
true)
562 "make_generator_lattice_cubic",
563 [](TSPHSetup &self, Tscal dr, Tvec box_min, Tvec box_max) {
564 return self.make_generator_lattice_cubic(dr, {box_min, box_max});
567 "make_generator_disc_mc",
573 std::function<Tscal(Tscal)> sigma_profile,
574 std::function<Tscal(Tscal)> H_profile,
575 std::function<Tscal(Tscal)> rot_profile,
576 std::function<Tscal(Tscal)> cs_profile,
577 std::function<Tvec(Tvec)> velocity_field,
578 std::function<Tscal(Tvec)> cs_field,
580 Tscal init_h_factor) {
581 auto build_vel_lambda = [&]() -> std::function<Tvec(Tvec)> {
582 if (!velocity_field && !rot_profile) {
584 "make_generator_disc_mc: either velocity_field or rot_profile must be "
585 "provided, you must provide one of them");
588 if (velocity_field && rot_profile) {
590 "make_generator_disc_mc: either velocity_field or rot_profile must be "
591 "provided, you cannot provide both");
594 if (velocity_field) {
595 return std::move(velocity_field);
597 return [vth_r = std::move(rot_profile)](Tvec pos) {
599 Tscal r = sycl::length(pos);
601 auto etheta = sycl::vec<Tscal, 3>{-pos.y(), pos.x(), 0};
602 etheta /= sycl::length(etheta);
604 return vth_r(r) * etheta;
608 auto build_cs_lambda = [&]() -> std::function<Tscal(Tvec)> {
609 bool need_cs = self.solver_config.is_eos_locally_isothermal();
616 "make_generator_disc_mc: with the current EOS, cs_field is "
624 "make_generator_disc_mc: with the current EOS, cs_profile is "
628 return std::function<Tscal(Tvec)>{};
631 if (!cs_field && !cs_profile) {
633 "make_generator_disc_mc: either cs_field or cs_profile must be "
634 "provided, you must provide one of them");
637 if (cs_field && cs_profile) {
639 "make_generator_disc_mc: either cs_field or cs_profile must be "
640 "provided, you cannot provide both");
644 return std::move(cs_field);
647 return [cs_r = std::move(cs_profile)](Tvec pos) {
649 Tscal r = sycl::length(pos);
654 return self.make_generator_disc_mc(
659 std::move(sigma_profile),
660 std::move(H_profile),
663 std::mt19937_64(random_seed),
667 py::arg(
"part_mass"),
668 py::arg(
"disc_mass"),
671 py::arg(
"sigma_profile"),
672 py::arg(
"H_profile"),
673 py::arg(
"rot_profile") = std::function<Tscal(Tscal)>{},
674 py::arg(
"cs_profile") = std::function<Tscal(Tscal)>{},
675 py::arg(
"velocity_field") = std::function<Tvec(Tvec)>{},
676 py::arg(
"cs_field") = std::function<Tscal(Tvec)>{},
677 py::arg(
"random_seed"),
678 py::arg(
"init_h_factor") = 0.8,
680 Create a Monte Carlo disc particle generator.
682 Particles are sampled in cylindrical coordinates: the radius is drawn
683 with rejection sampling from ``sigma_profile``, the azimuth is uniform,
684 and the vertical coordinate follows a Gaussian with scale ``H_profile(r)``.
685 The initial density is extrapolated from the surface density profile, and
686 smoothing lengths are set from that density.
689 part_mass: Mass of each SPH particle.
690 disc_mass: Total disc mass. The particle count is ``disc_mass / part_mass``.
691 r_in: Inner disc radius.
692 r_out: Outer disc radius.
693 sigma_profile: Surface density profile ``sigma(r)``.
694 H_profile: Disc scale height profile ``H(r)``.
695 rot_profile: Azimuthal speed profile ``v_theta(r)``. The velocity is
696 projected along the cylindrical azimuthal direction at each
697 particle position. Mutually exclusive with ``velocity_field``.
698 cs_profile: Sound speed profile ``c_s(r)``. Evaluated at the cylindrical
699 radius of each particle. Required when the solver uses a locally
700 isothermal EOS. Mutually exclusive with ``cs_field``.
701 velocity_field: Velocity profile ``v(x, y, z)``. Mutually exclusive
702 with ``rot_profile``.
703 cs_field: Sound speed profile ``c_s(x, y, z)``. Required when the solver
704 uses a locally isothermal EOS. Mutually exclusive with ``cs_profile``.
705 random_seed: Seed for the Monte Carlo sampler.
706 init_h_factor: Multiplier applied to the smoothing length inferred from
707 the generated density. Defaults to ``0.8``.
710 Exactly one of ``velocity_field`` or ``rot_profile`` must be provided.
712 If the solver uses a locally isothermal EOS, exactly one of ``cs_field``
713 or ``cs_profile`` must be provided. Otherwise both sound-speed profiles
714 are ignored and a warning is emitted if either is supplied.
717 A setup node to pass to :py:meth:`apply_setup`.
720 "make_generator_from_context",
722 return self.make_generator_from_context(context_other);
729 return self.make_combiner_add(parent1, parent2);
732 "make_modifier_warp_disc",
739 return self.make_modifier_warp_disc(parent, Rwarp, Hwarp, inclination, posangle);
745 py::arg(
"inclination"),
746 py::arg(
"posangle") = 0.)
748 "make_modifier_custom_warp",
751 std::function<Tscal(Tscal)> inc_profile,
752 std::function<Tscal(Tscal)> psi_profile,
753 std::function<Tvec(Tscal)> k_profile) {
754 return self.make_modifier_custom_warp(parent, inc_profile, psi_profile, k_profile);
758 py::arg(
"inc_profile"),
759 py::arg(
"psi_profile"),
760 py::arg(
"k_profile"))
762 "make_modifier_offset",
766 Tvec offset_velocity) {
767 return self.make_modifier_add_offset(parent, offset_postion, offset_velocity);
771 py::arg(
"offset_position"),
772 py::arg(
"offset_velocity"))
774 "make_modifier_filter",
777 std::function<
bool(Tvec)> filter) {
778 return self.make_modifier_filter(parent, filter);
784 "make_modifier_split_part",
790 return self.make_modifier_split_part(parent, n_split, seed, h_scaling);
796 py::arg(
"h_scaling") = 0.6)
801 bool part_reordering,
802 std::optional<u32> gen_step,
803 std::optional<u32> insert_step,
804 std::optional<u64> msg_count_limit,
805 std::optional<u64> msg_size_limit,
806 std::optional<u64> max_msg_size,
809 bool speculative_balancing) {
811 return self.apply_setup_new(
820 speculative_balancing);
822 if (
bool(gen_step)) {
825 "SPHSetup",
"gen_step is ignored when using old setup"));
827 if (
bool(msg_count_limit)) {
830 "SPHSetup",
"msg_count_limit is ignored when using old setup"));
832 if (
bool(msg_size_limit)) {
835 "SPHSetup",
"msg_size_limit is ignored when using old setup"));
837 if (
bool(max_msg_size)) {
840 "SPHSetup",
"max_msg_size is ignored when using old setup"));
842 if (
bool(do_setup_log)) {
845 "SPHSetup",
"do_setup_log is ignored when using old setup"));
847 return self.apply_setup(setup, part_reordering, insert_step);
852 py::arg(
"part_reordering") =
true,
853 py::arg(
"gen_step") = std::nullopt,
854 py::arg(
"insert_step") = std::nullopt,
855 py::arg(
"msg_count_limit") = std::nullopt,
856 py::arg(
"rank_comm_size_limit") = std::nullopt,
857 py::arg(
"max_msg_size") = std::nullopt,
858 py::arg(
"do_setup_log") =
false,
859 py::arg(
"use_new_setup") =
true,
860 py::arg(
"speculative_balancing") =
false);
862 py::class_<T>(m, name_model.c_str())
864 return std::make_unique<T>(ctx);
866 .def(
"init", &T::init)
867 .def(
"init_scheduler", &T::init_scheduler)
870 "evolve_once_override_time",
871 &T::evolve_once_time_expl,
874 .def(
"evolve_once", &T::evolve_once)
877 [](T &self,
f64 target_time,
i32 niter_max,
f64 max_walltime) {
878 return self.evolve_until(target_time, niter_max, max_walltime);
880 py::arg(
"target_time"),
882 py::arg(
"niter_max") = -1,
883 py::arg(
"max_walltime") = -1)
884 .def(
"timestep", &T::timestep)
885 .def(
"set_cfl_cour", &T::set_cfl_cour, py::arg(
"cfl_cour"))
886 .def(
"set_cfl_force", &T::set_cfl_force, py::arg(
"cfl_force"))
887 .def(
"set_eta_sink", &T::set_eta_sink, py::arg(
"eta_sink"))
888 .def(
"set_particle_mass", &T::set_particle_mass, py::arg(
"gpart_mass"))
889 .def(
"get_particle_mass", &T::get_particle_mass)
890 .def(
"rho_h", &T::rho_h)
891 .def(
"get_hfact", &T::get_hfact)
898 "get_solver_dot_graph",
903 "get_box_dim_fcc_3d",
905 return self.get_box_dim_fcc_3d(dr, xcnt, ycnt, zcnt);
909 [](T &self,
f64 dr, f64_3 box_min, f64_3 box_max) {
913 "The python function get_ideal_fcc_box is deprecated in the SPH model and "
914 "will be removed at some point, replace it by "
915 "shamrock.math.get_ideal_hcp_box"));
916 return shammath::LatticeHCP<f64_3>::get_ideal_hcp_box(dr, {box_min, box_max});
920 [](T &self,
f64 dr, f64_3 box_min, f64_3 box_max) {
924 "The python function get_ideal_hcp_box is deprecated in the SPH model and "
925 "will be removed at some point, replace it by "
926 "shamrock.math.get_ideal_hcp_box"));
927 return shammath::LatticeHCP<f64_3>::get_ideal_hcp_box(dr, {box_min, box_max});
930 "resize_simulation_box",
931 [](T &self, f64_3 box_min, f64_3 box_max) {
932 return self.resize_simulation_box({box_min, box_max});
936 [](T &self, std::vector<f64_3> pos, std::vector<f64> hpart, std::vector<f64> upart) {
937 return self.push_particle(pos, hpart, upart);
942 std::vector<f64_3> pos,
943 std::vector<f64> hpart,
944 std::vector<f64> upart,
945 std::vector<f64_3> B_on_rho,
946 std::vector<f64> psi_on_ch) {
947 return self.push_particle_mhd(pos, hpart, upart, B_on_rho, psi_on_ch);
951 [](T &self,
f64 dr, f64_3 box_min, f64_3 box_max) {
952 return self.add_cube_fcc_3d(dr, {box_min, box_max});
956 [](T &self,
f64 dr, f64_3 box_min, f64_3 box_max) {
957 return self.add_cube_hcp_3d(dr, {box_min, box_max});
960 "add_cube_hcp_3d_v2",
961 [](T &self,
f64 dr, f64_3 box_min, f64_3 box_max) {
962 return self.add_cube_hcp_3d_v2(dr, {box_min, box_max});
965 "add_disc_3d_keplerian",
976 return self.add_cube_disc_3d(center, Npart, p, rho_0, m, r_in, r_out, q, cmass);
990 return self.add_disc_3d(
991 center, central_mass, Npart, r_in, r_out, disc_mass, p, H_r_in, q);
1006 self.add_big_disc_3d(
1016 std::mt19937{seed});
1017 return disc_mass / Npart;
1019 .def(
"get_total_part_count", &T::get_total_part_count)
1020 .def(
"total_mass_to_part_mass", &T::total_mass_to_part_mass)
1022 "set_value_in_a_box",
1024 const std::string &field_name,
1025 const std::string &field_type,
1026 const pybind11::object &value,
1030 if (field_type ==
"f64") {
1031 f64 val = value.cast<
f64>();
1032 self.set_value_in_a_box(field_name, val, {box_min, box_max}, ivar);
1033 }
else if (field_type ==
"f64_3") {
1034 f64_3 val = value.cast<f64_3>();
1035 self.set_value_in_a_box(field_name, val, {box_min, box_max}, ivar);
1038 "unknown field type");
1041 py::arg(
"field_name"),
1042 py::arg(
"field_type"),
1047 py::arg(
"ivar") = 0)
1049 "set_value_in_sphere",
1051 const std::string &field_name,
1052 const std::string &field_type,
1053 const pybind11::object &value,
1056 if (field_type ==
"f64") {
1057 f64 val = value.cast<
f64>();
1058 self.set_value_in_sphere(field_name, val, center, radius);
1059 }
else if (field_type ==
"f64_3") {
1060 f64_3 val = value.cast<f64_3>();
1061 self.set_value_in_sphere(field_name, val, center, radius);
1064 "unknown field type");
1068 "set_field_value_lambda_f64",
1070 std::string field_name,
1071 const std::function<
f64(Tvec)> pos_to_val,
1073 return self.template set_field_value_lambda<f64>(
1074 std::move(field_name), pos_to_val, offset);
1076 py::arg(
"field_name"),
1077 py::arg(
"pos_to_val"),
1078 py::arg(
"offset") = 0)
1080 "set_field_value_lambda_f64_3",
1082 std::string field_name,
1083 const std::function<f64_3(Tvec)> pos_to_val,
1085 return self.template set_field_value_lambda<f64_3>(
1086 std::move(field_name), pos_to_val, offset);
1088 py::arg(
"field_name"),
1089 py::arg(
"pos_to_val"),
1090 py::arg(
"offset") = 0)
1091 .def(
"overwrite_field_value_f64", &T::template overwrite_field_value<f64>)
1092 .def(
"overwrite_field_value_f64_3", &T::template overwrite_field_value<f64_3>)
1093 .def(
"remap_positions", &T::remap_positions)
1103 const std::string &field_name,
1104 const std::string &field_type,
1105 const pybind11::object &value,
1108 if (field_type ==
"f64") {
1109 f64 val = value.cast<
f64>();
1110 self.add_kernel_value(field_name, val, center, h_ker);
1111 }
else if (field_type ==
"f64_3") {
1112 f64_3 val = value.cast<f64_3>();
1113 self.add_kernel_value(field_name, val, center, h_ker);
1116 "unknown field type");
1121 [](T &self,
const std::string &field_name,
const std::string &field_type) {
1122 if (field_type ==
"f64") {
1123 return py::cast(self.template get_sum<f64>(field_name));
1124 }
else if (field_type ==
"f64_3") {
1125 return py::cast(self.template get_sum<f64_3>(field_name));
1128 "unknown field type");
1132 "get_closest_part_to",
1133 [](T &self, f64_3 pos) -> f64_3 {
1134 return self.get_closest_part_to(pos);
1137 "gen_default_config",
1139 return typename T::Solver::Config{};
1142 "get_current_config",
1144 return self.solver.solver_config;
1146 .def(
"set_solver_config", &T::set_solver_config)
1147 .def(
"add_sink", &T::add_sink)
1157 sink_dic[
"pos"] = sink.pos;
1158 sink_dic[
"velocity"] = sink.velocity;
1159 sink_dic[
"sph_acceleration"] = sink.sph_acceleration;
1160 sink_dic[
"ext_acceleration"] = sink.ext_acceleration;
1161 sink_dic[
"mass"] = sink.mass;
1162 sink_dic[
"angular_momentum"] = sink.angular_momentum;
1163 sink_dic[
"accretion_radius"] = sink.accretion_radius;
1164 list_out.append(sink_dic);
1172 return self.solver.solver_config.unit_sys;
1177 const std::string &name,
1178 const std::string &field_type,
1179 const std::vector<Tvec> &positions,
1180 const std::optional<custom_getter_t> &custom_getter)
1181 -> std::variant<std::vector<f64>, std::vector<f64_3>> {
1182 if (custom_getter.has_value()) {
1183 if (!(name ==
"custom" && field_type ==
"f64")) {
1185 "custom_getter only available for name=custom and field_type=f64");
1189 if (field_type ==
"f64") {
1191 self.ctx, self.solver.solver_config, self.solver.storage);
1192 return render.compute_slice(name, positions, custom_getter).copy_to_stdvec();
1195 if (field_type ==
"f64_3") {
1197 self.ctx, self.solver.solver_config, self.solver.storage);
1198 return render.compute_slice(name, positions, std::nullopt).copy_to_stdvec();
1204 py::arg(
"field_type"),
1205 py::arg(
"positions"),
1206 py::arg(
"custom_getter") = std::nullopt)
1211 const std::vector<Tvec> &positions) -> std::vector<f64> {
1213 self.ctx, self.solver.solver_config, self.solver.storage);
1214 return render.compute_slice(field, positions).copy_to_stdvec();
1217 py::arg(
"positions"))
1222 const std::vector<Tvec> &positions) -> std::vector<f64_3> {
1224 self.ctx, self.solver.solver_config, self.solver.storage);
1225 return render.compute_slice(field, positions).copy_to_stdvec();
1228 py::arg(
"positions"))
1230 "render_column_integ",
1232 const std::string &name,
1233 const std::string &field_type,
1235 const std::optional<custom_getter_t> &custom_getter)
1236 -> std::variant<std::vector<f64>, std::vector<f64_3>> {
1237 if (custom_getter.has_value()) {
1238 if (!(name ==
"custom" && field_type ==
"f64")) {
1240 "custom_getter only available for name=custom and field_type=f64");
1244 if (field_type ==
"f64") {
1246 self.ctx, self.solver.solver_config, self.solver.storage);
1247 return render.compute_column_integ(name, rays, custom_getter).copy_to_stdvec();
1250 if (field_type ==
"f64_3") {
1252 self.ctx, self.solver.solver_config, self.solver.storage);
1253 return render.compute_column_integ(name, rays, std::nullopt).copy_to_stdvec();
1259 py::arg(
"field_type"),
1261 py::arg(
"custom_getter") = std::nullopt)
1263 "render_column_integ",
1268 self.ctx, self.solver.solver_config, self.solver.storage);
1269 return render.compute_column_integ(field, rays).copy_to_stdvec();
1274 "render_column_integ",
1279 self.ctx, self.solver.solver_config, self.solver.storage);
1280 return render.compute_column_integ(field, rays).copy_to_stdvec();
1287 const std::string &name,
1288 const std::string &field_type,
1289 const std::optional<custom_getter_t> &custom_getter)
1293 if (custom_getter.has_value()) {
1294 if (!(name ==
"custom" && field_type ==
"f64")) {
1296 "custom_getter only available for name=custom and field_type=f64");
1300 if (field_type ==
"f64") {
1302 self.ctx, self.solver.solver_config, self.solver.storage);
1303 return render_field_getter.build_field(name, custom_getter);
1306 if (field_type ==
"f64_3") {
1308 self.ctx, self.solver.solver_config, self.solver.storage);
1309 return render_field_getter.build_field(name, custom_getter);
1315 py::arg(
"field_type"),
1316 py::arg(
"custom_getter") = std::nullopt)
1318 "render_azymuthal_integ",
1320 const std::string &name,
1321 const std::string &field_type,
1323 const std::optional<custom_getter_t> &custom_getter)
1324 -> std::variant<std::vector<f64>, std::vector<f64_3>> {
1325 if (custom_getter.has_value()) {
1326 if (!(name ==
"custom" && field_type ==
"f64")) {
1328 "custom_getter only available for name=custom and field_type=f64");
1332 if (field_type ==
"f64") {
1334 self.ctx, self.solver.solver_config, self.solver.storage);
1335 return render.compute_azymuthal_integ(name, ring_rays, custom_getter)
1339 if (field_type ==
"f64_3") {
1341 self.ctx, self.solver.solver_config, self.solver.storage);
1342 return render.compute_azymuthal_integ(name, ring_rays, std::nullopt)
1349 py::arg(
"field_type"),
1350 py::arg(
"ring_rays"),
1351 py::arg(
"custom_getter") = std::nullopt)
1353 "render_azymuthal_integ",
1358 self.ctx, self.solver.solver_config, self.solver.storage);
1359 return render.compute_azymuthal_integ(field, ring_rays).copy_to_stdvec();
1362 py::arg(
"ring_rays"))
1364 "render_azymuthal_integ",
1369 self.ctx, self.solver.solver_config, self.solver.storage);
1370 return render.compute_azymuthal_integ(field, ring_rays).copy_to_stdvec();
1373 py::arg(
"ring_rays"))
1375 "render_cartesian_slice",
1377 const std::string &name,
1378 const std::string &field_type,
1384 const std::optional<custom_getter_t> &custom_getter)
1385 -> std::variant<py::array_t<Tscal>> {
1386 if (custom_getter.has_value()) {
1387 if (!(name ==
"custom" && field_type ==
"f64")) {
1389 "custom_getter only available for name=custom and field_type=f64");
1393 if (field_type ==
"f64") {
1394 py::array_t<Tscal> ret({ny, nx});
1397 self.ctx, self.solver.solver_config, self.solver.storage);
1399 std::vector<f64> slice
1401 .compute_slice(name, center, delta_x, delta_y, nx, ny, custom_getter)
1404 for (
u32 iy = 0; iy < ny; iy++) {
1405 for (
u32 ix = 0; ix < nx; ix++) {
1406 ret.mutable_at(iy, ix) = slice[ix + nx * iy];
1413 if (field_type ==
"f64_3") {
1414 py::array_t<Tscal> ret({ny, nx, 3_u32});
1417 self.ctx, self.solver.solver_config, self.solver.storage);
1419 std::vector<f64_3> slice
1420 = render.compute_slice(name, center, delta_x, delta_y, nx, ny, std::nullopt)
1423 for (
u32 iy = 0; iy < ny; iy++) {
1424 for (
u32 ix = 0; ix < nx; ix++) {
1425 ret.mutable_at(iy, ix, 0) = slice[ix + nx * iy][0];
1426 ret.mutable_at(iy, ix, 1) = slice[ix + nx * iy][1];
1427 ret.mutable_at(iy, ix, 2) = slice[ix + nx * iy][2];
1435 return py::array_t<Tscal>({nx, ny});
1438 py::arg(
"field_type"),
1444 py::arg(
"custom_getter") = std::nullopt)
1446 "render_cartesian_slice",
1453 u32 ny) -> py::array_t<Tscal> {
1454 py::array_t<Tscal> ret({ny, nx});
1457 self.ctx, self.solver.solver_config, self.solver.storage);
1459 std::vector<f64> slice
1460 = render.compute_slice(field, center, delta_x, delta_y, nx, ny)
1463 for (
u32 iy = 0; iy < ny; iy++) {
1464 for (
u32 ix = 0; ix < nx; ix++) {
1465 ret.mutable_at(iy, ix) = slice[ix + nx * iy];
1478 "render_cartesian_slice",
1485 u32 ny) -> py::array_t<Tscal> {
1486 py::array_t<Tscal> ret({ny, nx, 3_u32});
1489 self.ctx, self.solver.solver_config, self.solver.storage);
1491 std::vector<f64_3> slice
1492 = render.compute_slice(field, center, delta_x, delta_y, nx, ny)
1495 for (
u32 iy = 0; iy < ny; iy++) {
1496 for (
u32 ix = 0; ix < nx; ix++) {
1497 ret.mutable_at(iy, ix, 0) = slice[ix + nx * iy][0];
1498 ret.mutable_at(iy, ix, 1) = slice[ix + nx * iy][1];
1499 ret.mutable_at(iy, ix, 2) = slice[ix + nx * iy][2];
1512 "render_cartesian_column_integ",
1514 const std::string &name,
1515 const std::string &field_type,
1521 const std::optional<custom_getter_t> &custom_getter)
1522 -> std::variant<py::array_t<Tscal>> {
1523 if (custom_getter.has_value()) {
1524 if (!(name ==
"custom" && field_type ==
"f64")) {
1526 "custom_getter only available for name=custom and field_type=f64");
1530 if (field_type ==
"f64") {
1531 py::array_t<Tscal> ret({ny, nx});
1534 self.ctx, self.solver.solver_config, self.solver.storage);
1536 std::vector<f64> slice
1538 .compute_column_integ(
1539 name, center, delta_x, delta_y, nx, ny, custom_getter)
1542 for (
u32 iy = 0; iy < ny; iy++) {
1543 for (
u32 ix = 0; ix < nx; ix++) {
1544 ret.mutable_at(iy, ix) = slice[ix + nx * iy];
1551 if (field_type ==
"f64_3") {
1552 py::array_t<Tscal> ret({ny, nx, 3_u32});
1555 self.ctx, self.solver.solver_config, self.solver.storage);
1557 std::vector<f64_3> slice
1559 .compute_column_integ(
1560 name, center, delta_x, delta_y, nx, ny, std::nullopt)
1563 for (
u32 iy = 0; iy < ny; iy++) {
1564 for (
u32 ix = 0; ix < nx; ix++) {
1565 ret.mutable_at(iy, ix, 0) = slice[ix + nx * iy][0];
1566 ret.mutable_at(iy, ix, 1) = slice[ix + nx * iy][1];
1567 ret.mutable_at(iy, ix, 2) = slice[ix + nx * iy][2];
1575 return py::array_t<Tscal>({nx, ny});
1578 py::arg(
"field_type"),
1584 py::arg(
"custom_getter") = std::nullopt)
1586 "render_cartesian_column_integ",
1593 u32 ny) -> py::array_t<Tscal> {
1594 py::array_t<Tscal> ret({ny, nx});
1597 self.ctx, self.solver.solver_config, self.solver.storage);
1599 std::vector<f64> slice
1600 = render.compute_column_integ(field, center, delta_x, delta_y, nx, ny)
1603 for (
u32 iy = 0; iy < ny; iy++) {
1604 for (
u32 ix = 0; ix < nx; ix++) {
1605 ret.mutable_at(iy, ix) = slice[ix + nx * iy];
1618 "render_cartesian_column_integ",
1625 u32 ny) -> py::array_t<Tscal> {
1626 py::array_t<Tscal> ret({ny, nx, 3_u32});
1629 self.ctx, self.solver.solver_config, self.solver.storage);
1631 std::vector<f64_3> slice
1632 = render.compute_column_integ(field, center, delta_x, delta_y, nx, ny)
1635 for (
u32 iy = 0; iy < ny; iy++) {
1636 for (
u32 ix = 0; ix < nx; ix++) {
1637 ret.mutable_at(iy, ix, 0) = slice[ix + nx * iy][0];
1638 ret.mutable_at(iy, ix, 1) = slice[ix + nx * iy][1];
1639 ret.mutable_at(iy, ix, 2) = slice[ix + nx * iy][2];
1652 "gen_config_from_phantom_dump",
1653 [](T &self,
PhantomDump &dump,
bool bypass_error) {
1654 return self.gen_config_from_phantom_dump(dump, bypass_error);
1657 py::arg(
"bypass_error") =
false,
1659 This function generate a shamrock sph solver config from a phantom dump
1664 bypass_error = false (default) bypass any error in the config
1667 "init_from_phantom_dump",
1668 [](T &self,
PhantomDump &dump, Tscal hpart_fact_load) {
1669 self.init_from_phantom_dump(dump, hpart_fact_load);
1672 py::arg(
"hpart_fact_load") = 1.0)
1674 "make_phantom_dump",
1676 return self.make_phantom_dump();
1678 .def(
"do_vtk_dump", &T::do_vtk_dump)
1679 .def(
"set_debug_dump", &T::set_debug_dump)
1680 .def(
"solver_logs_last_rate", &T::solver_logs_last_rate)
1681 .def(
"solver_logs_last_obj_count", &T::solver_logs_last_obj_count)
1683 "solver_logs_last_system_metrics",
1685 auto system_metrics = self.solver.solve_logs.get_last_system_metrics();
1687 ret[
"duration"] = system_metrics.wall_time;
1688 if (system_metrics.rank_energy_consummed.has_value()) {
1689 ret[
"rank_energy_consummed"] = system_metrics.rank_energy_consummed.value();
1691 if (system_metrics.gpu_energy_consummed.has_value()) {
1692 ret[
"gpu_energy_consummed"] = system_metrics.gpu_energy_consummed.value();
1694 if (system_metrics.cpu_energy_consummed.has_value()) {
1695 ret[
"cpu_energy_consummed"] = system_metrics.cpu_energy_consummed.value();
1697 if (system_metrics.dram_energy_consummed.has_value()) {
1698 ret[
"dram_energy_consummed"] = system_metrics.dram_energy_consummed.value();
1702 .def(
"solver_logs_cumulated_step_time", &T::solver_logs_cumulated_step_time)
1703 .def(
"solver_logs_reset_cumulated_step_time", &T::solver_logs_reset_cumulated_step_time)
1704 .def(
"solver_logs_step_count", &T::solver_logs_step_count)
1705 .def(
"solver_logs_reset_step_count", &T::solver_logs_reset_step_count)
1709 return self.get_time();
1714 return self.get_dt_sph();
1718 [](T &self, Tscal t) {
1719 return self.set_time(t);
1723 [](T &self, Tscal dt) {
1724 return self.set_next_dt(dt);
1728 [](T &self,
f64 dt) {
1729 self.set_next_dt(dt);
1732 "set_cfl_multipler",
1733 [](T &self, Tscal lambda) {
1734 return self.set_cfl_multipler(lambda);
1738 "set_cfl_mult_stiffness",
1739 [](T &self, Tscal cstiff) {
1740 return self.solver.solver_config.set_cfl_mult_stiffness(cstiff);
1744 "change_htolerance",
1745 [](T &self, Tscal in) {
1748 ".change_htolerance(val) is deprecated,\n"
1749 " -> calling this is replaced internally by "
1750 ".change_htolerances(coarse=val, fine=min(val, 1.1))\n"
1752 "https://shamrock-code.github.io/Shamrock/sphinx/user_guide/sph/"
1753 "smoothing_length_tolerance.html"););
1754 self.change_htolerances(in, std::min(in, (Tscal) 1.1));
1757 "change_htolerances",
1758 [](T &self, Tscal coarse, Tscal fine) {
1759 self.change_htolerances(coarse, fine);
1765 "make_analysis_sodtube",
1773 return std::make_unique<TAnalysisSodTube>(
1775 self.solver.solver_config,
1776 self.solver.storage,
1785 py::arg(
"direction"),
1786 py::arg(
"time_val"),
1791 "make_analysis_disc",
1793 return std::make_unique<TAnalysisDisc>(
1794 self.ctx, self.solver.solver_config, self.solver.storage);
1796 .def(
"load_from_dump", &T::load_from_dump)
1797 .def(
"dump", &T::dump)
1798 .def(
"get_setup", &T::get_setup)
1800 "get_patch_transform",
1803 return sched.get_patch_transform<Tvec>();
1805 .def(
"apply_momentum_offset", &T::apply_momentum_offset)
1806 .def(
"apply_position_offset", &T::apply_position_offset)
1808 "add_timestep_callback",
1810 std::optional<std::function<
void(
void)>> step_begin_callback,
1811 std::optional<std::function<
void(
void)>> step_end_callback) {
1812 self.solver.timestep_callbacks.push_back(
1813 {std::move(step_begin_callback), std::move(step_end_callback)});
1816 py::arg(
"step_begin") = std::nullopt,
1817 py::arg(
"step_end") = std::nullopt);
1820template<
class Tvec,
template<
class>
class SPHKernel>
1821void add_analysisBarycenter_instance(py::module &m,
const std::string &name_model) {
1824 using Tscal = shambase::VecComponent<Tvec>;
1828 py::class_<modules::AnalysisBarycenter<Tvec, SPHKernel>>(m, name_model.c_str())
1829 .def(py::init([](T &model) {
1830 return std::make_unique<modules::AnalysisBarycenter<Tvec, SPHKernel>>(model);
1833 auto result = self.get_barycenter();
1834 return py::make_tuple(result.barycenter, result.mass_disc);
1838template<
class Tvec,
template<
class>
class SPHKernel>
1839void add_analysisEnergyKinetic_instance(py::module &m,
const std::string &name_model) {
1842 using Tscal = shambase::VecComponent<Tvec>;
1845 py::class_<modules::AnalysisEnergyKinetic<Tvec, SPHKernel>>(m, name_model.c_str())
1846 .def(py::init([](T &model) {
1847 return std::make_unique<modules::AnalysisEnergyKinetic<Tvec, SPHKernel>>(model);
1850 return self.get_kinetic_energy();
1854template<
class Tvec,
template<
class>
class SPHKernel>
1855void add_analysisEnergyPotential_instance(py::module &m,
const std::string &name_model) {
1858 using Tscal = shambase::VecComponent<Tvec>;
1861 py::class_<modules::AnalysisEnergyPotential<Tvec, SPHKernel>>(m, name_model.c_str())
1862 .def(py::init([](T &model) {
1863 return std::make_unique<modules::AnalysisEnergyPotential<Tvec, SPHKernel>>(model);
1866 return self.get_potential_energy();
1870template<
class Tvec,
template<
class>
class SPHKernel>
1871void add_analysisTotalMomentum_instance(py::module &m,
const std::string &name_model) {
1874 using Tscal = shambase::VecComponent<Tvec>;
1877 py::class_<modules::AnalysisTotalMomentum<Tvec, SPHKernel>>(m, name_model.c_str())
1878 .def(py::init([](T &model) {
1879 return std::make_unique<modules::AnalysisTotalMomentum<Tvec, SPHKernel>>(model);
1882 return self.get_total_momentum();
1886template<
class Tvec,
template<
class>
class SPHKernel>
1887void add_analysisAngularMomentum_instance(py::module &m,
const std::string &name_model) {
1890 using Tscal = shambase::VecComponent<Tvec>;
1893 py::class_<modules::AnalysisAngularMomentum<Tvec, SPHKernel>>(m, name_model.c_str())
1894 .def(py::init([](T &model) {
1895 return std::make_unique<modules::AnalysisAngularMomentum<Tvec, SPHKernel>>(model);
1898 return self.get_angular_momentum();
1902template<
class Tvec,
template<
class>
class SPHKernel>
1903void add_analysisDustMass_instance(py::module &m,
const std::string &name_model) {
1906 using Tscal = shambase::VecComponent<Tvec>;
1909 py::class_<modules::AnalysisDustMass<Tvec, SPHKernel>>(m, name_model.c_str())
1910 .def(py::init([](T &model) {
1911 return std::make_unique<modules::AnalysisDustMass<Tvec, SPHKernel>>(model);
1914 return self.get_dust_mass();
1920template<
class Analysis,
typename Tvec,
template<
class>
class SPHKernel>
1922 return Analysis(model);
1925template<
template<
class,
template<
class>
class>
class Analysis>
1926void register_analysis_impl_for_each_kernel(py::module &msph, const char *name_class) {
1927 using namespace shammodels::sph;
1929 using SPHModel_f64_3_M4 = shammodels::sph::Model<f64_3, shammath::M4>;
1930 using SPHModel_f64_3_M6 = shammodels::sph::Model<f64_3, shammath::M6>;
1931 using SPHModel_f64_3_M8 = shammodels::sph::Model<f64_3, shammath::M8>;
1933 using SPHModel_f64_3_C2 = shammodels::sph::Model<f64_3, shammath::C2>;
1934 using SPHModel_f64_3_C4 = shammodels::sph::Model<f64_3, shammath::C4>;
1935 using SPHModel_f64_3_C6 = shammodels::sph::Model<f64_3, shammath::C6>;
1939 [](SPHModel_f64_3_M4 &model) {
1940 return analysis_impl<Analysis<f64_3, shammath::M4>>(model);
1947 [](SPHModel_f64_3_M6 &model) {
1948 return analysis_impl<Analysis<f64_3, shammath::M6>>(model);
1955 [](SPHModel_f64_3_M8 &model) {
1956 return analysis_impl<Analysis<f64_3, shammath::M8>>(model);
1963 [](SPHModel_f64_3_C2 &model) {
1964 return analysis_impl<Analysis<f64_3, shammath::C2>>(model);
1971 [](SPHModel_f64_3_C4 &model) {
1972 return analysis_impl<Analysis<f64_3, shammath::C4>>(model);
1979 [](SPHModel_f64_3_C6 &model) {
1980 return analysis_impl<Analysis<f64_3, shammath::C6>>(model);
1987 auto &m = root_module;
1989 py::module msph = m.def_submodule(
"model_sph",
"Shamrock sph solver");
1991 py::class_<shamrock::PatchDataLazyGetter>(m,
"PatchDataLazyGetter")
1992 .def(
"__getitem__", &shamrock::PatchDataLazyGetter::get_item);
1994 py::class_<EvolveUntilResults>(m,
"EvolveUntilResults")
1995 .def_readwrite(
"reach_target_time", &EvolveUntilResults::reach_target_time)
1996 .def_readwrite(
"reach_niter_max", &EvolveUntilResults::reach_niter_max)
1997 .def_readwrite(
"reach_max_walltime", &EvolveUntilResults::reach_max_walltime)
1998 .def_readwrite(
"iter_count", &EvolveUntilResults::iter_count)
2000 return sham::format(
2001 "EvolveUntilResults(reach_target_time={}, reach_niter_max={}, "
2002 "reach_max_walltime={}, iter_count={})",
2003 self.reach_target_time,
2004 self.reach_niter_max,
2005 self.reach_max_walltime,
2011 add_instance<f64_3, shammath::M4>(msph,
"SPHModel_f64_3_M4_SolverConfig",
"SPHModel_f64_3_M4");
2012 add_instance<f64_3, shammath::M6>(msph,
"SPHModel_f64_3_M6_SolverConfig",
"SPHModel_f64_3_M6");
2013 add_instance<f64_3, shammath::M8>(msph,
"SPHModel_f64_3_M8_SolverConfig",
"SPHModel_f64_3_M8");
2015 add_instance<f64_3, shammath::C2>(msph,
"SPHModel_f64_3_C2_SolverConfig",
"SPHModel_f64_3_C2");
2016 add_instance<f64_3, shammath::C4>(msph,
"SPHModel_f64_3_C4_SolverConfig",
"SPHModel_f64_3_C4");
2017 add_instance<f64_3, shammath::C6>(msph,
"SPHModel_f64_3_C6_SolverConfig",
"SPHModel_f64_3_C6");
2019 using VariantSPHModelBind = std::variant<
2020 std::unique_ptr<Model<f64_3, shammath::M4>>,
2021 std::unique_ptr<Model<f64_3, shammath::M6>>,
2022 std::unique_ptr<Model<f64_3, shammath::M8>>,
2023 std::unique_ptr<Model<f64_3, shammath::C2>>,
2024 std::unique_ptr<Model<f64_3, shammath::C4>>,
2025 std::unique_ptr<Model<f64_3, shammath::C6>>>;
2030 const std::string &vector_type,
2031 const std::string &kernel) -> VariantSPHModelBind {
2032 VariantSPHModelBind ret;
2034 if (vector_type ==
"f64_3" && kernel ==
"M4") {
2035 ret = std::make_unique<Model<f64_3, shammath::M4>>(ctx);
2036 }
else if (vector_type ==
"f64_3" && kernel ==
"M6") {
2037 ret = std::make_unique<Model<f64_3, shammath::M6>>(ctx);
2038 }
else if (vector_type ==
"f64_3" && kernel ==
"M8") {
2039 ret = std::make_unique<Model<f64_3, shammath::M8>>(ctx);
2040 }
else if (vector_type ==
"f64_3" && kernel ==
"C2") {
2041 ret = std::make_unique<Model<f64_3, shammath::C2>>(ctx);
2042 }
else if (vector_type ==
"f64_3" && kernel ==
"C4") {
2043 ret = std::make_unique<Model<f64_3, shammath::C4>>(ctx);
2044 }
else if (vector_type ==
"f64_3" && kernel ==
"C6") {
2045 ret = std::make_unique<Model<f64_3, shammath::C6>>(ctx);
2048 "unknown combination of representation and kernel");
2055 py::arg(
"vector_type"),
2056 py::arg(
"sph_kernel"));
2060 std::shared_ptr<shammodels::sph::modules::ISPHSetupNode>>(msph,
"ISPHSetupNode")
2061 .def(
"get_dot", [](std::shared_ptr<shammodels::sph::modules::ISPHSetupNode> &self) {
2062 return self->get_dot();
2065 py::class_<shammodels::sph::TimestepLog>(msph,
"TimestepLog")
2067 .def_readwrite(
"rank", &shammodels::sph::TimestepLog::rank)
2068 .def_readwrite(
"rate", &shammodels::sph::TimestepLog::rate)
2069 .def_readwrite(
"npart", &shammodels::sph::TimestepLog::npart)
2070 .def_readwrite(
"tcompute", &shammodels::sph::TimestepLog::tcompute)
2071 .def(
"rate_sum", &shammodels::sph::TimestepLog::rate_sum)
2072 .def(
"npart_sum", &shammodels::sph::TimestepLog::npart_sum);
2074 add_analysisBarycenter_instance<f64_3, shammath::M4>(msph,
"AnalysisBarycenter_f64_3_M4");
2075 add_analysisBarycenter_instance<f64_3, shammath::M6>(msph,
"AnalysisBarycenter_f64_3_M6");
2076 add_analysisBarycenter_instance<f64_3, shammath::M8>(msph,
"AnalysisBarycenter_f64_3_M8");
2078 add_analysisBarycenter_instance<f64_3, shammath::C2>(msph,
"AnalysisBarycenter_f64_3_C2");
2079 add_analysisBarycenter_instance<f64_3, shammath::C4>(msph,
"AnalysisBarycenter_f64_3_C4");
2080 add_analysisBarycenter_instance<f64_3, shammath::C6>(msph,
"AnalysisBarycenter_f64_3_C6");
2082 add_analysisEnergyKinetic_instance<f64_3, shammath::M4>(msph,
"AnalysisEnergyKinetic_f64_3_M4");
2083 add_analysisEnergyKinetic_instance<f64_3, shammath::M6>(msph,
"AnalysisEnergyKinetic_f64_3_M6");
2084 add_analysisEnergyKinetic_instance<f64_3, shammath::M8>(msph,
"AnalysisEnergyKinetic_f64_3_M8");
2086 add_analysisEnergyKinetic_instance<f64_3, shammath::C2>(msph,
"AnalysisEnergyKinetic_f64_3_C2");
2087 add_analysisEnergyKinetic_instance<f64_3, shammath::C4>(msph,
"AnalysisEnergyKinetic_f64_3_C4");
2088 add_analysisEnergyKinetic_instance<f64_3, shammath::C6>(msph,
"AnalysisEnergyKinetic_f64_3_C6");
2090 add_analysisEnergyPotential_instance<f64_3, shammath::M4>(
2091 msph,
"AnalysisEnergyPotential_f64_3_M4");
2092 add_analysisEnergyPotential_instance<f64_3, shammath::M6>(
2093 msph,
"AnalysisEnergyPotential_f64_3_M6");
2094 add_analysisEnergyPotential_instance<f64_3, shammath::M8>(
2095 msph,
"AnalysisEnergyPotential_f64_3_M8");
2097 add_analysisEnergyPotential_instance<f64_3, shammath::C2>(
2098 msph,
"AnalysisEnergyPotential_f64_3_C2");
2099 add_analysisEnergyPotential_instance<f64_3, shammath::C4>(
2100 msph,
"AnalysisEnergyPotential_f64_3_C4");
2101 add_analysisEnergyPotential_instance<f64_3, shammath::C6>(
2102 msph,
"AnalysisEnergyPotential_f64_3_C6");
2104 add_analysisTotalMomentum_instance<f64_3, shammath::M4>(msph,
"AnalysisTotalMomentum_f64_3_M4");
2105 add_analysisTotalMomentum_instance<f64_3, shammath::M6>(msph,
"AnalysisTotalMomentum_f64_3_M6");
2106 add_analysisTotalMomentum_instance<f64_3, shammath::M8>(msph,
"AnalysisTotalMomentum_f64_3_M8");
2108 add_analysisTotalMomentum_instance<f64_3, shammath::C2>(msph,
"AnalysisTotalMomentum_f64_3_C2");
2109 add_analysisTotalMomentum_instance<f64_3, shammath::C4>(msph,
"AnalysisTotalMomentum_f64_3_C4");
2110 add_analysisTotalMomentum_instance<f64_3, shammath::C6>(msph,
"AnalysisTotalMomentum_f64_3_C6");
2112 add_analysisAngularMomentum_instance<f64_3, shammath::M4>(
2113 msph,
"AnalysisAngularMomentum_f64_3_M4");
2114 add_analysisAngularMomentum_instance<f64_3, shammath::M6>(
2115 msph,
"AnalysisAngularMomentum_f64_3_M6");
2116 add_analysisAngularMomentum_instance<f64_3, shammath::M8>(
2117 msph,
"AnalysisAngularMomentum_f64_3_M8");
2119 add_analysisAngularMomentum_instance<f64_3, shammath::C2>(
2120 msph,
"AnalysisAngularMomentum_f64_3_C2");
2121 add_analysisAngularMomentum_instance<f64_3, shammath::C4>(
2122 msph,
"AnalysisAngularMomentum_f64_3_C4");
2123 add_analysisAngularMomentum_instance<f64_3, shammath::C6>(
2124 msph,
"AnalysisAngularMomentum_f64_3_C6");
2126 register_analysis_impl_for_each_kernel<modules::AnalysisBarycenter>(msph,
"analysisBarycenter");
2127 register_analysis_impl_for_each_kernel<modules::AnalysisEnergyKinetic>(
2128 msph,
"analysisEnergyKinetic");
2129 register_analysis_impl_for_each_kernel<modules::AnalysisEnergyPotential>(
2130 msph,
"analysisEnergyPotential");
2131 register_analysis_impl_for_each_kernel<modules::AnalysisTotalMomentum>(
2132 msph,
"analysisTotalMomentum");
2133 register_analysis_impl_for_each_kernel<modules::AnalysisAngularMomentum>(
2134 msph,
"analysisAngularMomentum");
2136 add_analysisDustMass_instance<f64_3, shammath::M4>(msph,
"AnalysisDustMass_f64_3_M4");
2137 add_analysisDustMass_instance<f64_3, shammath::M6>(msph,
"AnalysisDustMass_f64_3_M6");
2138 add_analysisDustMass_instance<f64_3, shammath::M8>(msph,
"AnalysisDustMass_f64_3_M8");
2140 add_analysisDustMass_instance<f64_3, shammath::C2>(msph,
"AnalysisDustMass_f64_3_C2");
2141 add_analysisDustMass_instance<f64_3, shammath::C4>(msph,
"AnalysisDustMass_f64_3_C4");
2142 add_analysisDustMass_instance<f64_3, shammath::C6>(msph,
"AnalysisDustMass_f64_3_C6");
2144 register_analysis_impl_for_each_kernel<modules::AnalysisDustMass>(msph,
"analysisDustMass");
AnalysisAngularMomentum class.
AnalysisBarycenter class with one method AnalysisBarycenter.get_barycenter().
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().
Compute the gravitational wave quadrupole. Based on Toscani et. al. 2021.
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
This class is an interface that all SPH setup nodes must implement. It describe an operation associat...
Lazily fetches a single named field of a patch as a numpy array, on demand.
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...
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.
std::shared_ptr< ISPHSetupNode > SetupNodePtr
Alias for a shared pointer to an ISPHSetupNode.
namespace for the sph model
std::vector< SinkParticle< Tvec > > to_sink_particles(const SinkEdges< Tvec > &e)
Build an AoS sink list from the current SoA edges (Python API / dump helpers).
SinkEdges< Tvec > get_sink_edges(shamrock::solvergraph::SolverGraphSerializable &sync)
Fetch mutable references to the sink SoA synchronized edges.
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.
Utilities to convert JSON objects to Python objects and vice versa. TODO: try to convert directly wit...
Helpers to access SPH sink particles stored as SoA synchronized data edges.
Ray representation for intersection testing.
Ring ray representation for intersection testing.
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.