35#include <pybind11/cast.h>
36#include <pybind11/numpy.h>
39template<
class Tvec,
template<
class>
class SPHKernel>
40void add_gsph_instance(py::module &m, std::string name_config, std::string name_model) {
41 using namespace shammodels::gsph;
43 using Tscal = shambase::VecComponent<Tvec>;
46 using TConfig =
typename T::SolverConfig;
48 shamlog_debug_ln(
"[Py]",
"registering class :", name_config,
typeid(T).name());
49 shamlog_debug_ln(
"[Py]",
"registering class :", name_model,
typeid(T).name());
51 py::class_<TConfig> config_cls(m, name_config.c_str());
53 shammodels::common::add_json_defs<TConfig>(config_cls);
55 config_cls.def(
"print_status", &TConfig::print_status)
56 .def(
"set_tree_reduction_level", &TConfig::set_tree_reduction_level)
58 "set_neigh_cache_strategy",
59 &TConfig::set_neigh_cache_strategy,
61 Set the strategy used to build the neighbours cache.
65 strategy : NeighCacheStrategy
66 Either ``NeighCacheStrategy.SingleStage`` or ``NeighCacheStrategy.TwoStage``
67 (the default), as obtained from ``from shamrock import NeighCacheStrategy``.
70 "set_two_stage_search",
71 &TConfig::set_two_stage_search,
73 Set the neighbours cache strategy from a boolean.
76 Use :py:meth:`set_neigh_cache_strategy` instead.
80 "set_riemann_iterative",
81 [](TConfig &self, Tscal tol,
u32 max_iter) {
82 self.set_riemann_iterative(tol, max_iter);
85 py::arg(
"tolerance") = Tscal{1e-6},
86 py::arg(
"max_iter") = 20,
88 Set iterative Riemann solver (van Leer 1997).
90 This is the most accurate but slower Riemann solver.
91 Uses Newton-Raphson iteration to find the pressure in the star region.
96 Convergence tolerance for Newton-Raphson iteration (default: 1e-6)
98 Maximum number of iterations (default: 20)
103 self.set_riemann_hllc();
106 Set HLLC approximate Riemann solver.
108 Fast approximate Riemann solver that captures contact discontinuities.
109 Recommended for general use - good balance of accuracy and speed.
113 [](TConfig &self, Tscal tol,
u32 max_iter) {
114 self.set_riemann_exact(tol, max_iter);
117 py::arg(
"tolerance") = Tscal{1e-8},
118 py::arg(
"max_iter") = 100,
120 Set exact Riemann solver (Toro 2009).
122 Classifies the wave pattern (shock/rarefaction on each side) from the
123 initial states, then solves the matching closed-form relation via
124 bisection. Most accurate but computationally expensive; unlike the
125 iterative (van Leer) solver, it also remains accurate for strong
126 rarefactions / near-vacuum conditions.
131 Bisection convergence tolerance (default: 1e-8)
133 Maximum number of bisection iterations (default: 100)
137 "set_reconstruct_piecewise_constant",
139 self.set_reconstruct_piecewise_constant();
142 Set first-order piecewise constant reconstruction.
144 Sets all gradients to zero. Most diffusive but most stable.
145 Good for very strong shocks or initial testing.
149 "set_force_cha_whitworth",
151 self.set_force_cha_whitworth();
154 Set the Cha & Whitworth (2003) symmetric SPH force formulation (default).
156 Uses the standard SPH momentum equation (nabla_W/rho^2/Omega) with the
157 Riemann-solved interface pressure p* substituted for pressure.
160 "set_force_inutsuka_v2",
162 self.set_force_inutsuka_v2();
165 Set the Inutsuka (2002) effective volume/face force formulation.
167 Uses linear (1st order) interpolation of the volume element between each
168 particle pair to build an effective face (V2_ij, s*), following the
169 original GSPH momentum equation: acc -= m * p* * V2_ij * grad_W_ij.
174 [](TConfig &self, Tscal gamma) {
175 self.set_eos_adiabatic(gamma);
179 Set adiabatic equation of state: P = (\gamma-1) \rho u
184 Adiabatic index (e.g., 5/3 for monatomic gas, 7/5 for diatomic)
187 "set_eos_isothermal",
188 [](TConfig &self, Tscal cs) {
189 self.set_eos_isothermal(cs);
193 Set isothermal equation of state: P = cs^2 \rho
201 .def(
"set_boundary_free", &TConfig::set_boundary_free)
202 .def(
"set_boundary_periodic", &TConfig::set_boundary_periodic)
205 "add_ext_force_point_mass",
206 [](TConfig &self, Tscal central_mass, Tscal Racc, Tvec central_pos) {
207 self.add_ext_force_point_mass(central_mass, Racc, central_pos);
209 py::arg(
"central_mass"),
212 py::arg(
"central_pos") = Tvec{0, 0, 0})
214 .def(
"set_units", &TConfig::set_units)
218 [](TConfig &self, Tscal cfl_cour) {
219 self.cfl_config.cfl_cour = cfl_cour;
223 [](TConfig &self, Tscal cfl_force) {
224 self.cfl_config.cfl_force = cfl_force;
228 [](TConfig &self, Tscal gpart_mass) {
229 self.gpart_mass = gpart_mass;
232 "set_scheduler_config",
233 [](TConfig &self,
u64 split_crit,
u64 merge_crit) {
234 self.scheduler_conf.split_load_value = split_crit;
235 self.scheduler_conf.merge_load_value = merge_crit;
238 py::arg(
"split_load_value"),
239 py::arg(
"merge_load_value"));
241 py::class_<T>(m, name_model.c_str())
243 return std::make_unique<T>(ctx);
245 .def(
"init", &T::init)
246 .def(
"init_scheduler", &T::init_scheduler)
247 .def(
"evolve_once", &T::evolve_once)
250 [](T &self,
f64 target_time,
i32 niter_max) {
251 return self.evolve_until(target_time, niter_max);
253 py::arg(
"target_time"),
255 py::arg(
"niter_max") = -1)
256 .def(
"timestep", &T::timestep)
257 .def(
"set_cfl_cour", &T::set_cfl_cour, py::arg(
"cfl_cour"))
258 .def(
"set_cfl_force", &T::set_cfl_force, py::arg(
"cfl_force"))
259 .def(
"set_particle_mass", &T::set_particle_mass, py::arg(
"gpart_mass"))
260 .def(
"get_particle_mass", &T::get_particle_mass)
261 .def(
"rho_h", &T::rho_h)
262 .def(
"get_hfact", &T::get_hfact)
264 "get_box_dim_fcc_3d",
266 return self.get_box_dim_fcc_3d(dr, xcnt, ycnt, zcnt);
270 [](T &self,
f64 dr, f64_3 box_min, f64_3 box_max) {
271 return self.get_ideal_fcc_box(dr, {box_min, box_max});
275 [](T &self,
f64 dr, f64_3 box_min, f64_3 box_max) {
276 return self.get_ideal_hcp_box(dr, {box_min, box_max});
279 "resize_simulation_box",
280 [](T &self, f64_3 box_min, f64_3 box_max) {
281 return self.resize_simulation_box({box_min, box_max});
285 [](T &self,
f64 dr, f64_3 box_min, f64_3 box_max) {
286 return self.add_cube_fcc_3d(dr, {box_min, box_max});
290 [](T &self,
f64 dr, f64_3 box_min, f64_3 box_max) {
291 return self.add_cube_hcp_3d(dr, {box_min, box_max});
293 .def(
"get_total_part_count", &T::get_total_part_count)
294 .def(
"total_mass_to_part_mass", &T::total_mass_to_part_mass)
298 std::string field_name,
299 std::string field_type,
300 pybind11::object value,
304 if (field_type ==
"f64") {
305 f64 val = value.cast<
f64>();
306 self.set_field_in_box(field_name, val, {box_min, box_max}, ivar);
307 }
else if (field_type ==
"f64_3") {
308 f64_3 val = value.cast<f64_3>();
309 self.set_field_in_box(field_name, val, {box_min, box_max}, ivar);
310 }
else if (field_type ==
"u32") {
311 u32 val = value.cast<
u32>();
312 self.set_field_in_box(field_name, val, {box_min, box_max}, ivar);
315 "unknown field type: " + field_type +
". Valid types: f64, f64_3, u32");
318 py::arg(
"field_name"),
319 py::arg(
"field_type"),
326 Set field value for particles within a box region.
328 Useful for setting up discontinuous initial conditions like Sod shock tube.
333 Name of the field to set (e.g., "vxyz", "uint", "hpart")
335 Type of the field: "f64", "f64_3", or "u32"
336 value : float, tuple, or int
337 Value to set (type must match field_type)
339 Minimum corner of the box (x, y, z)
341 Maximum corner of the box (x, y, z)
343 Variable index for multi-component fields (default: 0)
347 >>> # Sod shock tube: set left state internal energy
348 >>> model.set_field_in_box("uint", "f64", u_left, (-1,-1,-1), (0,1,1))
349 >>> # Set right state
350 >>> model.set_field_in_box("uint", "f64", u_right, (0,-1,-1), (1,1,1))
353 "set_field_in_sphere",
355 std::string field_name,
356 std::string field_type,
357 pybind11::object value,
360 if (field_type ==
"f64") {
361 f64 val = value.cast<
f64>();
362 self.set_field_in_sphere(field_name, val, center, radius);
363 }
else if (field_type ==
"f64_3") {
364 f64_3 val = value.cast<f64_3>();
365 self.set_field_in_sphere(field_name, val, center, radius);
368 "unknown field type");
371 py::arg(
"field_name"),
372 py::arg(
"field_type"),
377 Set field value for particles within a spherical region.
379 Useful for setting up point-source initial conditions like Sedov blast.
384 Name of the field to set (e.g., "uint")
386 Type of the field: "f64" or "f64_3"
387 value : float or tuple
388 Value to set (type must match field_type)
390 Center of the sphere (x, y, z)
396 >>> # Sedov blast: inject energy in central sphere
397 >>> model.set_field_in_sphere("uint", "f64", u_blast, (0,0,0), r_blast)
399 .def("apply_field_from_position_f64_3", &T::template apply_field_from_position<f64_3>)
400 .def(
"apply_field_from_position_f64", &T::template apply_field_from_position<f64>)
403 [](T &self, std::string field_name, std::string field_type) {
404 if (field_type ==
"f64") {
405 return py::cast(self.template get_sum<f64>(field_name));
406 }
else if (field_type ==
"f64_3") {
407 return py::cast(self.template get_sum<f64_3>(field_name));
410 "unknown field type");
414 "gen_default_config",
416 return self.gen_default_config();
419 "get_current_config",
421 return self.solver.solver_config;
423 .def(
"set_solver_config", &T::set_solver_config)
424 .def(
"do_vtk_dump", &T::do_vtk_dump)
425 .def(
"solver_logs_last_rate", &T::solver_logs_last_rate)
426 .def(
"solver_logs_last_obj_count", &T::solver_logs_last_obj_count)
430 return self.solver.get_time();
435 return self.solver.get_dt();
439 [](T &self, Tscal t) {
440 return self.solver.set_time(t);
444 [](T &self, Tscal dt) {
445 return self.solver.set_next_dt(dt);
452 Load simulation state from a Shamrock dump file.
454 Uses the shared ShamrockDump mechanism (same as SPH).
459 Path to the dump file
463 >>> model.load_from_dump("checkpoint.shamrock")
470 Write simulation state to a Shamrock dump file.
472 Uses the shared ShamrockDump mechanism (same as SPH).
477 Path to the dump file
481 >>> model.dump("checkpoint.shamrock")
485using namespace shammodels::gsph;
488 auto &m = root_module;
490 py::module mgsph = m.def_submodule(
"model_gsph",
"Shamrock GSPH (Godunov SPH) solver");
492 using namespace shammodels::gsph;
495 add_gsph_instance<f64_3, shammath::M4>(
496 mgsph,
"GSPHModel_f64_3_M4_SolverConfig",
"GSPHModel_f64_3_M4");
497 add_gsph_instance<f64_3, shammath::M6>(
498 mgsph,
"GSPHModel_f64_3_M6_SolverConfig",
"GSPHModel_f64_3_M6");
499 add_gsph_instance<f64_3, shammath::M8>(
500 mgsph,
"GSPHModel_f64_3_M8_SolverConfig",
"GSPHModel_f64_3_M8");
502 add_gsph_instance<f64_3, shammath::C2>(
503 mgsph,
"GSPHModel_f64_3_C2_SolverConfig",
"GSPHModel_f64_3_C2");
504 add_gsph_instance<f64_3, shammath::C4>(
505 mgsph,
"GSPHModel_f64_3_C4_SolverConfig",
"GSPHModel_f64_3_C4");
506 add_gsph_instance<f64_3, shammath::C6>(
507 mgsph,
"GSPHModel_f64_3_C6_SolverConfig",
"GSPHModel_f64_3_C6");
509 using VariantGSPHModelBind = std::variant<
510 std::unique_ptr<Model<f64_3, shammath::M4>>,
511 std::unique_ptr<Model<f64_3, shammath::M6>>,
512 std::unique_ptr<Model<f64_3, shammath::M8>>,
513 std::unique_ptr<Model<f64_3, shammath::C2>>,
514 std::unique_ptr<Model<f64_3, shammath::C4>>,
515 std::unique_ptr<Model<f64_3, shammath::C6>>>;
519 [](
ShamrockCtx &ctx, std::string vector_type, std::string kernel) -> VariantGSPHModelBind {
520 VariantGSPHModelBind ret;
522 if (vector_type ==
"f64_3" && kernel ==
"M4") {
523 ret = std::make_unique<Model<f64_3, shammath::M4>>(ctx);
524 }
else if (vector_type ==
"f64_3" && kernel ==
"M6") {
525 ret = std::make_unique<Model<f64_3, shammath::M6>>(ctx);
526 }
else if (vector_type ==
"f64_3" && kernel ==
"M8") {
527 ret = std::make_unique<Model<f64_3, shammath::M8>>(ctx);
528 }
else if (vector_type ==
"f64_3" && kernel ==
"C2") {
529 ret = std::make_unique<Model<f64_3, shammath::C2>>(ctx);
530 }
else if (vector_type ==
"f64_3" && kernel ==
"C4") {
531 ret = std::make_unique<Model<f64_3, shammath::C4>>(ctx);
532 }
else if (vector_type ==
"f64_3" && kernel ==
"C6") {
533 ret = std::make_unique<Model<f64_3, shammath::C6>>(ctx);
536 "unknown combination of representation and kernel");
543 py::arg(
"vector_type") =
"f64_3",
544 py::arg(
"sph_kernel") =
"M4",
546 Create a GSPH (Godunov SPH) model.
548 GSPH uses Riemann solvers at particle interfaces instead of artificial viscosity,
549 giving sharper shock resolution.
553 context : ShamrockCtx
556 Vector type, e.g., "f64_3" for 3D double precision (default: "f64_3")
558 SPH kernel type: "M4" (cubic spline, default), "M6", "M8" (quintic spline),
559 "C2", "C4", "C6" (Wendland kernels)
564 A GSPH model instance
568 >>> ctx = shamrock.ShamrockCtx()
569 >>> model = shamrock.get_Model_GSPH(context=ctx) # Uses M4 kernel by default
570 >>> config = model.gen_default_config()
571 >>> config.set_riemann_hllc()
572 >>> config.set_eos_adiabatic(1.4)
573 >>> model.set_solver_config(config)
double f64
Alias for double.
std::uint32_t u32
32 bit unsigned integer
std::uint64_t u64
64 bit unsigned integer
std::int32_t i32
32 bit integer
This header file contains utility functions related to exception handling in the code.
GSPH Model class - high-level interface for GSPH simulations.
ExcptTypes make_except_with_loc(std::string message, SourceLocation loc=SourceLocation{})
Create an exception with a message and a location.
Pybind11 include and definitions.
#define ON_PYTHON_INIT
Register a Python module init function using static initialization.
Utilities to convert JSON objects to Python objects and vice versa. TODO: try to convert directly wit...
Functions related to the MPI communicator.