Shamrock 2025.10.0
Astrophysical Code
Loading...
Searching...
No Matches
Solver.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
18
20#include "SolverConfig.hpp"
21#include "shambackends/vec.hpp"
34#include <functional>
35#include <limits>
36#include <memory>
37#include <optional>
38#include <stdexcept>
39#include <variant>
40#include <vector>
41namespace shammodels::sph {
42
43 struct TimestepLog {
44 i32 rank;
45 f64 rate;
46 u64 npart;
47 f64 tcompute;
48
49 inline f64 rate_sum() { return shamalgs::collective::allreduce_sum(rate); }
50
51 inline u64 npart_sum() { return shamalgs::collective::allreduce_sum(npart); }
52
53 inline f64 tcompute_max() { return shamalgs::collective::allreduce_max(tcompute); }
54 };
55
57 bool reach_target_time;
58 bool reach_niter_max;
59 bool reach_max_walltime;
60
61 i32 iter_count;
62 };
63
70 template<class Tvec, template<class> class SPHKernel>
71 class Solver {
72 public:
73 using Tscal = shambase::VecComponent<Tvec>;
74 static constexpr u32 dim = shambase::VectorProperties<Tvec>::dimension;
75 using Kernel = SPHKernel<Tscal>;
76
77 using Config = SolverConfig<Tvec, SPHKernel>;
78
79 using u_morton = typename Config::u_morton;
80
81 static constexpr Tscal Rkern = Kernel::Rkern;
82
83 ShamrockCtx &context;
84 inline PatchScheduler &scheduler() { return shambase::get_check_ref(context.sched); }
85
87
88 Config solver_config;
89 SolverLog solve_logs;
90
92 inline Tscal &time_edge_value() {
93 return scheduler()
94 .synchronized_data
95 .template get_edge_ref<shamrock::solvergraph::IDataEdgeSerializable<Tscal>>("time")
96 .data;
97 }
98
100 inline Tscal &dt_edge_value() {
101 return scheduler()
102 .synchronized_data
103 .template get_edge_ref<shamrock::solvergraph::IDataEdgeSerializable<Tscal>>("dt")
104 .data;
105 }
106
109 return scheduler()
110 .synchronized_data
111 .template get_edge_ref<shamrock::solvergraph::IDataEdgeSerializable<Tscal>>(
112 "cfl_multiplier")
113 .data;
114 }
115
116 inline Tscal get_time() { return time_edge_value(); }
117 inline void set_time(Tscal t) { time_edge_value() = t; }
118 inline Tscal get_dt_sph() { return dt_edge_value(); }
119 inline void set_next_dt(Tscal dt) { dt_edge_value() = dt; }
120 inline Tscal get_cfl_multipler() { return cfl_multiplier_edge_value(); }
121 inline void set_cfl_multipler(Tscal lambda) { cfl_multiplier_edge_value() = lambda; }
122
125 auto &sync = scheduler().synchronized_data;
126 auto names = sync.get_edge_names();
127 auto has_edge = [&](const std::string &name) {
128 return std::find(names.begin(), names.end(), name) != names.end();
129 };
130
131 if (!has_edge("time")) {
132 auto edge = sync.register_edge(
134 edge->data = 0;
135 }
136 if (!has_edge("dt")) {
137 auto edge = sync.register_edge(
139 edge->data = 0;
140 }
141 if (!has_edge("cfl_multiplier")) {
142 auto edge = sync.register_edge(
143 "cfl_multiplier",
145 "cfl_multiplier", "C_{\\rm CFL}"));
146 edge->data = 1e-2;
147 }
148 }
149
151 std::optional<std::function<void(void)>> step_begin_callback;
152 std::optional<std::function<void(void)>> step_end_callback;
153 };
154 std::vector<SolverStepCallback> timestep_callbacks{};
155
156 inline void init_required_fields() { solver_config.set_layout(context.get_pdl_write()); }
157
158 // serial patch tree control
159 void gen_serial_patch_tree();
160 inline void reset_serial_patch_tree() { storage.serial_patch_tree.reset(); }
161
162 // interface_control
163 using GhostHandle = sph::BasicSPHGhostHandler<Tvec>;
164 using GhostHandleCache = typename GhostHandle::CacheMap;
165
166 inline void gen_ghost_handler(Tscal time_val) {
167
168 using CfgClass = sph::BasicSPHGhostHandlerConfig<Tvec>;
169 using BCConfig = typename CfgClass::Variant;
170
171 using BCFree = typename CfgClass::Free;
172 using BCPeriodic = typename CfgClass::Periodic;
173 using BCShearingPeriodic = typename CfgClass::ShearingPeriodic;
174
175 using SolverConfigBC = typename Config::BCConfig;
176 using SolverBCFree = typename SolverConfigBC::Free;
177 using SolverBCPeriodic = typename SolverConfigBC::Periodic;
178 using SolverBCShearingPeriodic = typename SolverConfigBC::ShearingPeriodic;
179
180 // boundary condition selections
181 if (SolverBCFree *c
182 = std::get_if<SolverBCFree>(&solver_config.boundary_config.config)) {
183 storage.ghost_handler.set(
184 GhostHandle{
185 scheduler(),
186 BCFree{},
187 storage.patch_rank_owner,
188 storage.xyzh_ghost_layout});
189 } else if (
190 SolverBCPeriodic *c
191 = std::get_if<SolverBCPeriodic>(&solver_config.boundary_config.config)) {
192 storage.ghost_handler.set(
193 GhostHandle{
194 scheduler(),
195 BCPeriodic{},
196 storage.patch_rank_owner,
197 storage.xyzh_ghost_layout});
198 } else if (
199 SolverBCShearingPeriodic *c
200 = std::get_if<SolverBCShearingPeriodic>(&solver_config.boundary_config.config)) {
201 storage.ghost_handler.set(
202 GhostHandle{
203 scheduler(),
204 BCShearingPeriodic{
205 c->shear_base, c->shear_dir, c->shear_speed * time_val, c->shear_speed},
206 storage.patch_rank_owner,
207 storage.xyzh_ghost_layout});
208 }
209 }
210 inline void reset_ghost_handler() { storage.ghost_handler.reset(); }
211
213 void build_ghost_cache();
215 void clear_ghost_cache();
216
219
220 // trees
221 using RTree = typename Config::RTree;
226
230 void reset_presteps_rint();
231
236
238 void sph_prestep(Tscal time_val, Tscal dt);
239
241 void apply_position_boundary(Tscal time_val);
242
244 void update_artificial_viscosity(Tscal dt);
245
247 void init_ghost_layout();
248
253
255 void compute_eos_fields();
256
258 void reset_eos_fields();
259
261 void prepare_corrector();
263 void update_derivs(Tscal dt_hydro);
270 bool apply_corrector(Tscal dt, u64 Npart_all);
271
274
275 Solver(ShamrockCtx &context) : context(context) {}
276
278 void init_solver_graph();
279
281 void vtk_do_dump(std::string filename, bool add_patch_world_id);
282
283 void set_debug_dump(bool _do_debug_dump, std::string _debug_dump_filename) {
284 solver_config.set_debug_dump(_do_debug_dump, _debug_dump_filename);
285 }
286
287 inline void print_timestep_logs() {
288 if (shamcomm::world_rank() == 0) {
289 logger::info_ln("SPH", "iteration since start :", solve_logs.get_iteration_count());
290 logger::info_ln("SPH", "time since start :", shambase::details::get_wtime(), "(s)");
291 }
292 }
293
295 TimestepLog evolve_once();
296
298 Tscal evolve_once_time_expl(Tscal t_current, Tscal dt_input) {
299 set_time(t_current);
300 set_next_dt(dt_input);
301 evolve_once();
302 return get_dt_sph();
303 }
304
305 inline EvolveUntilResults evolve_until(
306 Tscal target_time, i32 niter_max, f64 max_walltime = -1) {
307
308 const bool niter_limit_active = (niter_max >= 0);
309 const bool walltime_limit_active = (max_walltime >= 0);
310
311 if (shamcomm::world_rank() == 0) {
313 "SPH",
314 shambase::format(
315 "evolve_until (target_time = {:.2f}s, niter_max = {}, max_walltime = "
316 "{:.2f}s)",
317 target_time,
318 niter_max,
319 max_walltime));
320 }
321
322 auto synced_wtime = [&]() -> f64 {
323 if (walltime_limit_active) {
324 return shamalgs::collective::allreduce_max(shambase::details::get_wtime());
325 }
326 return 0;
327 };
328
329 auto step = [&]() {
330 Tscal dt = get_dt_sph();
331 Tscal t = get_time();
332
333 if (t > target_time) {
335 "the target time is higher than the current time");
336 }
337
338 if (t + dt > target_time) {
339 set_next_dt(target_time - t);
340 }
341 evolve_once();
342 };
343
344 f64 start_wall_time = (walltime_limit_active) ? synced_wtime() : 0;
345
346 i32 next_walltime_check_iter
347 = walltime_limit_active ? 1 : std::numeric_limits<i32>::max();
348
349 i32 iter_count = 0;
350
351 while (get_time() < target_time) {
352 step();
353 iter_count++;
354
355 // if the iteration count is greater than the maximum iteration count
356 if (niter_limit_active && iter_count >= niter_max) {
357 if (shamcomm::world_rank() == 0) {
358 logger::info_ln(
359 "SPH", "stopping evolve until because of niter =", iter_count);
360 }
361 return {
362 .reach_target_time = false,
363 .reach_niter_max = true,
364 .reach_max_walltime = false,
365 .iter_count = iter_count,
366 };
367 }
368
369 // if walltime limit is active and the next walltime check is due
370 if (walltime_limit_active && iter_count >= next_walltime_check_iter) {
371 f64 global_walltime = synced_wtime();
372
373 // if the global walltime is greater than the max walltime
374 if (global_walltime >= max_walltime) {
375 if (shamcomm::world_rank() == 0) {
376 logger::info_ln(
377 "SPH",
378 shambase::format(
379 "stopping evolve until because of "
380 "max_walltime = {:.2f}s > {:.2f}s",
381 global_walltime,
382 max_walltime));
383 }
384 return {
385 .reach_target_time = false,
386 .reach_niter_max = false,
387 .reach_max_walltime = true,
388 .iter_count = iter_count,
389 };
390 }
391
392 f64 sec_per_iter
393 = (global_walltime - start_wall_time) / static_cast<f64>(iter_count);
394
395 auto get_remaining_iters = [&](f64 delta_walltime, f64 factor) -> i32 {
396 if (sec_per_iter > 0) {
397 f64 tmp = factor * delta_walltime / sec_per_iter;
398 if (tmp > std::numeric_limits<i32>::max()) {
399 return std::numeric_limits<i32>::max();
400 }
401 return static_cast<i32>(tmp);
402 }
403 return 1000; // default to 1000 iterations if sec_per_iter is 0
404 };
405
406 i32 iters_to_limit = get_remaining_iters(max_walltime - global_walltime, 0.25);
407 i32 iters_to_next_check = iters_to_limit;
408
409 next_walltime_check_iter = iter_count + std::max(1, iters_to_next_check);
410
411 if (shamcomm::world_rank() == 0) {
412 logger::info_ln(
413 "SPH",
414 shambase::format(
415 "next walltime check in {:.2f}s (niter = {}) global walltime = "
416 "{:.2f}s (max_walltime = {:.2f}s)",
417 iters_to_next_check * sec_per_iter,
418 iters_to_next_check,
419 global_walltime,
420 max_walltime));
421 }
422 }
423 }
424
425 print_timestep_logs();
426
427 return {
428 .reach_target_time = true,
429 .reach_niter_max = false,
430 .reach_max_walltime = false,
431 .iter_count = iter_count,
432 };
433 }
434 };
435
436} // namespace shammodels::sph
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
The MPI scheduler.
void reset_presteps_rint()
Resets tree radius interval field.
Definition Solver.cpp:1242
void ensure_time_state_edges()
Register time/dt/cfl_multiplier synchronized edges if missing (idempotent).
Definition Solver.hpp:124
void reset_merge_ghosts_fields()
Resets merged ghost field data.
Definition Solver.cpp:1519
void update_sync_load_values()
Updates load balancing values and synchronizes patch ownership.
Definition Solver.cpp:1818
Tscal & cfl_multiplier_edge_value()
Access synchronized CFL multiplier (scheduler edge "cfl_multiplier").
Definition Solver.hpp:108
bool apply_corrector(Tscal dt, u64 Npart_all)
Definition Solver.cpp:1813
void merge_position_ghost()
Merges ghost particle positions from neighboring patches.
Definition Solver.cpp:889
void reset_eos_fields()
Frees memory allocated for EOS fields.
Definition Solver.cpp:1545
void prepare_corrector()
Saves old derivative fields for predictor-corrector integration.
Definition Solver.cpp:1551
void build_ghost_cache()
Builds ghost particle interface cache for inter-patch communication.
Definition Solver.cpp:867
void update_artificial_viscosity(Tscal dt)
Updates artificial viscosity coefficients for shock capturing.
Definition Solver.cpp:1528
TimestepLog evolve_once()
Performs one complete SPH timestep evolution.
Definition Solver.cpp:1825
void vtk_do_dump(std::string filename, bool add_patch_world_id)
Writes VTK dump file for visualization.
Definition Solver.cpp:644
void update_derivs(Tscal dt_hydro)
Updates time derivatives and applies external forces.
Definition Solver.cpp:1647
void build_merged_pos_trees()
Builds spatial BVH trees for merged positions including ghosts.
Definition Solver.cpp:932
void clear_merged_pos_trees()
Clears merged position trees to free memory.
Definition Solver.cpp:937
void init_solver_graph()
Initializes the solver graph for computation pipeline.
Definition Solver.cpp:122
void sph_prestep(Tscal time_val, Tscal dt)
Performs pre-step operations for SPH timestep.
Definition Solver.cpp:943
void compute_presteps_rint()
Computes maximum smoothing length in tree nodes for neighbor search.
Definition Solver.cpp:1205
void compute_eos_fields()
Computes equation of state fields (pressure, sound speed).
Definition Solver.cpp:1539
void apply_position_boundary(Tscal time_val)
Applies position-based boundary conditions.
Definition Solver.cpp:821
void reset_neighbors_cache()
Resets neighbor cache.
Definition Solver.cpp:1272
Tscal evolve_once_time_expl(Tscal t_current, Tscal dt_input)
Evolves system by one explicit timestep with specified time and dt.
Definition Solver.hpp:298
Tscal & dt_edge_value()
Access synchronized next dt (scheduler edge "dt", not solver_graph "dt").
Definition Solver.hpp:100
void communicate_merge_ghosts_fields()
Communicates and merges ghost particle fields across processes.
Definition Solver.cpp:1277
void clear_ghost_cache()
Clears ghost particle cache to free memory.
Definition Solver.cpp:883
void init_ghost_layout()
Initializes data layout for ghost particle fields.
Definition Solver.cpp:1190
void start_neighbors_cache()
Builds neighbor particle cache for SPH calculations.
Definition Solver.cpp:1247
Tscal & time_edge_value()
Access synchronized simulation time (scheduler edge "time").
Definition Solver.hpp:92
This header file contains utility functions related to exception handling in the code.
T & get_check_ref(const std::unique_ptr< T > &ptr, SourceLocation loc=SourceLocation())
Takes a std::unique_ptr and returns a reference to the object it holds. It throws a std::runtime_erro...
Definition memory.hpp:110
ExcptTypes make_except_with_loc(std::string message, SourceLocation loc=SourceLocation{})
Create an exception with a message and a location.
i32 world_rank()
Gives the rank of the current process in the MPI communicator.
Definition worldInfo.cpp:40
namespace for the sph model
void info_ln(std::string module_name, Types... var2)
Prints a log message with multiple arguments followed by a newline.
Definition logs.hpp:133
f64 get_wtime()
Returns the current wall clock time in seconds.
The configuration for a sph solver.
BCConfig boundary_config
Boundary condition configuration.
u32 u_morton
The type of the Morton code for the tree.
BCConfig< Tvec > BCConfig
Configuration of the boundary conditions.
Class holding the logs of the solver /todo add a variable to keep only a definite number of steps in ...
Definition SolverLog.hpp:33