Shamrock 2025.10.0
Astrophysical Code
Loading...
Searching...
No Matches
SolverConfig.hpp
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
10#pragma once
11
28
30#include "shambackends/math.hpp"
33#include "shambackends/vec.hpp"
42#include "shammodels/sph/config/BCConfig.hpp" // Reuse boundary conditions from SPH
51#include <nlohmann/json.hpp>
54#include <variant>
55#include <vector>
56
57namespace shammodels::gsph {
58
65 template<class Tvec, template<class> class SPHKernel>
66 struct SolverConfig;
67
73 template<class Tscal>
74 struct CFLConfig {
75 Tscal cfl_cour = 0.3;
76 Tscal cfl_force = 0.25;
77 };
78
80 struct DensityBased {};
82 u32 max_neigh_count = 500;
83 };
84
85 using mode = std::variant<DensityBased, DensityBasedNeighLim>;
86
87 mode config = DensityBased{};
88
89 void set_density_based() { config = DensityBased{}; }
90 void set_density_based_neigh_lim(u32 max_neigh_count) {
91 config = DensityBasedNeighLim{max_neigh_count};
92 }
93
94 bool is_density_based_neigh_lim() const {
95 return std::holds_alternative<DensityBasedNeighLim>(config);
96 }
97 };
98
99} // namespace shammodels::gsph
100
101template<class Tvec, template<class> class SPHKernel>
103
104 using Tscal = shambase::VecComponent<Tvec>;
105 static constexpr u32 dim = shambase::VectorProperties<Tvec>::dimension;
106 using Kernel = SPHKernel<Tscal>;
107 using u_morton = u32;
108
110
111 static constexpr Tscal Rkern = Kernel::Rkern;
112
113 Tscal gpart_mass{0};
114
116
117 PatchSchedulerConfig scheduler_conf = {};
118
120 // Units Config
122
123 std::optional<shamunits::UnitSystem<Tscal>> unit_sys = {};
124
125 inline void set_units(shamunits::UnitSystem<Tscal> new_sys) { unit_sys = new_sys; }
126
127 inline Tscal get_constant_G() const {
128 if (!unit_sys) {
129 ON_RANK_0(logger::warn_ln("gsph::Config", "the unit system is not set"));
131 return ctes.G();
132 } else {
133 return shamunits::Constants<Tscal>{*unit_sys}.G();
134 }
135 }
136
138 // Units Config (END)
140
142 // Riemann Solver Config
144
145 using RiemannConfig = RiemannConfig<Tvec>;
146 RiemannConfig riemann_config;
147
148 inline void set_riemann_iterative(Tscal tol = Tscal{1e-6}, u32 max_iter = 20) {
149 riemann_config.set_iterative(tol, max_iter);
150 }
151
152 inline void set_riemann_hllc() { riemann_config.set_hllc(); }
153
154 inline void set_riemann_exact(Tscal tol = Tscal{1e-8}, u32 max_iter = 100) {
155 riemann_config.set_exact(tol, max_iter);
156 }
157
159 // Riemann Solver Config (END)
161
163 // Reconstruction Config
165
166 using ReconstructConfig = ReconstructConfig<Tvec>;
167 ReconstructConfig reconstruct_config;
168
169 inline void set_reconstruct_piecewise_constant() {
170 reconstruct_config.set_piecewise_constant();
171 }
172
173 inline void set_reconstruct_muscl(
175 reconstruct_config.set_muscl(limiter);
176 }
177
178 inline bool requires_gradients() const { return reconstruct_config.requires_gradients(); }
179
181 // Reconstruction Config (END)
183
185 // Force Formulation Config
187
188 using ForceFormulationConfig = ForceFormulationConfig<Tvec>;
189 ForceFormulationConfig force_formulation_config;
190
191 inline void set_force_cha_whitworth() { force_formulation_config.set_cha_whitworth(); }
192
193 inline void set_force_inutsuka_v2() { force_formulation_config.set_inutsuka_v2(); }
194
195 inline bool is_force_inutsuka_v2() const { return force_formulation_config.is_inutsuka_v2(); }
196
198 // Force Formulation Config (END)
200
202 // EOS Config
204
205 using EOSConfig = shammodels::EOSConfig<Tvec>;
206 EOSConfig eos_config;
207
208 inline bool is_eos_adiabatic() const {
209 using T = typename EOSConfig::Adiabatic;
210 return bool(std::get_if<T>(&eos_config.config));
211 }
212
213 inline bool is_eos_isothermal() const {
214 using T = typename EOSConfig::Isothermal;
215 return bool(std::get_if<T>(&eos_config.config));
216 }
217
223 inline Tscal get_eos_gamma() const {
224 using Adiabatic = typename EOSConfig::Adiabatic;
225 using Polytropic = typename EOSConfig::Polytropic;
226 if (const auto *eos = std::get_if<Adiabatic>(&eos_config.config)) {
227 return eos->gamma;
228 } else if (const auto *eos = std::get_if<Polytropic>(&eos_config.config)) {
229 return eos->gamma;
230 }
231 return Tscal{1.4}; // Default for non-gamma EOS types
232 }
233
234 inline void set_eos_adiabatic(Tscal gamma) { eos_config.set_adiabatic(gamma); }
235
236 inline void set_eos_isothermal(Tscal cs) { eos_config.set_isothermal(cs); }
237
239 // EOS Config (END)
241
243 // Boundary Config
245
246 using BCConfig = shammodels::sph::BCConfig<Tvec>; // Reuse from SPH
247 BCConfig boundary_config;
248
249 inline void set_boundary_free() { boundary_config.set_free(); }
250 inline void set_boundary_periodic() { boundary_config.set_periodic(); }
251
262 inline void set_boundary_shearing_periodic(i32_3 shear_base, i32_3 shear_dir, Tscal speed) {
263 boundary_config.set_shearing_periodic(shear_base, shear_dir, speed);
264 }
265
267 // Boundary Config (END)
269
271 // External Force Config
273
275 ExtForceConfig ext_force_config{};
276
277 inline void add_ext_force_point_mass(
278 Tscal central_mass, Tscal Racc, Tvec central_pos = Tvec{}) {
279 ext_force_config.add_point_mass(central_mass, Racc, central_pos);
280 }
281
283 // External Force Config (END)
285
287 // Tree config
289
290 u32 tree_reduction_level = 3;
291
296
297 inline void set_tree_reduction_level(u32 level) { tree_reduction_level = level; }
298
301 neigh_cache_strategy = strategy;
302 }
303
308 inline void set_two_stage_search(bool enable) {
309 ON_RANK_0(shamlog_warn_ln(
310 "GSPH::SolverConfig",
311 "set_two_stage_search() is deprecated,\n"
312 " -> use set_neigh_cache_strategy(NeighCacheStrategy.TwoStage) or\n"
313 " set_neigh_cache_strategy(NeighCacheStrategy.SingleStage) instead"););
315 }
316
318 // Tree config (END)
320
322 // Solver behavior config
324
331 Tscal epsilon_h = 1e-6;
334
335 SmoothingLengthConfig smoothing_length_config;
336
337 inline void set_smoothing_length_density_based() {
338 smoothing_length_config.set_density_based();
339 }
340 inline void set_smoothing_length_density_based_neigh_lim(u32 max_neigh_count) {
341 smoothing_length_config.set_density_based_neigh_lim(max_neigh_count);
342 }
343
344 bool enable_particle_reordering = false;
345 inline void set_enable_particle_reordering(bool enable) { enable_particle_reordering = enable; }
346 u64 particle_reordering_step_freq = 1000;
347 inline void set_particle_reordering_step_freq(u64 freq) {
348 if (freq == 0) {
350 "particle_reordering_step_freq cannot be zero");
351 }
352 particle_reordering_step_freq = freq;
353 }
354
355 bool save_dt_to_fields = false;
356 inline void set_save_dt_to_fields(bool enable) { save_dt_to_fields = enable; }
357 inline bool should_save_dt_to_fields() const { return save_dt_to_fields; }
358
359 bool show_ghost_zone_graph = false;
360 inline void set_show_ghost_zone_graph(bool enable) { show_ghost_zone_graph = enable; }
361
363 // Solver behavior config (END)
365
366 inline bool has_field_uint() const { return is_eos_adiabatic(); }
367
368 inline void print_status() {
369 if (shamcomm::world_rank() != 0) {
370 return;
371 }
372 logger::raw_ln("----- GSPH Solver configuration -----");
373 logger::raw_ln("gpart_mass =", gpart_mass);
374 riemann_config.print_status();
375 reconstruct_config.print_status();
376 force_formulation_config.print_status();
377 eos_config.print_status();
378 logger::raw_ln("--------------------------------------");
379 }
380
381 inline void check_config() const {
382 // Validate configuration (gpart_mass checked later at runtime)
383 // Only check gamma for adiabatic EOS types
384 if (is_eos_adiabatic() && get_eos_gamma() <= 1) {
385 shambase::throw_with_loc<std::runtime_error>("gamma must be > 1 for adiabatic gas");
386 }
387
388 // InutsukaV2 is only wired into update_derivs_iterative()/update_derivs_exact();
389 // update_derivs_hllc() would silently fall back to ChaWhitworth otherwise.
390 if (force_formulation_config.is_inutsuka_v2() && riemann_config.is_hllc()) {
392 "InutsukaV2 force formulation is not yet supported with the HLLC Riemann "
393 "solver. Use set_riemann_iterative() or set_riemann_exact() instead.");
394 }
395 }
396
397 inline void check_config_runtime() const {
398 // Validate configuration for runtime (called before simulation starts)
399 if (gpart_mass <= 0) {
401 "gpart_mass must be positive. Call set_particle_mass() before evolving.");
402 }
403 check_config();
404 }
405
406 void set_layout(shamrock::patch::PatchDataLayerLayout &pdl);
407 void set_ghost_layout(shamrock::patch::PatchDataLayerLayout &ghost_layout);
408};
409
410namespace shammodels::gsph {
411
412 template<class Tscal>
413 inline void to_json(nlohmann::json &j, const CFLConfig<Tscal> &p) {
414 j = nlohmann::json{
415 {"cfl_cour", p.cfl_cour},
416 {"cfl_force", p.cfl_force},
417 };
418 }
419
420 template<class Tscal>
421 inline void from_json(const nlohmann::json &j, CFLConfig<Tscal> &p) {
422 j.at("cfl_cour").get_to(p.cfl_cour);
423 j.at("cfl_force").get_to(p.cfl_force);
424 }
425
426 // JSON serialization for SmoothingLengthConfig
427 inline void to_json(nlohmann::json &j, const SmoothingLengthConfig &p) {
429 = std::get_if<SmoothingLengthConfig::DensityBased>(&p.config)) {
430 j = {
431 {"type", "density_based"},
432 };
433
434 } else if (
436 = std::get_if<SmoothingLengthConfig::DensityBasedNeighLim>(&p.config)) {
437
438 j = {
439 {"type", "density_based_neigh_lim"},
440 {"max_neigh_count", conf->max_neigh_count},
441 };
442 } else {
444 }
445 }
446
447 inline void from_json(const nlohmann::json &j, SmoothingLengthConfig &p) {
448 if (j.at("type").get<std::string>() == "density_based") {
450 } else if (j.at("type").get<std::string>() == "density_based_neigh_lim") {
451 p.config
452 = SmoothingLengthConfig::DensityBasedNeighLim{j.at("max_neigh_count").get<u32>()};
453 } else {
455 }
456 }
457
458 template<class Tvec, template<class> class SPHKernel>
459 inline void to_json(nlohmann::json &j, const SolverConfig<Tvec, SPHKernel> &p) {
461 using Tkernel = typename T::Kernel;
462
463 std::string kernel_id = shambase::get_type_name<Tkernel>();
464 std::string type_id = shambase::get_type_name<Tvec>();
465
466 j = nlohmann::json{
467 {"solver_type", "gsph"},
468 {"kernel_id", kernel_id},
469 {"type_id", type_id},
470 {"scheduler_config", p.scheduler_conf},
471 {"gpart_mass", p.gpart_mass},
472 {"cfl_config", p.cfl_config},
473 {"unit_sys", p.unit_sys},
474 {"riemann_config", p.riemann_config},
475 {"reconstruct_config", p.reconstruct_config},
476 {"force_formulation_config", p.force_formulation_config},
477 {"eos_config", p.eos_config},
478 {"boundary_config", p.boundary_config},
479 {"tree_reduction_level", p.tree_reduction_level},
480 {shammodels::neigh_cache_strategy_json_key, p.neigh_cache_strategy},
481 {"htol_up_coarse_cycle", p.htol_up_coarse_cycle},
482 {"htol_up_fine_cycle", p.htol_up_fine_cycle},
483 {"epsilon_h", p.epsilon_h},
484 {"h_iter_per_subcycles", p.h_iter_per_subcycles},
485 {"h_max_subcycles_count", p.h_max_subcycles_count},
486 {"combined_dtdiv_divcurlv_compute", p.combined_dtdiv_divcurlv_compute},
487 {"enable_particle_reordering", p.enable_particle_reordering},
488 {"particle_reordering_step_freq", p.particle_reordering_step_freq},
489 {"set_save_dt_to_fields", p.save_dt_to_fields},
490 {"show_ghost_zone_graph", p.show_ghost_zone_graph},
491 };
492 }
493
494 template<class Tvec, template<class> class SPHKernel>
495 inline void from_json(const nlohmann::json &j, SolverConfig<Tvec, SPHKernel> &p) {
497 using Tkernel = typename T::Kernel;
498
499 std::string kernel_id = j.at("kernel_id").get<std::string>();
500 if (kernel_id != shambase::get_type_name<Tkernel>()) {
502 "Invalid kernel type: expected " + shambase::get_type_name<Tkernel>() + " but got "
503 + kernel_id);
504 }
505
506 std::string type_id = j.at("type_id").get<std::string>();
507 if (type_id != shambase::get_type_name<Tvec>()) {
509 "Invalid vector type: expected " + shambase::get_type_name<Tvec>() + " but got "
510 + type_id);
511 }
512
513 bool has_used_defaults = false;
514 bool has_updated_config = false;
515
516 auto _get_to_if_contains = [&](const std::string &key, auto &value) {
517 shamrock::get_to_if_contains(j, key, value, has_used_defaults);
518 };
519
520 _get_to_if_contains("scheduler_config", p.scheduler_conf);
521 _get_to_if_contains("gpart_mass", p.gpart_mass);
522 _get_to_if_contains("cfl_config", p.cfl_config);
523 _get_to_if_contains("unit_sys", p.unit_sys);
524 _get_to_if_contains("riemann_config", p.riemann_config);
525 _get_to_if_contains("reconstruct_config", p.reconstruct_config);
526 _get_to_if_contains("force_formulation_config", p.force_formulation_config);
527 _get_to_if_contains("eos_config", p.eos_config);
528 _get_to_if_contains("boundary_config", p.boundary_config);
529 _get_to_if_contains("tree_reduction_level", p.tree_reduction_level);
530 // Reads the new enum key, falling back on the legacy `use_two_stage_search` boolean
532 j, p.neigh_cache_strategy, "GSPH::SolverConfig", has_used_defaults, has_updated_config);
533 _get_to_if_contains("htol_up_coarse_cycle", p.htol_up_coarse_cycle);
534 _get_to_if_contains("htol_up_fine_cycle", p.htol_up_fine_cycle);
535 _get_to_if_contains("epsilon_h", p.epsilon_h);
536 _get_to_if_contains("h_iter_per_subcycles", p.h_iter_per_subcycles);
537 _get_to_if_contains("h_max_subcycles_count", p.h_max_subcycles_count);
538
539 if (has_used_defaults || has_updated_config) {
540 if (shamcomm::world_rank() == 0) {
541 logger::info_ln(
542 "GSPH::SolverConfig",
543 shamrock::log_json_changes(p, j, has_used_defaults, has_updated_config));
544 }
545 }
546 }
547
548} // namespace shammodels::gsph
Configuration for the GSPH momentum equation formulation.
Header file describing a Node Instance.
MPI scheduler.
Configuration for reconstruction methods in GSPH.
Configuration for Riemann solvers in GSPH.
std::uint32_t u32
32 bit unsigned integer
std::uint64_t u64
64 bit unsigned integer
A Compressed Leaf Bounding Volume Hierarchy (CLBVH) for neighborhood queries.
Defines a unit system.
Neighbour cache build strategy enum + json serialization/deserialization.
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.
void throw_unimplemented(SourceLocation loc=SourceLocation{})
Throw a std::runtime_error saying that the function is unimplemented.
i32 world_rank()
Gives the rank of the current process in the MPI communicator.
Definition worldInfo.cpp:41
constexpr const char * neigh_cache_strategy_json_key
Json key holding the neighbour cache strategy.
void get_to_neigh_cache_strategy(const nlohmann::json &j, NeighCacheStrategy &value, const std::string &log_ctx, bool &has_used_defaults, bool &has_updated_config)
Deserialize the neighbour cache strategy, falling back on the legacy boolean key.
NeighCacheStrategy neigh_cache_strategy_from_two_stage_search(bool use_two_stage_search)
Map the legacy use_two_stage_search boolean onto the strategy enum.
NeighCacheStrategy
Strategy used to build the neighbour cache out of the tree traversal.
@ TwoStage
Two stage neighbours search (see shamrock paper).
void get_to_if_contains(const nlohmann::json &j, const std::string &key, T &value, bool &has_used_defaults)
std::string log_json_changes(const nlohmann::json &j_current, const nlohmann::json &j, bool has_used_defaults, bool has_updated_config)
Shown the changes between two JSON objects to log config changes.
Contains traits and utilities for backend related types.
void warn_ln(std::string module_name, Types... var2)
Prints a log message with multiple arguments followed by a newline.
Definition logs.hpp:132
sph kernels
shamphys::EOS_Config_Polytropic< Tscal > Polytropic
Polytropic equation of state configuration.
Definition EOSConfig.hpp:55
shamphys::EOS_Config_Isothermal< Tscal > Isothermal
Isothermal equation of state configuration.
Definition EOSConfig.hpp:58
shamphys::EOS_Config_Adiabatic< Tscal > Adiabatic
Adiabatic equation of state configuration.
Definition EOSConfig.hpp:52
The configuration for the CFL condition in GSPH.
Tscal cfl_force
CFL condition for the force.
Tscal cfl_cour
CFL condition for the courant factor.
Limiter
Slope limiter types for MUSCL reconstruction.
The configuration for a GSPH solver.
u32 h_iter_per_subcycles
Maximum number of iterations per subcycle.
void set_neigh_cache_strategy(NeighCacheStrategy strategy)
Setter for the neighbours cache strategy.
void set_boundary_shearing_periodic(i32_3 shear_base, i32_3 shear_dir, Tscal speed)
Set shearing periodic boundary conditions.
NeighCacheStrategy neigh_cache_strategy
Tscal get_eos_gamma() const
Get the adiabatic index (gamma) from the EOS config.
Tscal gpart_mass
The mass of each gas particle (must be set before use).
u32 h_max_subcycles_count
Maximum number of subcycles before solver crash.
Tscal htol_up_fine_cycle
Maximum factor of the smoothing length evolution per subcycles.
void set_two_stage_search(bool enable)
Setter for the two stage search.
CFLConfig< Tscal > cfl_config
CFL configuration.
Tscal epsilon_h
Convergence criteria for the smoothing length.
Boundary conditions configuration.
Definition BCConfig.hpp:40
void set_free()
Set the boundary condition to free boundaries.
Definition BCConfig.hpp:98
Physical constants.
constexpr T G()
get the value of G in the current unit system units
Functions related to the MPI communicator.
#define ON_RANK_0(x)
Macro to execute code only on rank 0.
Definition worldInfo.hpp:73