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)
57 .def(
"set_two_stage_search", &TConfig::set_two_stage_search)
60 "set_riemann_iterative",
61 [](TConfig &self, Tscal tol,
u32 max_iter) {
62 self.set_riemann_iterative(tol, max_iter);
65 py::arg(
"tolerance") = Tscal{1e-6},
66 py::arg(
"max_iter") = 20,
68 Set iterative Riemann solver (van Leer 1997).
70 This is the most accurate but slower Riemann solver.
71 Uses Newton-Raphson iteration to find the pressure in the star region.
76 Convergence tolerance for Newton-Raphson iteration (default: 1e-6)
78 Maximum number of iterations (default: 20)
83 self.set_riemann_hllc();
86 Set HLLC approximate Riemann solver.
88 Fast approximate Riemann solver that captures contact discontinuities.
89 Recommended for general use - good balance of accuracy and speed.
93 [](TConfig &self, Tscal tol,
u32 max_iter) {
94 self.set_riemann_exact(tol, max_iter);
97 py::arg(
"tolerance") = Tscal{1e-8},
98 py::arg(
"max_iter") = 100,
100 Set exact Riemann solver (Toro 2009).
102 Classifies the wave pattern (shock/rarefaction on each side) from the
103 initial states, then solves the matching closed-form relation via
104 bisection. Most accurate but computationally expensive; unlike the
105 iterative (van Leer) solver, it also remains accurate for strong
106 rarefactions / near-vacuum conditions.
111 Bisection convergence tolerance (default: 1e-8)
113 Maximum number of bisection iterations (default: 100)
117 "set_reconstruct_piecewise_constant",
119 self.set_reconstruct_piecewise_constant();
122 Set first-order piecewise constant reconstruction.
124 Sets all gradients to zero. Most diffusive but most stable.
125 Good for very strong shocks or initial testing.
129 "set_force_cha_whitworth",
131 self.set_force_cha_whitworth();
134 Set the Cha & Whitworth (2003) symmetric SPH force formulation (default).
136 Uses the standard SPH momentum equation (nabla_W/rho^2/Omega) with the
137 Riemann-solved interface pressure p* substituted for pressure.
140 "set_force_inutsuka_v2",
142 self.set_force_inutsuka_v2();
145 Set the Inutsuka (2002) effective volume/face force formulation.
147 Uses linear (1st order) interpolation of the volume element between each
148 particle pair to build an effective face (V2_ij, s*), following the
149 original GSPH momentum equation: acc -= m * p* * V2_ij * grad_W_ij.
154 [](TConfig &self, Tscal gamma) {
155 self.set_eos_adiabatic(gamma);
159 Set adiabatic equation of state: P = (\gamma-1) \rho u
164 Adiabatic index (e.g., 5/3 for monatomic gas, 7/5 for diatomic)
167 "set_eos_isothermal",
168 [](TConfig &self, Tscal cs) {
169 self.set_eos_isothermal(cs);
173 Set isothermal equation of state: P = cs^2 \rho
181 .def(
"set_boundary_free", &TConfig::set_boundary_free)
182 .def(
"set_boundary_periodic", &TConfig::set_boundary_periodic)
185 "add_ext_force_point_mass",
186 [](TConfig &self, Tscal central_mass, Tscal Racc) {
187 self.add_ext_force_point_mass(central_mass, Racc);
190 py::arg(
"central_mass"),
193 .def(
"set_units", &TConfig::set_units)
197 [](TConfig &self, Tscal cfl_cour) {
198 self.cfl_config.cfl_cour = cfl_cour;
202 [](TConfig &self, Tscal cfl_force) {
203 self.cfl_config.cfl_force = cfl_force;
207 [](TConfig &self, Tscal gpart_mass) {
208 self.gpart_mass = gpart_mass;
211 "set_scheduler_config",
212 [](TConfig &self,
u64 split_crit,
u64 merge_crit) {
213 self.scheduler_conf.split_load_value = split_crit;
214 self.scheduler_conf.merge_load_value = merge_crit;
217 py::arg(
"split_load_value"),
218 py::arg(
"merge_load_value"));
220 py::class_<T>(m, name_model.c_str())
222 return std::make_unique<T>(ctx);
224 .def(
"init", &T::init)
225 .def(
"init_scheduler", &T::init_scheduler)
226 .def(
"evolve_once", &T::evolve_once)
229 [](T &self,
f64 target_time,
i32 niter_max) {
230 return self.evolve_until(target_time, niter_max);
232 py::arg(
"target_time"),
234 py::arg(
"niter_max") = -1)
235 .def(
"timestep", &T::timestep)
236 .def(
"set_cfl_cour", &T::set_cfl_cour, py::arg(
"cfl_cour"))
237 .def(
"set_cfl_force", &T::set_cfl_force, py::arg(
"cfl_force"))
238 .def(
"set_particle_mass", &T::set_particle_mass, py::arg(
"gpart_mass"))
239 .def(
"get_particle_mass", &T::get_particle_mass)
240 .def(
"rho_h", &T::rho_h)
241 .def(
"get_hfact", &T::get_hfact)
243 "get_box_dim_fcc_3d",
245 return self.get_box_dim_fcc_3d(dr, xcnt, ycnt, zcnt);
249 [](T &self,
f64 dr, f64_3 box_min, f64_3 box_max) {
250 return self.get_ideal_fcc_box(dr, {box_min, box_max});
254 [](T &self,
f64 dr, f64_3 box_min, f64_3 box_max) {
255 return self.get_ideal_hcp_box(dr, {box_min, box_max});
258 "resize_simulation_box",
259 [](T &self, f64_3 box_min, f64_3 box_max) {
260 return self.resize_simulation_box({box_min, box_max});
264 [](T &self,
f64 dr, f64_3 box_min, f64_3 box_max) {
265 return self.add_cube_fcc_3d(dr, {box_min, box_max});
269 [](T &self,
f64 dr, f64_3 box_min, f64_3 box_max) {
270 return self.add_cube_hcp_3d(dr, {box_min, box_max});
272 .def(
"get_total_part_count", &T::get_total_part_count)
273 .def(
"total_mass_to_part_mass", &T::total_mass_to_part_mass)
277 std::string field_name,
278 std::string field_type,
279 pybind11::object value,
283 if (field_type ==
"f64") {
284 f64 val = value.cast<
f64>();
285 self.set_field_in_box(field_name, val, {box_min, box_max}, ivar);
286 }
else if (field_type ==
"f64_3") {
287 f64_3 val = value.cast<f64_3>();
288 self.set_field_in_box(field_name, val, {box_min, box_max}, ivar);
289 }
else if (field_type ==
"u32") {
290 u32 val = value.cast<
u32>();
291 self.set_field_in_box(field_name, val, {box_min, box_max}, ivar);
294 "unknown field type: " + field_type +
". Valid types: f64, f64_3, u32");
297 py::arg(
"field_name"),
298 py::arg(
"field_type"),
305 Set field value for particles within a box region.
307 Useful for setting up discontinuous initial conditions like Sod shock tube.
312 Name of the field to set (e.g., "vxyz", "uint", "hpart")
314 Type of the field: "f64", "f64_3", or "u32"
315 value : float, tuple, or int
316 Value to set (type must match field_type)
318 Minimum corner of the box (x, y, z)
320 Maximum corner of the box (x, y, z)
322 Variable index for multi-component fields (default: 0)
326 >>> # Sod shock tube: set left state internal energy
327 >>> model.set_field_in_box("uint", "f64", u_left, (-1,-1,-1), (0,1,1))
328 >>> # Set right state
329 >>> model.set_field_in_box("uint", "f64", u_right, (0,-1,-1), (1,1,1))
332 "set_field_in_sphere",
334 std::string field_name,
335 std::string field_type,
336 pybind11::object value,
339 if (field_type ==
"f64") {
340 f64 val = value.cast<
f64>();
341 self.set_field_in_sphere(field_name, val, center, radius);
342 }
else if (field_type ==
"f64_3") {
343 f64_3 val = value.cast<f64_3>();
344 self.set_field_in_sphere(field_name, val, center, radius);
347 "unknown field type");
350 py::arg(
"field_name"),
351 py::arg(
"field_type"),
356 Set field value for particles within a spherical region.
358 Useful for setting up point-source initial conditions like Sedov blast.
363 Name of the field to set (e.g., "uint")
365 Type of the field: "f64" or "f64_3"
366 value : float or tuple
367 Value to set (type must match field_type)
369 Center of the sphere (x, y, z)
375 >>> # Sedov blast: inject energy in central sphere
376 >>> model.set_field_in_sphere("uint", "f64", u_blast, (0,0,0), r_blast)
378 .def("apply_field_from_position_f64_3", &T::template apply_field_from_position<f64_3>)
379 .def(
"apply_field_from_position_f64", &T::template apply_field_from_position<f64>)
382 [](T &self, std::string field_name, std::string field_type) {
383 if (field_type ==
"f64") {
384 return py::cast(self.template get_sum<f64>(field_name));
385 }
else if (field_type ==
"f64_3") {
386 return py::cast(self.template get_sum<f64_3>(field_name));
389 "unknown field type");
393 "gen_default_config",
395 return self.gen_default_config();
398 "get_current_config",
400 return self.solver.solver_config;
402 .def(
"set_solver_config", &T::set_solver_config)
403 .def(
"do_vtk_dump", &T::do_vtk_dump)
404 .def(
"solver_logs_last_rate", &T::solver_logs_last_rate)
405 .def(
"solver_logs_last_obj_count", &T::solver_logs_last_obj_count)
409 return self.solver.get_time();
414 return self.solver.get_dt();
418 [](T &self, Tscal t) {
419 return self.solver.set_time(t);
423 [](T &self, Tscal dt) {
424 return self.solver.set_next_dt(dt);
431 Load simulation state from a Shamrock dump file.
433 Uses the shared ShamrockDump mechanism (same as SPH).
438 Path to the dump file
442 >>> model.load_from_dump("checkpoint.shamrock")
449 Write simulation state to a Shamrock dump file.
451 Uses the shared ShamrockDump mechanism (same as SPH).
456 Path to the dump file
460 >>> model.dump("checkpoint.shamrock")
464using namespace shammodels::gsph;
467 auto &m = root_module;
469 py::module mgsph = m.def_submodule(
"model_gsph",
"Shamrock GSPH (Godunov SPH) solver");
471 using namespace shammodels::gsph;
474 add_gsph_instance<f64_3, shammath::M4>(
475 mgsph,
"GSPHModel_f64_3_M4_SolverConfig",
"GSPHModel_f64_3_M4");
476 add_gsph_instance<f64_3, shammath::M6>(
477 mgsph,
"GSPHModel_f64_3_M6_SolverConfig",
"GSPHModel_f64_3_M6");
478 add_gsph_instance<f64_3, shammath::M8>(
479 mgsph,
"GSPHModel_f64_3_M8_SolverConfig",
"GSPHModel_f64_3_M8");
481 add_gsph_instance<f64_3, shammath::C2>(
482 mgsph,
"GSPHModel_f64_3_C2_SolverConfig",
"GSPHModel_f64_3_C2");
483 add_gsph_instance<f64_3, shammath::C4>(
484 mgsph,
"GSPHModel_f64_3_C4_SolverConfig",
"GSPHModel_f64_3_C4");
485 add_gsph_instance<f64_3, shammath::C6>(
486 mgsph,
"GSPHModel_f64_3_C6_SolverConfig",
"GSPHModel_f64_3_C6");
488 using VariantGSPHModelBind = std::variant<
489 std::unique_ptr<Model<f64_3, shammath::M4>>,
490 std::unique_ptr<Model<f64_3, shammath::M6>>,
491 std::unique_ptr<Model<f64_3, shammath::M8>>,
492 std::unique_ptr<Model<f64_3, shammath::C2>>,
493 std::unique_ptr<Model<f64_3, shammath::C4>>,
494 std::unique_ptr<Model<f64_3, shammath::C6>>>;
498 [](
ShamrockCtx &ctx, std::string vector_type, std::string kernel) -> VariantGSPHModelBind {
499 VariantGSPHModelBind ret;
501 if (vector_type ==
"f64_3" && kernel ==
"M4") {
502 ret = std::make_unique<Model<f64_3, shammath::M4>>(ctx);
503 }
else if (vector_type ==
"f64_3" && kernel ==
"M6") {
504 ret = std::make_unique<Model<f64_3, shammath::M6>>(ctx);
505 }
else if (vector_type ==
"f64_3" && kernel ==
"M8") {
506 ret = std::make_unique<Model<f64_3, shammath::M8>>(ctx);
507 }
else if (vector_type ==
"f64_3" && kernel ==
"C2") {
508 ret = std::make_unique<Model<f64_3, shammath::C2>>(ctx);
509 }
else if (vector_type ==
"f64_3" && kernel ==
"C4") {
510 ret = std::make_unique<Model<f64_3, shammath::C4>>(ctx);
511 }
else if (vector_type ==
"f64_3" && kernel ==
"C6") {
512 ret = std::make_unique<Model<f64_3, shammath::C6>>(ctx);
515 "unknown combination of representation and kernel");
522 py::arg(
"vector_type") =
"f64_3",
523 py::arg(
"sph_kernel") =
"M4",
525 Create a GSPH (Godunov SPH) model.
527 GSPH uses Riemann solvers at particle interfaces instead of artificial viscosity,
528 giving sharper shock resolution.
532 context : ShamrockCtx
535 Vector type, e.g., "f64_3" for 3D double precision (default: "f64_3")
537 SPH kernel type: "M4" (cubic spline, default), "M6", "M8" (quintic spline),
538 "C2", "C4", "C6" (Wendland kernels)
543 A GSPH model instance
547 >>> ctx = shamrock.ShamrockCtx()
548 >>> model = shamrock.get_Model_GSPH(context=ctx) # Uses M4 kernel by default
549 >>> config = model.gen_default_config()
550 >>> config.set_riemann_hllc()
551 >>> config.set_eos_adiabatic(1.4)
552 >>> 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.