Shamrock 2025.10.0
Astrophysical Code
Loading...
Searching...
No Matches
GSPHGhostHandler.cpp
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
20#include <functional>
21#include <vector>
22
23template<class T>
24struct ShiftInfo {
25 sycl::vec<T, 3> shift;
26 sycl::vec<T, 3> shift_speed;
27};
28
29template<class T>
30using ShearPeriodicInfo =
32
33template<class T>
34inline ShiftInfo<T> compute_shift_infos(
35 i32_3 ioff, ShearPeriodicInfo<T> shear, sycl::vec<T, 3> bsize) {
36
37 i32 dx = ioff.x() * shear.shear_base.x();
38 i32 dy = ioff.y() * shear.shear_base.y();
39 i32 dz = ioff.z() * shear.shear_base.z();
40
41 i32 d = dx + dy + dz;
42
43 sycl::vec<T, 3> shift
44 = {(d * shear.shear_dir.x()) * shear.shear_value + bsize.x() * ioff.x(),
45 (d * shear.shear_dir.y()) * shear.shear_value + bsize.y() * ioff.y(),
46 (d * shear.shear_dir.z()) * shear.shear_value + bsize.z() * ioff.z()};
47 sycl::vec<T, 3> shift_speed
48 = {(d * shear.shear_dir.x()) * shear.shear_speed,
49 (d * shear.shear_dir.y()) * shear.shear_speed,
50 (d * shear.shear_dir.z()) * shear.shear_speed};
51
52 return {shift, shift_speed};
53}
54
55template<class T>
56inline void for_each_patch_shift(
57 ShearPeriodicInfo<T> shearinfo,
58 sycl::vec<T, 3> bsize,
59 std::function<void(i32_3, ShiftInfo<T>)> funct) {
60
61 i32_3 loop_offset = {0, 0, 0};
62
63 std::vector<i32_3> list_possible;
64
65 i32 repetition_x = 1 + abs(shearinfo.shear_dir.x());
66 i32 repetition_y = 1 + abs(shearinfo.shear_dir.y());
67 i32 repetition_z = 1 + abs(shearinfo.shear_dir.z());
68
69 T sz = bsize.x() * shearinfo.shear_dir.x() + bsize.y() * shearinfo.shear_dir.y()
70 + bsize.z() * shearinfo.shear_dir.z();
71
72 for (i32 xoff = -repetition_x; xoff <= repetition_x; xoff++) {
73 for (i32 yoff = -repetition_y; yoff <= repetition_y; yoff++) {
74 for (i32 zoff = -repetition_z; zoff <= repetition_z; zoff++) {
75
76 i32 dx = xoff * shearinfo.shear_base.x();
77 i32 dy = yoff * shearinfo.shear_base.y();
78 i32 dz = zoff * shearinfo.shear_base.z();
79
80 i32 d = dx + dy + dz;
81
82 i32 df = -int(d * shearinfo.shear_value / sz);
83
84 i32_3 off_d
85 = {shearinfo.shear_dir.x() * df,
86 shearinfo.shear_dir.y() * df,
87 shearinfo.shear_dir.z() * df};
88
89 list_possible.resize(list_possible.size() + 1);
90 list_possible[list_possible.size() - 1]
91 = i32_3{xoff + off_d.x(), yoff + off_d.y(), zoff + off_d.z()};
92 }
93 }
94 }
95
96 for (i32_3 off : list_possible) {
97
98 auto shift = compute_shift_infos(off, shearinfo, bsize);
99
100 funct(off, shift);
101 }
102}
103
104using namespace shammodels::gsph;
105
106template<class vec>
108 SerialPatchTree<vec> &sptree,
109 shamrock::patch::PatchtreeField<flt> &int_range_max_tree,
110 shamrock::patch::PatchField<flt> &int_range_max) -> GeneratorMap {
111
112 StackEntry stack_loc{};
113
114 using namespace shamrock::patch;
115 using namespace shammath;
116
117 i32 repetition_x = 1;
118 i32 repetition_y = 1;
119 i32 repetition_z = 1;
120
121 shamrock::patch::SimulationBoxInfo &sim_box = sched.get_sim_box();
122
123 PatchCoordTransform<vec> patch_coord_transf = sim_box.get_patch_transform<vec>();
124 vec bsize = sim_box.get_bounding_box_size<vec>();
125
126 GeneratorMap interf_map;
127
128 using CfgClass = gsph::GSPHGhostHandlerConfig<vec>;
129 using BCConfig = typename CfgClass::Variant;
130
131 using BCFree = typename CfgClass::Free;
132 using BCPeriodic = typename CfgClass::Periodic;
133 using BCShearingPeriodic = typename CfgClass::ShearingPeriodic;
134
135 if (BCPeriodic *cfg = std::get_if<BCPeriodic>(&ghost_config)) {
136 sycl::host_accessor acc_tf{
137 shambase::get_check_ref(int_range_max_tree.internal_buf), sycl::read_only};
138
139 for (i32 xoff = -repetition_x; xoff <= repetition_x; xoff++) {
140 for (i32 yoff = -repetition_y; yoff <= repetition_y; yoff++) {
141 for (i32 zoff = -repetition_z; zoff <= repetition_z; zoff++) {
142
143 // sender translation
144 vec periodic_offset = vec{xoff * bsize.x(), yoff * bsize.y(), zoff * bsize.z()};
145
146 sched.for_each_local_patch([&](const Patch &psender) {
147 CoordRange<vec> sender_bsize = patch_coord_transf.to_obj_coord(psender);
148 CoordRange<vec> sender_bsize_off = sender_bsize.add_offset(periodic_offset);
149
150 flt sender_volume = sender_bsize.get_volume();
151
152 flt sender_h_max = int_range_max.get(psender.id_patch);
153
154 using PtNode = typename SerialPatchTree<vec>::PtNode;
155
156 sptree.host_for_each_leafs(
157 [&](u64 tree_id, PtNode n) {
158 flt receiv_h_max = acc_tf[tree_id];
159 CoordRange<vec> receiv_exp{
160 n.box_min - receiv_h_max, n.box_max + receiv_h_max};
161
162 return receiv_exp.get_intersect(sender_bsize_off).is_not_empty();
163 },
164 [&](u64 id_found, PtNode n) {
165 if ((id_found == psender.id_patch) && (xoff == 0) && (yoff == 0)
166 && (zoff == 0)) {
167 return;
168 }
169
170 CoordRange<vec> receiv_exp
171 = CoordRange<vec>{n.box_min, n.box_max}.expand_all(
172 int_range_max.get(id_found));
173
174 CoordRange<vec> interf_volume = sender_bsize.get_intersect(
175 receiv_exp.add_offset(-periodic_offset));
176
177 interf_map.add_obj(
178 psender.id_patch,
179 id_found,
180 {periodic_offset,
181 {0, 0, 0},
182 {xoff, yoff, zoff},
183 interf_volume,
184 interf_volume.get_volume() / sender_volume});
185 });
186 });
187 }
188 }
189 }
190 } else if (BCShearingPeriodic *cfg = std::get_if<BCShearingPeriodic>(&ghost_config)) {
191 sycl::host_accessor acc_tf{
192 shambase::get_check_ref(int_range_max_tree.internal_buf), sycl::read_only};
193
194 for_each_patch_shift<flt>(*cfg, bsize, [&](i32_3 ioff, ShiftInfo<flt> shift) {
195 i32 xoff = ioff.x();
196 i32 yoff = ioff.y();
197 i32 zoff = ioff.z();
198
199 vec offset = shift.shift;
200
201 sched.for_each_local_patch([&](const Patch &psender) {
202 CoordRange<vec> sender_bsize = patch_coord_transf.to_obj_coord(psender);
203 CoordRange<vec> sender_bsize_off = sender_bsize.add_offset(offset);
204
205 flt sender_volume = sender_bsize.get_volume();
206
207 flt sender_h_max = int_range_max.get(psender.id_patch);
208
209 using PtNode = typename SerialPatchTree<vec>::PtNode;
210
211 sptree.host_for_each_leafs(
212 [&](u64 tree_id, PtNode n) {
213 flt receiv_h_max = acc_tf[tree_id];
214 CoordRange<vec> receiv_exp{
215 n.box_min - receiv_h_max, n.box_max + receiv_h_max};
216
217 return receiv_exp.get_intersect(sender_bsize_off).is_not_empty();
218 },
219 [&](u64 id_found, PtNode n) {
220 if ((id_found == psender.id_patch) && (xoff == 0) && (yoff == 0)
221 && (zoff == 0)) {
222 return;
223 }
224
225 CoordRange<vec> receiv_exp
226 = CoordRange<vec>{n.box_min, n.box_max}.expand_all(
227 int_range_max.get(id_found));
228
229 CoordRange<vec> interf_volume
230 = sender_bsize.get_intersect(receiv_exp.add_offset(-offset));
231
232 interf_map.add_obj(
233 psender.id_patch,
234 id_found,
235 {offset,
236 shift.shift_speed,
237 {xoff, yoff, zoff},
238 interf_volume,
239 interf_volume.get_volume() / sender_volume});
240 });
241 });
242 });
243
244 } else {
245 sycl::host_accessor acc_tf{
246 shambase::get_check_ref(int_range_max_tree.internal_buf), sycl::read_only};
247 // sender translation
248 vec periodic_offset = vec{0, 0, 0};
249
250 sched.for_each_local_patch([&](const Patch &psender) {
251 CoordRange<vec> sender_bsize = patch_coord_transf.to_obj_coord(psender);
252 CoordRange<vec> sender_bsize_off = sender_bsize.add_offset(periodic_offset);
253
254 flt sender_volume = sender_bsize.get_volume();
255
256 flt sender_h_max = int_range_max.get(psender.id_patch);
257
258 using PtNode = typename SerialPatchTree<vec>::PtNode;
259
260 sptree.host_for_each_leafs(
261 [&](u64 tree_id, PtNode n) {
262 flt receiv_h_max = acc_tf[tree_id];
263 CoordRange<vec> receiv_exp{n.box_min - receiv_h_max, n.box_max + receiv_h_max};
264
265 return receiv_exp.get_intersect(sender_bsize_off).is_not_empty();
266 },
267 [&](u64 id_found, PtNode n) {
268 if (id_found == psender.id_patch) {
269 return;
270 }
271
272 CoordRange<vec> receiv_exp = CoordRange<vec>{n.box_min, n.box_max}.expand_all(
273 int_range_max.get(id_found));
274
275 CoordRange<vec> interf_volume
276 = sender_bsize.get_intersect(receiv_exp.add_offset(-periodic_offset));
277
278 interf_map.add_obj(
279 psender.id_patch,
280 id_found,
281 {periodic_offset,
282 {0, 0, 0},
283 {0, 0, 0},
284 interf_volume,
285 interf_volume.get_volume() / sender_volume});
286 });
287 });
288 }
289
290 return interf_map;
291}
292
293template<class vec>
296 StackEntry stack_loc{};
297 using namespace shamrock::patch;
298
300
301 std::map<u64, f64> send_count_stats;
302
303 const u32 ixyz = sched.pdl_old().template get_field_idx<vec>("xyz");
304
305 gen.for_each([&](u64 sender, u64 receiver, InterfaceBuildInfos &build) {
306 shamrock::patch::PatchDataLayer &src = sched.patch_data.get_pdat(sender);
307 PatchDataField<vec> &xyz = src.get_field<vec>(ixyz);
308
309 sham::DeviceBuffer<u32> idxs_res = xyz.get_ids_where(
310 [](auto access, u32 id, vec vmin, vec vmax) {
311 return Patch::is_in_patch_converted(access[id], vmin, vmax);
312 },
313 build.cut_volume.lower,
314 build.cut_volume.upper);
315
316 u32 pcnt = idxs_res.get_size();
317
318 // prevent sending empty patches
319 if (pcnt == 0) {
320 return;
321 }
322
323 f64 ratio = f64(pcnt) / f64(src.get_obj_cnt());
324
325 shamlog_debug_ln(
326 "InterfaceGen",
327 "gen interface :",
328 sender,
329 "->",
330 receiver,
331 "volume ratio:",
332 build.volume_ratio,
333 "part_ratio:",
334 ratio);
335
336 res.add_obj(sender, receiver, InterfaceIdTable{build, std::move(idxs_res), ratio});
337
338 send_count_stats[sender] += ratio;
339 });
340
341 bool has_warn = false;
342
343 std::string warn_log = "";
344
345 for (auto &[k, v] : send_count_stats) {
346 if (v > 0.2) {
347 warn_log += shambase::format("\n patch {} high interf/patch volume: {}", k, v);
348 has_warn = true;
349 }
350 }
351
352 if (has_warn && shamcomm::world_rank() == 0) {
353 warn_log = "\n This can lead to high mpi "
354 "overhead, try to increase the patch split crit"
355 + warn_log;
356 }
357
358 if (has_warn) {
359 logger::warn_ln("InterfaceGen", "High interface/patch volume ratio." + warn_log);
360 }
361
362 return res;
363}
364
365template<class vec>
368 StackEntry stack_loc{};
369
370 static u32 cnt_dump_debug = 0;
371
372 std::string loc_graph = "";
373 interf_info.for_each([&loc_graph](u64 send, u64 recv, InterfaceIdTable &info) {
374 loc_graph += shambase::format(" p{} -> p{}\n", send, recv);
375 });
376
377 sched.for_each_patch_data(
379 if (pdat.get_obj_cnt() > 0) {
380 loc_graph += shambase::format(
381 " p{} [label= \"id={} N={}\"]\n", id, id, pdat.get_obj_cnt());
382 }
383 });
384
385 std::string dot_graph = "";
386 shamalgs::collective::gather_str(loc_graph, dot_graph);
387
388 dot_graph = "strict digraph {\n" + dot_graph + "}";
389
390 if (shamcomm::world_rank() == 0) {
391 std::string fname = shambase::format("ghost_graph_{}.dot", cnt_dump_debug);
392 logger::info_ln("GSPH Ghost", "writing", fname);
393 shambase::write_string_to_file(fname, dot_graph);
394 cnt_dump_debug++;
395 }
396}
397
constexpr const char * xyz
Position field (3D coordinates)
GSPH-specific ghost handler using Newtonian physics field names.
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
A buffer allocated in USM (Unified Shared Memory)
Container for objects shared between two distributed data elements.
void for_each(std::function< void(u64, u64, T &)> &&f)
Apply a function to all stored objects.
iterator add_obj(u64 left_id, u64 right_id, T &&obj)
Add an object associated with a patch pair.
Vector class based on std::array storage and mdspan.
Definition matrix.hpp:96
PatchDataLayer container class, the layout is described in patchdata_layout.
Store the information related to the size of the simulation box to convert patch integer coordinates ...
Definition SimBox.hpp:35
T get_bounding_box_size() const
Get the size of the stored bounding box of the domain.
Definition SimBox.hpp:87
PatchCoordTransform< T > get_patch_transform() const
Get a PatchCoordTransform object that describes the conversion between patch coordinates and domain c...
Definition SimBox.hpp:285
This header file contains utility functions related to exception handling in the code.
MPI string gather / allgather helpers (declarations; implementations in shamalgs/src/collective/gathe...
void gather_str(const std::string &send_vec, std::string &recv_vec)
Gathers a string from all nodes and store the result in a std::string.
void write_string_to_file(std::string filename, std::string s)
dump a string to a file
Definition string.hpp:168
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
i32 world_rank()
Gives the rank of the current process in the MPI communicator.
Definition worldInfo.cpp:40
namespace for math utility
Definition AABB.hpp:26
Patch object that contain generic patch information.
Definition Patch.hpp:33
u64 id_patch
unique key that identify the patch
Definition Patch.hpp:86