Shamrock 2025.10.0
Astrophysical Code
Loading...
Searching...
No Matches
PatchScheduler.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
22
25#include "shambase/time.hpp"
30#include <nlohmann/json.hpp>
31#include <unordered_set>
32#include <fstream>
33#include <functional>
34#include <memory>
35#include <stdexcept>
36#include <tuple>
37#include <vector>
38// #include "shamrock/scheduler/SerialPatchTree.hpp"
41// #include "shamrock/legacy/patch/patchdata_buffer.hpp"
43#include "shambackends/math.hpp"
52
54 u64 split_load_value = 0_u64;
55 u64 merge_load_value = 0_u64;
56};
57
64inline void to_json(nlohmann::json &j, const PatchSchedulerConfig &p) {
65 j = nlohmann::json{
66 {"split_load_value", p.split_load_value},
67 {"merge_load_value", p.merge_load_value},
68 };
69}
70
77inline void from_json(const nlohmann::json &j, PatchSchedulerConfig &p) {
78 j.at("split_load_value").get_to<u64>(p.split_load_value);
79 j.at("merge_load_value").get_to<u64>(p.merge_load_value);
80}
81
86class PatchScheduler {
87
89
90 public:
91 static constexpr u64 max_axis_patch_coord = LoadBalancer::max_box_sz;
92 static constexpr u64 max_axis_patch_coord_length = LoadBalancer::max_box_sz + 1;
93
94 using PatchTree = shamrock::scheduler::PatchTree;
95 using SchedulerPatchData = shamrock::scheduler::SchedulerPatchData;
97
98 std::shared_ptr<shamrock::patch::PatchDataLayerLayout> pdl_ptr;
99
102
104 SchedulerPatchData patch_data;
105 PatchTree patch_tree;
106 SynchronizedData synchronized_data;
107
108 // using unordered set is not an issue since we use the find command after
109 std::unordered_set<u64> owned_patch_id;
111
112 inline shamrock::patch::PatchDataLayerLayout &pdl_old() {
113 return shambase::get_check_ref(pdl_ptr);
114 }
115
116 inline std::shared_ptr<shamrock::patch::PatchDataLayerLayout> get_layout_ptr_old() const {
117 return pdl_ptr;
118 }
119
126 void scheduler_step(bool do_split_merge, bool do_load_balancing);
127
128 void init_mpi_required_types();
129
130 void free_mpi_required_types();
131
132 PatchScheduler(
133 const std::shared_ptr<shamrock::patch::PatchDataLayerLayout> &pdl_ptr,
134 u64 crit_split,
135 u64 crit_merge);
136
137 ~PatchScheduler();
138
139 std::string dump_status();
140
141 inline void update_local_load_value(std::function<u64(shamrock::patch::Patch)> load_function) {
142 for (u64 id : owned_patch_id) {
143 shamrock::patch::Patch &p = patch_list.local[patch_list.id_patch_to_local_idx[id]];
144 p.load_value = load_function(p);
145 }
146 patch_list.is_load_values_up_to_date = true;
147 }
148
149 template<class vectype>
150 std::tuple<vectype, vectype> get_box_tranform();
151
152 template<class vectype>
153 std::tuple<vectype, vectype> get_box_volume();
154
155 bool should_resize_box(bool node_in);
156
164 template<class vectype>
165 void set_coord_domain_bound(vectype bmin, vectype bmax) {
166
167 if (!pdl_old().check_main_field_type<vectype>()) {
168 std::invalid_argument(
169 std::string("the main field is not of the correct type to call this function\n")
170 + "fct called : " + __PRETTY_FUNCTION__
171 + "current patch data layout : " + pdl_old().get_description_str());
172 }
173
174 patch_data.sim_box.set_bounding_box<vectype>({bmin, bmax});
175
176 shamlog_debug_ln("PatchScheduler", "box resized to :", bmin, bmax);
177 }
178
186
187 template<u32 dim>
188 void make_patch_base_grid(std::array<u32, dim> patch_count);
189
196 template<class vectype>
197 void set_coord_domain_bound(std::tuple<vectype, vectype> box) {
198 auto [a, b] = box;
200 }
201
202 std::string format_patch_coord(shamrock::patch::Patch p);
203
204 void check_patchdata_locality_correctness();
205
206 [[deprecated]]
207 void dump_local_patches(std::string filename);
208
209 std::vector<std::unique_ptr<shamrock::patch::PatchDataLayer>> gather_data(u32 rank);
210
219 //[[deprecated]]
220 // inline u64 add_patch(shamrock::patch::Patch p, shamrock::patch::PatchData && pdat){
221 // p.id_patch = patch_list._next_patch_id;
222 // patch_list._next_patch_id ++;
223 //
224 // patch_list.global.push_back(p);
225 //
226 // patch_data.owned_data.insert({p.id_patch , pdat});
227 //
228 // return p.id_patch;
229 //}
230
231 void add_root_patch();
232
233 [[deprecated]]
234 void sync_build_LB(bool global_patch_sync, bool balance_load);
235
236 template<class vec>
237 inline shamrock::patch::PatchCoordTransform<vec> get_patch_transform() {
238 return get_sim_box().template get_patch_transform<vec>();
239 }
240
241 // template<class vec>
242 // inline SerialPatchTree<vec> make_serial_ptree(){
243 // return SerialPatchTree<vec>(patch_tree, get_patch_transform<vec>());
244 // }
245
260 template<class Function>
261 inline void for_each_patch_data(Function &&fct) {
262
263 patch_data.for_each_patchdata([&](u64 patch_id, shamrock::patch::PatchDataLayer &pdat) {
265 = patch_list.global[patch_list.id_patch_to_global_idx[patch_id]];
266
267 if (!cur_p.is_err_mode()) {
268 fct(patch_id, cur_p, pdat);
269 }
270 });
271 }
272
273 template<class Function>
274 inline void for_each_patch(Function &&fct) {
275
276 patch_data.for_each_patchdata([&](u64 patch_id, shamrock::patch::PatchDataLayer &pdat) {
279
280 // TODO should feed the sycl queue to the lambda
281 if (!cur_p.is_err_mode()) {
282 fct(patch_id, cur_p);
283 }
284 });
285 }
286
287 inline void for_each_global_patch(
288 const std::function<void(const shamrock::patch::Patch &)> &fct) {
289 for (const shamrock::patch::Patch &p : patch_list.global) {
290 if (!p.is_err_mode()) {
291 fct(p);
292 }
293 }
294 }
295
296 inline void for_each_local_patch(
297 const std::function<void(const shamrock::patch::Patch &)> &fct) {
298 for (const shamrock::patch::Patch &p : patch_list.local) {
299 if (!p.is_err_mode()) {
300 fct(p);
301 }
302 }
303 }
304
305 inline void for_each_local_patchdata(
306 const std::function<void(const shamrock::patch::Patch &, shamrock::patch::PatchDataLayer &)>
307 &fct) {
308 for (const shamrock::patch::Patch &p : patch_list.local) {
309 if (!p.is_err_mode()) {
310 fct(p, patch_data.get_pdat(p.id_patch));
311 }
312 }
313 }
314
315 inline void for_each_local_patch_nonempty(
316 std::function<void(const shamrock::patch::Patch &)> fct) {
317 patch_data.for_each_patchdata([&](u64 patch_id, shamrock::patch::PatchDataLayer &pdat) {
318 shamrock::patch::Patch &cur_p
319 = patch_list.global[patch_list.id_patch_to_global_idx.at(patch_id)];
320
321 if ((!cur_p.is_err_mode()) && (!pdat.is_empty())) {
322 fct(cur_p);
323 }
324 });
325 }
326
327 inline u32 get_patch_rank_owner(u64 patch_id) {
328 shamrock::patch::Patch &cur_p
329 = patch_list.global[patch_list.id_patch_to_global_idx.at(patch_id)];
330 return cur_p.node_owner_id;
331 }
332
333 inline void for_each_patchdata_nonempty(
334 std::function<void(const shamrock::patch::Patch, shamrock::patch::PatchDataLayer &)> fct) {
335 patch_data.for_each_patchdata([&](u64 patch_id, shamrock::patch::PatchDataLayer &pdat) {
336 shamrock::patch::Patch &cur_p
337 = patch_list.global[patch_list.id_patch_to_global_idx.at(patch_id)];
338
339 if ((!cur_p.is_err_mode()) && (!pdat.is_empty())) {
340 fct(cur_p, pdat);
341 }
342 });
343 }
344
345 template<class T>
346 inline shambase::DistributedData<T> map_owned_patchdata(
347 std::function<T(const shamrock::patch::Patch, shamrock::patch::PatchDataLayer &pdat)> fct) {
348 shambase::DistributedData<T> ret;
349
350 using namespace shamrock::patch;
351 for_each_patch_data([&](u64 id_patch, Patch cur_p, PatchDataLayer &pdat) {
352 ret.add_obj(id_patch, fct(cur_p, pdat));
353 });
354
355 return ret;
356 }
357
358 template<class T>
359 inline shambase::DistributedData<T> distrib_data_local_to_all_simple(
360 shambase::DistributedData<T> &src) {
361 using namespace shamrock::patch;
362
363 // TODO : after a split the scheduler patch list state does not match global =
364 // allgather(local) but here it is implicitely assumed, that's ... bad
365 return shamalgs::collective::fetch_all_simple<T, Patch>(
366 src, patch_list.local, patch_list.global, [](Patch p) {
367 return p.id_patch;
368 });
369 }
370
371 template<class T>
372 inline shambase::DistributedData<T> distrib_data_local_to_all_load_store(
373 shambase::DistributedData<T> &src) {
374 using namespace shamrock::patch;
375
376 return shamalgs::collective::fetch_all_storeload<T, Patch>(
377 src, patch_list.local, patch_list.global, [](Patch p) {
378 return p.id_patch;
379 });
380 }
381
382 template<class T>
383 inline shambase::DistributedData<T> map_owned_patchdata_fetch_simple(
384 std::function<T(const shamrock::patch::Patch, shamrock::patch::PatchDataLayer &pdat)> fct) {
385 shambase::DistributedData<T> ret;
386
387 using namespace shamrock::patch;
388 for_each_patch_data([&](u64 id_patch, Patch cur_p, PatchDataLayer &pdat) {
389 ret.add_obj(id_patch, fct(cur_p, pdat));
390 });
391
392 return distrib_data_local_to_all_simple(ret);
393 }
394
395 template<class T>
396 inline shambase::DistributedData<T> map_owned_patchdata_fetch_load_store(
397 std::function<T(const shamrock::patch::Patch, shamrock::patch::PatchDataLayer &pdat)> fct) {
398 shambase::DistributedData<T> ret;
399
400 using namespace shamrock::patch;
401 for_each_patch_data([&](u64 id_patch, Patch cur_p, PatchDataLayer &pdat) {
402 ret.add_obj(id_patch, fct(cur_p, pdat));
403 });
404
405 return distrib_data_local_to_all_load_store(ret);
406 }
407
408 template<class T>
409 inline shamrock::patch::PatchField<T> map_owned_to_patch_field_simple(
410 std::function<T(const shamrock::patch::Patch, shamrock::patch::PatchDataLayer &pdat)> fct) {
411 return shamrock::patch::PatchField<T>(map_owned_patchdata_fetch_simple(fct));
412 }
413
414 template<class T>
415 inline shamrock::patch::PatchField<T> map_owned_to_patch_field_load_store(
416 std::function<T(const shamrock::patch::Patch, shamrock::patch::PatchDataLayer &pdat)> fct) {
417 return shamrock::patch::PatchField<T>(map_owned_patchdata_fetch_load_store(fct));
418 }
419
420 inline u64 get_rank_count() {
421 StackEntry stack_loc{};
422 using namespace shamrock::patch;
423 u64 num_obj = 0; // TODO get_rank_count() in scheduler
424 for_each_patch_data([&](u64 id_patch, Patch cur_p, PatchDataLayer &pdat) {
425 num_obj += pdat.get_obj_cnt();
426 });
427
428 return num_obj;
429 }
430
431 inline u64 get_total_obj_count() {
432 StackEntry stack_loc{};
433 u64 part_cnt = get_rank_count();
434 return shamalgs::collective::allreduce_sum(part_cnt);
435 }
436
437 template<class T>
438 inline std::unique_ptr<sycl::buffer<T>> rankgather_field(u32 field_idx) {
439 StackEntry stack_loc{};
440 std::unique_ptr<sycl::buffer<T>> ret;
441
442 auto fd = pdl_old().get_field<T>(field_idx);
443 u64 nvar = fd.nvar;
444
445 u64 num_obj = get_rank_count();
446
447 if (num_obj > 0) {
448 ret = std::make_unique<sycl::buffer<T>>(num_obj * nvar);
449
450 using namespace shamrock::patch;
451
452 u64 ptr = 0; // TODO accumulate_field() in scheduler ?
453 for_each_patch_data([&](u64 id_patch, Patch cur_p, PatchDataLayer &pdat) {
454 using namespace shamalgs::memory;
455 using namespace shambase;
456
457 if (pdat.get_obj_cnt() > 0) {
458 write_with_offset_into(
459 shamsys::instance::get_compute_scheduler().get_queue(),
460 get_check_ref(ret),
461 pdat.get_field<T>(field_idx).get_buf(),
462 ptr,
463 pdat.get_obj_cnt() * nvar);
464
465 ptr += pdat.get_obj_cnt() * nvar;
466 }
467 });
468 }
469
470 return ret;
471 }
472
473 // template<class Function, class Pfield>
474 // inline void compute_patch_field(Pfield & field, MPI_Datatype & dtype , Function && lambda){
475 // field.local_nodes_value.resize(patch_list.local.size());
476 //
477 //
478 //
479 // for (u64 idx = 0; idx < patch_list.local.size(); idx++) {
480 //
481 // Patch &cur_p = patch_list.local[idx];
482 //
483 // PatchDataBuffer pdatbuf =
484 // attach_to_patchData(patch_data.owned_data.at(cur_p.id_patch));
485 //
486 // field.local_nodes_value[idx] =
487 // lambda(shamsys::instance::get_compute_queue(),cur_p,pdatbuf);
488 //
489 // }
490 //
491 // field.build_global(dtype);
492 //
493 // }
494
495 template<class Function, class Pfield>
496 inline void compute_patch_field(Pfield &field, MPI_Datatype &dtype, Function &&lambda) {
497 field.local_nodes_value.resize(patch_list.local.size());
498
499 for (u64 idx = 0; idx < patch_list.local.size(); idx++) {
500
501 shamrock::patch::Patch &cur_p = patch_list.local[idx];
502
503 if (!cur_p.is_err_mode()) {
504 field.local_nodes_value[idx] = lambda(
505 shamsys::instance::get_compute_queue(),
506 cur_p,
507 patch_data.owned_data.get(cur_p.id_patch));
508 }
509 }
510
511 field.build_global(dtype);
512 }
513
514 inline auto get_node_set_edge_patchdata_layer_refs() {
515 shamrock::solvergraph::NodeSetEdge<shamrock::solvergraph::PatchDataLayerRefs> node_set_edge(
516 [&](shamrock::solvergraph::PatchDataLayerRefs &edge) {
517 edge.free_alloc();
518 using namespace shamrock::patch;
519 for_each_patchdata_nonempty([&](Patch cur_p, PatchDataLayer &pdat) {
520 edge.patchdatas.add_obj(cur_p.id_patch, std::ref(pdat));
521 });
522 });
523
524 return std::make_shared<decltype(node_set_edge)>(std::move(node_set_edge));
525 };
526
533 std::vector<u64> add_root_patches(std::vector<shamrock::patch::PatchCoord<3>> coords);
534
535 shamrock::patch::SimulationBoxInfo &get_sim_box() { return patch_data.sim_box; }
536
537 nlohmann::json serialize_patch_metadata();
538
539 private:
540 void split_patches(std::unordered_set<u64> split_rq);
541 void merge_patches(std::unordered_set<u64> merge_rq);
542
543 void set_patch_pack_values(std::unordered_set<u64> merge_rq);
544};
function to run load balancing with the hilbert curve
Node that applies a custom function to modify connected edges.
Defines the PatchDataLayerRefs class for managing distributed references to patch data layers.
void to_json(nlohmann::json &j, const PatchSchedulerConfig &p)
Converts a PatchSchedulerConfig object to a JSON object.
void from_json(const nlohmann::json &j, PatchSchedulerConfig &p)
Deserializes a PatchSchedulerConfig object from a JSON object.
Header file for the patch struct and related function.
PatchData handling.
Declare a class to register and retrieve nodes and edges from a unique container.
std::uint32_t u32
32 bit unsigned integer
std::uint64_t u64
64 bit unsigned integer
void set_coord_domain_bound(std::tuple< vectype, vectype > box)
modify the bounding box of the patch domain
void for_each_patch_data(Function &&fct)
for each macro for patchadata example usage
SchedulerPatchData patch_data
handle the data of the patches of the scheduler
u64 crit_patch_split
splitting limit (if load value > crit_patch_split => patch split)
PatchTree patch_tree
handle the tree structure of the patches
void scheduler_step(bool do_split_merge, bool do_load_balancing)
scheduler step
void set_coord_domain_bound(vectype bmin, vectype bmax)
modify the bounding box of the patch domain
SynchronizedData synchronized_data
data that is synchroneous across all ranks
SchedulerPatchList patch_list
handle the list of the patches of the scheduler
std::unordered_set< u64 > owned_patch_id
(owned_patch_id = patch_list.build_local())
std::vector< u64 > add_root_patches(std::vector< shamrock::patch::PatchCoord< 3 > > coords)
add a root patch to the scheduler
u64 crit_patch_merge
merging limit (if load value < crit_patch_merge => patch merge)
void allpush_data(shamrock::patch::PatchDataLayer &pdat)
push data in the scheduler The content of pdat as to be the same for each node
void add_root_patch()
add patch to the scheduler
Handle the patch list of the mpi scheduler.
std::vector< shamrock::patch::Patch > global
contain the list of all patches in the simulation
std::unordered_map< u64, u64 > id_patch_to_global_idx
id_patch_to_global_idx[patch_id] = index in global patch list
iterator add_obj(u64 id, T &&obj)
Adds a new object to the collection.
PatchDataLayer container class, the layout is described in patchdata_layout.
Patch Tree : Tree structure organisation for an abstract list of patches Nb : this tree is compatible...
Definition PatchTree.hpp:29
Class to handle PatchData owned by the node.
virtual void free_alloc() override
Free allocated memory.
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
header for PatchData related function and declaration
Class to handle the patch list of the mpi scheduler.
This file contains the definition for the stacktrace related functionality.
shambase::details::BasicStackEntry StackEntry
Alias for shambase::details::BasicStackEntry.
Patch object that contain generic patch information.
Definition Patch.hpp:33
bool is_err_mode() const
check if a patch is in error mode
Definition Patch.hpp:119
u32 node_owner_id
node rank owner of this patch
Definition Patch.hpp:93
u64 id_patch
unique key that identify the patch
Definition Patch.hpp:86
header file to manage sycl