Shamrock 2025.10.0
Astrophysical Code
Loading...
Searching...
No Matches
BasicSPHGhosts.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
16
17/*
18
19Test code for godbolt
20
21
22#include <iostream>
23#include <vector>
24
25namespace sycl{
26 template<class T>
27 struct vec{
28 T _x,_y,_z;
29
30 inline T & x(){
31 return _x;
32 }
33
34 inline T & y(){
35 return _y;
36 }
37 inline T & z(){
38 return _z;
39 }
40 };
41}
42
43
44using i32 = int;
45using i32_3 = sycl::vec<i32>;
46
47template<class T>
48struct ShiftInfo{
49 sycl::vec<T> shift;
50 sycl::vec<T> shift_speed;
51};
52
53template<class T>
54struct ShearPeriodicInfo{
55 i32_3 shear_base;
56 i32_3 shear_dir;
57 T shear_value;
58 T shear_speed;
59};
60
61template<class T>
62inline ShiftInfo<T> compute_shift_infos(
63 i32_3 ioff, ShearPeriodicInfo<T> shear, sycl::vec<T> bsize
64 ){
65
66 i32 dx = ioff.x()*shear.shear_base.x();
67 i32 dy = ioff.y()*shear.shear_base.y();
68 i32 dz = ioff.z()*shear.shear_base.z();
69
70 i32 d = dx + dy + dz;
71
72 sycl::vec<T> shift = {
73 (d*shear.shear_dir.x())*shear.shear_value + bsize.x()*ioff.x(),
74 (d*shear.shear_dir.y())*shear.shear_value + bsize.y()*ioff.y() ,
75 (d*shear.shear_dir.z())*shear.shear_value + bsize.z()*ioff.z()
76 };
77 sycl::vec<T> shift_speed = {
78 (d*shear.shear_dir.x())*shear.shear_speed,
79 (d*shear.shear_dir.y())*shear.shear_speed,
80 (d*shear.shear_dir.z())*shear.shear_speed
81 };
82
83 return {shift,shift_speed};
84}
85
86template<class T>
87inline void for_each_patch_shift(ShearPeriodicInfo<T> shearinfo, sycl::vec<T> bsize){
88
89 i32_3 loop_offset = {0,0,0};
90
91 std::vector<i32_3> list_possible;
92
93
94 i32 repetition_x = 1;
95 i32 repetition_y = 1;
96 i32 repetition_z = 1;
97
98
99
100 for (i32 xoff = -repetition_x; xoff <= repetition_x; xoff++) {
101 for (i32 yoff = -repetition_y; yoff <= repetition_y; yoff++) {
102 for (i32 zoff = -repetition_z; zoff <= repetition_z; zoff++) {
103
104
105 i32 dx = xoff*shearinfo.shear_base.x();
106 i32 dy = yoff*shearinfo.shear_base.y();
107 i32 dz = zoff*shearinfo.shear_base.z();
108
109 i32 d = dx + dy + dz;
110
111 i32 df = -int(d * shearinfo.shear_value);
112
113 i32_3 off_d = {
114 shearinfo.shear_dir.x()*df,
115 shearinfo.shear_dir.y()*df,
116 shearinfo.shear_dir.z()*df
117 };
118
119 list_possible.push_back({xoff+off_d.x(),yoff+off_d.y(),zoff+off_d.z()});
120 }
121 }
122 }
123
124 for(i32_3 off : list_possible){
125
126 auto shift = compute_shift_infos(off,shearinfo,bsize);
127
128 std::cout <<
129 off.x() << " " << off.y() << " " << off.z() << " | " <<
130 shift.shift.x() << " " << shift.shift.y() << " " << shift.shift.z() << " "<<std::endl;
131 }
132
133
134
135}
136
137
138int main(){
139
140 ShearPeriodicInfo<float> shear{
141 {1,0,0},
142 {0,0,1},
143 13.5,
144 1
145 };
146
147 for_each_patch_shift(shear, {1,1,1});
148
149}
150
151
152*/
153
154#include "shambase/exception.hpp"
155#include "shambase/time.hpp"
158#include "shamcomm/worldInfo.hpp"
163#include <functional>
164#include <vector>
165
166template<class T>
167struct ShiftInfo {
168 sycl::vec<T, 3> shift;
169 sycl::vec<T, 3> shift_speed;
170};
171
172template<class T>
173using ShearPeriodicInfo =
175
176template<class T>
177inline ShiftInfo<T> compute_shift_infos(
178 i32_3 ioff, ShearPeriodicInfo<T> shear, sycl::vec<T, 3> bsize) {
179
180 i32 dx = ioff.x() * shear.shear_base.x();
181 i32 dy = ioff.y() * shear.shear_base.y();
182 i32 dz = ioff.z() * shear.shear_base.z();
183
184 i32 d = dx + dy + dz;
185
186 sycl::vec<T, 3> shift
187 = {(d * shear.shear_dir.x()) * shear.shear_value + bsize.x() * ioff.x(),
188 (d * shear.shear_dir.y()) * shear.shear_value + bsize.y() * ioff.y(),
189 (d * shear.shear_dir.z()) * shear.shear_value + bsize.z() * ioff.z()};
190 sycl::vec<T, 3> shift_speed
191 = {(d * shear.shear_dir.x()) * shear.shear_speed,
192 (d * shear.shear_dir.y()) * shear.shear_speed,
193 (d * shear.shear_dir.z()) * shear.shear_speed};
194
195 return {shift, shift_speed};
196}
197
198template<class T>
199inline void for_each_patch_shift(
200 ShearPeriodicInfo<T> shearinfo,
201 sycl::vec<T, 3> bsize,
202 std::function<void(i32_3, ShiftInfo<T>)> funct) {
203
204 i32_3 loop_offset = {0, 0, 0};
205
206 std::vector<i32_3> list_possible;
207
208 // logger::raw_ln("testing :",shearinfo.shear_value,shearinfo.shear_dir, shearinfo.shear_base);
209
210 // a bit of dirty fix doesn't hurt
211 // this should be done in a better way a some point
212 i32 repetition_x = 1 + sham::abs(shearinfo.shear_dir.x());
213 i32 repetition_y = 1 + sham::abs(shearinfo.shear_dir.y());
214 i32 repetition_z = 1 + sham::abs(shearinfo.shear_dir.z());
215
216 T sz = bsize.x() * shearinfo.shear_dir.x() + bsize.y() * shearinfo.shear_dir.y()
217 + bsize.z() * shearinfo.shear_dir.z();
218
219 for (i32 xoff = -repetition_x; xoff <= repetition_x; xoff++) {
220 for (i32 yoff = -repetition_y; yoff <= repetition_y; yoff++) {
221 for (i32 zoff = -repetition_z; zoff <= repetition_z; zoff++) {
222
223 i32 dx = xoff * shearinfo.shear_base.x();
224 i32 dy = yoff * shearinfo.shear_base.y();
225 i32 dz = zoff * shearinfo.shear_base.z();
226
227 i32 d = dx + dy + dz;
228
229 i32 df = -int(d * shearinfo.shear_value / sz);
230
231 i32_3 off_d
232 = {shearinfo.shear_dir.x() * df,
233 shearinfo.shear_dir.y() * df,
234 shearinfo.shear_dir.z() * df};
235
236 // on redhat based systems stl vector freaks out
237 // because iterator to back does *(end() - 1)
238 // the issue is that the compiler gets confused
239 // by the sycl::vec defining the - operator
240 // creating the ambiguity and ...
241 // ultimatly the compiler shitting itself
242 list_possible.resize(list_possible.size() + 1);
243 list_possible[list_possible.size() - 1]
244 = i32_3{xoff + off_d.x(), yoff + off_d.y(), zoff + off_d.z()};
245 }
246 }
247 }
248
249 // logger::raw_ln("trying", list_possible.size(), "patches ghosts");
250
251 for (i32_3 off : list_possible) {
252
253 auto shift = compute_shift_infos(off, shearinfo, bsize);
254
255 // logger::raw_ln("check :",off,shift.shift, shift.shift_speed);
256
257 funct(off, shift);
258 }
259}
260
261using namespace shammodels::sph;
262
263template<class vec>
265 SerialPatchTree<vec> &sptree,
266 shamrock::patch::PatchtreeField<flt> &int_range_max_tree,
267 shamrock::patch::PatchField<flt> &int_range_max) -> GeneratorMap {
268
269 StackEntry stack_loc{};
270
271 using namespace shamrock::patch;
272 using namespace shammath;
273
274 i32 repetition_x = 1;
275 i32 repetition_y = 1;
276 i32 repetition_z = 1;
277
278 shamrock::patch::SimulationBoxInfo &sim_box = sched.get_sim_box();
279
280 PatchCoordTransform<vec> patch_coord_transf = sim_box.get_patch_transform<vec>();
281 vec bsize = sim_box.get_bounding_box_size<vec>();
282
283 GeneratorMap interf_map;
284
286 using BCConfig = typename CfgClass::Variant;
287
288 using BCFree = typename CfgClass::Free;
289 using BCPeriodic = typename CfgClass::Periodic;
290 using BCShearingPeriodic = typename CfgClass::ShearingPeriodic;
291
292 shambase::Timer base_timer;
293 base_timer.start();
294
295 if (BCPeriodic *cfg = std::get_if<BCPeriodic>(&ghost_config)) {
296 sycl::host_accessor acc_tf{
297 shambase::get_check_ref(int_range_max_tree.internal_buf), sycl::read_only};
298
299 for (i32 xoff = -repetition_x; xoff <= repetition_x; xoff++) {
300 for (i32 yoff = -repetition_y; yoff <= repetition_y; yoff++) {
301 for (i32 zoff = -repetition_z; zoff <= repetition_z; zoff++) {
302
303 // sender translation
304 vec periodic_offset = vec{xoff * bsize.x(), yoff * bsize.y(), zoff * bsize.z()};
305
306 sycl::host_accessor tree{
307 shambase::get_check_ref(sptree.serial_tree_buf), sycl::read_only};
308 sycl::host_accessor lpid{
309 shambase::get_check_ref(sptree.linked_patch_ids_buf), sycl::read_only};
310
311#pragma omp parallel for
312 for (u32 i = 0; i < sched.patch_list.local.size(); i++) {
313 const shamrock::patch::Patch &psender = sched.patch_list.local[i];
314 if (!psender.is_err_mode()) {
315 CoordRange<vec> sender_bsize = patch_coord_transf.to_obj_coord(psender);
316 CoordRange<vec> sender_bsize_off
317 = sender_bsize.add_offset(periodic_offset);
318
319 flt sender_volume = sender_bsize.get_volume();
320
321 flt sender_h_max = int_range_max.get(psender.id_patch);
322
323 using PtNode = typename SerialPatchTree<vec>::PtNode;
324
325 sptree.host_for_each_leafs_internal(
326 [&](u64 tree_id, PtNode n) {
327 flt receiv_h_max = acc_tf[tree_id];
328 CoordRange<vec> receiv_exp{
329 n.box_min - receiv_h_max, n.box_max + receiv_h_max};
330
331 return receiv_exp.get_intersect(sender_bsize_off)
332 .is_not_empty();
333 },
334 [&](u64 id_found, PtNode n) {
335 if ((id_found == psender.id_patch) && (xoff == 0) && (yoff == 0)
336 && (zoff == 0)) {
337 return;
338 }
339
340 CoordRange<vec> receiv_exp
341 = CoordRange<vec>{n.box_min, n.box_max}.expand_all(
342 int_range_max.get(id_found));
343
344 CoordRange<vec> interf_volume = sender_bsize.get_intersect(
345 receiv_exp.add_offset(-periodic_offset));
346
347#pragma omp critical
348 interf_map.add_obj(
349 psender.id_patch,
350 id_found,
351 {periodic_offset,
352 {0, 0, 0},
353 {xoff, yoff, zoff},
354 interf_volume,
355 interf_volume.get_volume() / sender_volume});
356 },
357 tree,
358 lpid);
359 }
360 }
361 }
362 }
363 }
364 } else if (BCShearingPeriodic *cfg = std::get_if<BCShearingPeriodic>(&ghost_config)) {
365 sycl::host_accessor acc_tf{
366 shambase::get_check_ref(int_range_max_tree.internal_buf), sycl::read_only};
367
368 for_each_patch_shift<flt>(*cfg, bsize, [&](i32_3 ioff, ShiftInfo<flt> shift) {
369 i32 xoff = ioff.x();
370 i32 yoff = ioff.y();
371 i32 zoff = ioff.z();
372
373 vec offset = shift.shift;
374
375 sycl::host_accessor tree{
376 shambase::get_check_ref(sptree.serial_tree_buf), sycl::read_only};
377 sycl::host_accessor lpid{
378 shambase::get_check_ref(sptree.linked_patch_ids_buf), sycl::read_only};
379
380#pragma omp parallel for
381 for (u32 i = 0; i < sched.patch_list.local.size(); i++) {
382 const shamrock::patch::Patch &psender = sched.patch_list.local[i];
383 if (!psender.is_err_mode()) {
384
385 CoordRange<vec> sender_bsize = patch_coord_transf.to_obj_coord(psender);
386 CoordRange<vec> sender_bsize_off = sender_bsize.add_offset(offset);
387
388 flt sender_volume = sender_bsize.get_volume();
389
390 flt sender_h_max = int_range_max.get(psender.id_patch);
391
392 using PtNode = typename SerialPatchTree<vec>::PtNode;
393
394 sptree.host_for_each_leafs_internal(
395 [&](u64 tree_id, PtNode n) {
396 flt receiv_h_max = acc_tf[tree_id];
397 CoordRange<vec> receiv_exp{
398 n.box_min - receiv_h_max, n.box_max + receiv_h_max};
399
400 return receiv_exp.get_intersect(sender_bsize_off).is_not_empty();
401 },
402 [&](u64 id_found, PtNode n) {
403 if ((id_found == psender.id_patch) && (xoff == 0) && (yoff == 0)
404 && (zoff == 0)) {
405 return;
406 }
407
408 CoordRange<vec> receiv_exp
409 = CoordRange<vec>{n.box_min, n.box_max}.expand_all(
410 int_range_max.get(id_found));
411
412 CoordRange<vec> interf_volume
413 = sender_bsize.get_intersect(receiv_exp.add_offset(-offset));
414
415#pragma omp critical
416 interf_map.add_obj(
417 psender.id_patch,
418 id_found,
419 {offset,
420 shift.shift_speed,
421 {xoff, yoff, zoff},
422 interf_volume,
423 interf_volume.get_volume() / sender_volume});
424
425 // logger::raw_ln("found :",offset, shift.shift_speed, vec{xoff, yoff,
426 // zoff});
427 },
428 tree,
429 lpid);
430 }
431 }
432 });
433
434 } else {
435 sycl::host_accessor acc_tf{
436 shambase::get_check_ref(int_range_max_tree.internal_buf), sycl::read_only};
437 // sender translation
438 vec periodic_offset = vec{0, 0, 0};
439
440 sycl::host_accessor tree{shambase::get_check_ref(sptree.serial_tree_buf), sycl::read_only};
441 sycl::host_accessor lpid{
442 shambase::get_check_ref(sptree.linked_patch_ids_buf), sycl::read_only};
443
444#pragma omp parallel for
445 for (u32 i = 0; i < sched.patch_list.local.size(); i++) {
446 const shamrock::patch::Patch &psender = sched.patch_list.local[i];
447 if (!psender.is_err_mode()) {
448 CoordRange<vec> sender_bsize = patch_coord_transf.to_obj_coord(psender);
449 CoordRange<vec> sender_bsize_off = sender_bsize.add_offset(periodic_offset);
450
451 flt sender_volume = sender_bsize.get_volume();
452
453 flt sender_h_max = int_range_max.get(psender.id_patch);
454
455 using PtNode = typename SerialPatchTree<vec>::PtNode;
456
457 sptree.host_for_each_leafs_internal(
458 [&](u64 tree_id, PtNode n) {
459 flt receiv_h_max = acc_tf[tree_id];
460 CoordRange<vec> receiv_exp{
461 n.box_min - receiv_h_max, n.box_max + receiv_h_max};
462
463 return receiv_exp.get_intersect(sender_bsize_off).is_not_empty();
464 },
465 [&](u64 id_found, PtNode n) {
466 if (id_found == psender.id_patch) {
467 return;
468 }
469
470 CoordRange<vec> receiv_exp
471 = CoordRange<vec>{n.box_min, n.box_max}.expand_all(
472 int_range_max.get(id_found));
473
474 CoordRange<vec> interf_volume
475 = sender_bsize.get_intersect(receiv_exp.add_offset(-periodic_offset));
476
477#pragma omp critical
478 interf_map.add_obj(
479 psender.id_patch,
480 id_found,
481 {periodic_offset,
482 {0, 0, 0},
483 {0, 0, 0},
484 interf_volume,
485 interf_volume.get_volume() / sender_volume});
486 },
487 tree,
488 lpid);
489 }
490 }
491 }
492
493 base_timer.stop();
494
495 // f64 worse_time = shamalgs::collective::allreduce_max(base_timer.elasped_sec());
496 // if (shamcomm::world_rank() == 0) {
497 // shamlog_info_ln(
498 // "BasicSPHGhosts",
499 // "find_interfaces time:",
500 // base_timer.get_time_str(),
501 // "worse time:",
502 // worse_time);
503 // }
504
505 // interf_map.for_each([](u64 sender, u64 receiver, InterfaceBuildInfos build){
506 // logger::raw_ln("found interface
507 // :",sender,"->",receiver,"ratio:",build.volume_ratio,
508 // "volume:",build.cut_volume.lower,build.cut_volume.upper);
509 // });
510
511 return interf_map;
512}
513
514template<class vec>
517 StackEntry stack_loc{};
518 using namespace shamrock::patch;
519
520 // ----------------------------------------------------------------------------------------
521 // temporary wrapper to slowly migrate to the new solvergraph
522 auto positions = std::make_shared<shamrock::solvergraph::FieldRefs<vec>>("", "");
524 sched.for_each_patchdata_nonempty([&](const Patch p, PatchDataLayer &pdat) {
525 positions_refs.add_obj(p.id_patch, std::ref(pdat.get_field<vec>(0)));
526 });
527 positions->set_refs(positions_refs);
528
529 auto interface_infos
530 = std::make_shared<shamrock::solvergraph::DDSharedScalar<InterfaceBuildInfos>>("", "");
531 interface_infos->values = std::forward<GeneratorMap>(gen);
532
533 auto interface_id_table
534 = std::make_shared<shamrock::solvergraph::DDSharedScalar<InterfaceIdTable>>("", "");
535
537 node.set_edges(positions, interface_infos, interface_id_table);
538 node.evaluate();
539 // ----------------------------------------------------------------------------------------
540
541 return std::move(interface_id_table->values);
542}
543
544template<class vec>
545void BasicSPHGhostHandler<vec>::gen_debug_patch_ghost(
547 StackEntry stack_loc{};
548
549 static u32 cnt_dump_debug = 0;
550
551 std::string loc_graph = "";
552 interf_info.for_each([&loc_graph](u64 send, u64 recv, InterfaceIdTable &info) {
553 loc_graph += sham::format(" p{} -> p{}\n", send, recv);
554 });
555
556 sched.for_each_patch_data(
558 if (pdat.get_obj_cnt() > 0) {
559 loc_graph += sham::format(
560 " p{} [label= \"id={} N={}\"]\n", id, id, pdat.get_obj_cnt());
561 }
562 });
563
564 std::string dot_graph = "";
565 shamalgs::collective::gather_str(loc_graph, dot_graph);
566
567 dot_graph = "strict digraph {\n" + dot_graph + "}";
568
569 if (shamcomm::world_rank() == 0) {
570 std::string fname = sham::format("ghost_graph_{}.dot", cnt_dump_debug);
571 logger::info_ln("SPH Ghost", "writing", fname);
572 shambase::write_string_to_file(fname, dot_graph);
573 cnt_dump_debug++;
574 }
575}
576
Solvergraph node selecting the ids of the particles sent through each ghost interface.
shambase::DistributedData< PatchDataFieldRef< T > > DDPatchDataFieldRef
Alias for a DistributedData of PatchDataFieldRefs.
std::uint32_t u32
32 bit unsigned integer
std::uint64_t u64
64 bit unsigned integer
std::int32_t i32
32 bit integer
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.
Class Timer measures the time elapsed since the timer was started.
Definition Timer.hpp:36
void start()
Starts the timer.
Definition Timer.hpp:51
Vector class based on std::array storage and mdspan.
Definition matrix.hpp:98
shambase::DistributedDataShared< InterfaceIdTable > gen_id_table_interfaces(GeneratorMap &&gen)
precompute interfaces members and cache result in the return
GeneratorMap find_interfaces(SerialPatchTree< vec > &sptree, shamrock::patch::PatchtreeField< flt > &int_range_max_tree, shamrock::patch::PatchField< flt > &int_range_max)
Find interfaces and their metadata.
PatchDataLayer container class, the layout is described in patchdata_layout.
u32 get_obj_cnt() const
get the number of objects (particles) stored in this layer
Store the information related to the size of the simulation box to convert patch integer coordinates ...
Definition SimBox.hpp:36
T get_bounding_box_size() const
Get the size of the stored bounding box of the domain.
Definition SimBox.hpp:88
PatchCoordTransform< T > get_patch_transform() const
Get a PatchCoordTransform object that describes the conversion between patch coordinates and domain c...
Definition SimBox.hpp:286
void evaluate()
Evaluate the node.
Definition INode.hpp:156
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:91
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:112
i32 world_rank()
Gives the rank of the current process in the MPI communicator.
Definition worldInfo.cpp:41
namespace for math utility
Definition AABB.hpp:26
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:132
shambase::details::BasicStackEntry StackEntry
Alias for shambase::details::BasicStackEntry.
Boundary conditions configuration.
Definition BCConfig.hpp:40
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
u64 id_patch
unique key that identify the patch
Definition Patch.hpp:86
Functions related to the MPI communicator.