Shamrock 2025.10.0
Astrophysical Code
Loading...
Searching...
No Matches
Model.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
19
22#include "shambase/string.hpp"
26#include "shambackends/vec.hpp"
27#include "shamcomm/logs.hpp"
43#include <pybind11/functional.h>
44#include <stdexcept>
45#include <vector>
46
47namespace shammodels::sph {
48
55 template<class Tvec, template<class> class SPHKernel>
56 class Model {
57 public:
58 using Tscal = shambase::VecComponent<Tvec>;
59 static constexpr u32 dim = shambase::VectorProperties<Tvec>::dimension;
60 using Kernel = SPHKernel<Tscal>;
61
62 using Solver = Solver<Tvec, SPHKernel>;
63 using SolverConfig = typename Solver::Config;
64 // using SolverConfig = typename Solver::Config;
65
66 ShamrockCtx &ctx;
67
68 Solver solver;
69
70 // SolverConfig sconfig;
71
72 Model(ShamrockCtx &ctx) : ctx(ctx), solver(ctx) {};
73
77
79 void init();
80
83 inline void init_scheduler(u32 crit_split, u32 crit_merge) {
84 solver.solver_config.scheduler_conf.split_load_value = crit_split;
85 solver.solver_config.scheduler_conf.merge_load_value = crit_merge;
86 init();
87 }
88
89 template<std::enable_if_t<dim == 3, int> = 0>
90 inline Tvec get_box_dim_fcc_3d(Tscal dr, u32 xcnt, u32 ycnt, u32 zcnt) {
91 return generic::setup::generators::get_box_dim(dr, xcnt, ycnt, zcnt);
92 }
93
94 inline void set_cfl_cour(Tscal cfl_cour) {
95 solver.solver_config.cfl_config.cfl_cour = cfl_cour;
96 }
97 inline void set_cfl_force(Tscal cfl_force) {
98 solver.solver_config.cfl_config.cfl_force = cfl_force;
99 }
100 inline void set_eta_sink(Tscal eta_sink) {
101 solver.solver_config.cfl_config.eta_sink = eta_sink;
102 }
103
104 inline Tscal get_time() { return solver.get_time(); }
105 inline void set_time(Tscal t) { solver.set_time(t); }
106 inline Tscal get_dt_sph() { return solver.get_dt_sph(); }
107 inline void set_next_dt(Tscal dt) { solver.set_next_dt(dt); }
108 inline Tscal get_cfl_multipler() { return solver.get_cfl_multipler(); }
109 inline void set_cfl_multipler(Tscal lambda) { solver.set_cfl_multipler(lambda); }
110
111 inline void set_particle_mass(Tscal gpart_mass) {
112 solver.solver_config.gpart_mass = gpart_mass;
113 }
114
115 inline Tscal get_particle_mass() { return solver.solver_config.gpart_mass; }
116
117 inline void resize_simulation_box(std::pair<Tvec, Tvec> box) {
118 ctx.set_coord_domain_bound({box.first, box.second});
119 }
120
121 SolverConfig gen_config_from_phantom_dump(PhantomDump &phdump, bool bypass_error);
122 void init_from_phantom_dump(PhantomDump &phdump, Tscal hpart_fact_load = 1.0);
123 PhantomDump make_phantom_dump();
124
125 void do_vtk_dump(std::string filename, bool add_patch_world_id) {
126 solver.vtk_do_dump(filename, add_patch_world_id);
127 }
128
129 void set_debug_dump(bool _do_debug_dump, std::string _debug_dump_filename) {
130 solver.set_debug_dump(_do_debug_dump, _debug_dump_filename);
131 }
132
133 u64 get_total_part_count();
134
135 f64 total_mass_to_part_mass(f64 totmass);
136
137 Tscal get_hfact() { return Kernel::hfactd; }
138
139 Tscal rho_h(Tscal h) {
140 return shamrock::sph::rho_h(solver.solver_config.gpart_mass, h, Kernel::hfactd);
141 }
142
143 void add_cube_fcc_3d(Tscal dr, std::pair<Tvec, Tvec> _box);
144 void add_cube_hcp_3d(Tscal dr, std::pair<Tvec, Tvec> _box);
145 void add_cube_hcp_3d_v2(Tscal dr, std::pair<Tvec, Tvec> _box);
146
147 inline std::unique_ptr<modules::SPHSetup<Tvec, SPHKernel>> get_setup() {
148 return std::make_unique<modules::SPHSetup<Tvec, SPHKernel>>(
149 ctx, solver.solver_config, solver.storage);
150 }
151
152 // std::function<Tscal(Tscal)> sigma_profile = [=](Tscal r, Tscal r_in, Tscal p){
153 // // we setup with an adimensional mass since it is monte carlo
154 // constexpr Tscal sigma_0 = 1;
155 // return sigma_0*sycl::pow(r/r_in, -p);
156 // };
157 //
158 // std::function<Tscal(Tscal)> cs_law = [=](Tscal r, Tscal r_in, Tscal q){
159 // return sycl::pow(r/r_in, -q);
160 // };
161 //
162 // std::function<Tscal(Tscal)> rot_profile = [=](Tscal r, Tscal central_mass){
163 // Tscal G = solver.solver_config.get_constant_G();
164 // return sycl::sqrt(G * central_mass/r);
165 // };
166 //
167 // std::function<Tscal(Tscal)> cs_profile = [&](Tscal r, Tscal r_in, Tscal H_r_in){
168 // Tscal cs_in = H_r_in*rot_profile(r_in);
169 // return cs_law(r)*cs_in;
170
171 void add_big_disc_3d(
172 Tvec center,
173 Tscal central_mass,
174 u32 Npart,
175 Tscal r_in,
176 Tscal r_out,
177 Tscal disc_mass,
178 Tscal p,
179 Tscal H_r_in,
180 Tscal q,
181 std::mt19937 eng);
182
183 inline void add_sink(Tscal mass, Tvec pos, Tvec velocity, Tscal accretion_radius) {
184 if (!ctx.is_scheduler_initialized()) {
186 "add_sink() requires that the scheduler has been initialized. "
187 "Call init_scheduler(...) before add_sink().");
188 }
189 PatchScheduler &sched = shambase::get_check_ref(ctx.sched);
190 if (!sched.synchronized_data.has_edge("sink_pos")) {
192 "add_sink() requires that sink edges are registered. "
193 "Call init_scheduler(...) before add_sink().");
194 }
195 auto edges = get_sink_edges<Tvec>(sched.synchronized_data);
196
197 shamlog_debug_ln("SPH", "add sink :", mass, pos, velocity, accretion_radius);
198
199 shammodels::sph::add_sink(edges, mass, pos, velocity, accretion_radius);
200 }
201
202 template<class T>
203 inline void set_field_value_lambda(
204 std::string field_name, const std::function<T(Tvec)> pos_to_val, const u32 offset) {
205
206 StackEntry stack_loc{};
207
208 PatchScheduler &sched = shambase::get_check_ref(ctx.sched);
209
210 u32 ixyz = sched.pdl_old().get_field_idx<Tvec>("xyz");
211 u32 ifield = sched.pdl_old().get_field_idx<T>(field_name);
212
213 sched.patch_data.for_each_patchdata(
214 [&](u64 patch_id, shamrock::patch::PatchDataLayer &pdat) {
215 PatchDataField<Tvec> &xyz = pdat.template get_field<Tvec>(ixyz);
216 PatchDataField<T> &f = pdat.template get_field<T>(ifield);
217
218 auto f_nvar = f.get_nvar();
219 if (offset >= f_nvar) {
221 "offset ({}) is out of bounds for field '{}' with nvar {}",
222 offset,
223 field_name,
224 f_nvar));
225 }
226
227 auto acc = f.get_buf().copy_to_stdvec();
228 auto acc_xyz = xyz.get_buf().copy_to_stdvec();
229
230 u32 obj_cnt = pdat.get_obj_cnt();
231 for (u32 i = 0; i < obj_cnt; i++) {
232 acc[i * f_nvar + offset] = pos_to_val(acc_xyz[i]);
233 }
234
235 f.get_buf().copy_from_stdvec(acc);
236 });
237 }
238
239 template<class T>
240 inline void overwrite_field_value(
241 std::string field_name,
242 const std::function<std::vector<T>(py::dict)> field_compute,
243 const u32 offset) {
244
245 StackEntry stack_loc{};
246
247 PatchScheduler &sched = shambase::get_check_ref(ctx.sched);
248
249 u32 ifield = sched.pdl_old().get_field_idx<T>(field_name);
250
251 sched.patch_data.for_each_patchdata(
252 [&](u64 patch_id, shamrock::patch::PatchDataLayer &pdat) {
253 PatchDataField<T> &f = pdat.template get_field<T>(ifield);
254
255 auto f_nvar = f.get_nvar();
256 if (offset >= f_nvar) {
258 "offset ({}) is out of bounds for field '{}' with nvar {}",
259 offset,
260 field_name,
261 f_nvar));
262 }
263
264 auto result = field_compute(shamrock::pdat_to_dic(pdat));
265
266 if (result.size() != f.get_obj_cnt()) {
268 "result.size() != f.get_obj_cnt() ({} != {})",
269 result.size(),
270 f.get_obj_cnt()));
271 }
272
273 auto acc = f.get_buf().copy_to_stdvec();
274
275 u32 obj_cnt = pdat.get_obj_cnt();
276 for (u32 i = 0; i < obj_cnt; i++) {
277 acc[i * f_nvar + offset] = result[i];
278 }
279
280 f.get_buf().copy_from_stdvec(acc);
281 });
282 }
283
298 template<std::enable_if_t<dim == 3, int> = 0>
299 inline Tscal add_disc_3d(
300 Tvec center,
301 Tscal central_mass,
302 u32 Npart,
303 Tscal r_in,
304 Tscal r_out,
305 Tscal disc_mass,
306 Tscal p,
307 Tscal H_r_in,
308 Tscal q) {
309
310 Tscal G = solver.solver_config.get_constant_G();
311
312 Tscal eos_gamma;
313 using Config = SolverConfig;
314 using SolverConfigEOS = typename Config::EOSConfig;
315 using SolverEOS_Adiabatic = typename SolverConfigEOS::Adiabatic;
316 if (SolverEOS_Adiabatic *eos_config
317 = std::get_if<SolverEOS_Adiabatic>(&solver.solver_config.eos_config.config)) {
318
319 eos_gamma = eos_config->gamma;
320
321 } else {
322 // dirty hack for disc setup in locally isothermal
323 eos_gamma = 2;
324 // shambase::throw_unimplemented();
325 }
326
328
329 auto sigma_profile = [=](Tscal r) {
330 // we setup with an adimensional mass since it is monte carlo
331 constexpr Tscal sigma_0 = 1;
332 return sigma_0 * sycl::pow(r / r_in, -p);
333 };
334
335 auto cs_law = [=](Tscal r) {
336 return sycl::pow(r / r_in, -q);
337 };
338
339 auto rot_profile = [=](Tscal r) {
340 return sycl::sqrt(G * central_mass / r);
341 };
342
343 Tscal cs_in = H_r_in * rot_profile(r_in);
344 auto cs_profile = [&](Tscal r) {
345 return cs_law(r) * cs_in;
346 };
347
348 std::vector<Out> part_list;
349
351 Npart,
352 r_in,
353 r_out,
354 [&](Tscal r) {
355 return sigma_profile(r);
356 },
357 [&](Tscal r) {
358 return cs_profile(r);
359 },
360 [&](Tscal r) {
361 return rot_profile(r);
362 },
363 [&](Out out) {
364 part_list.push_back(out);
365 });
366
367 Tscal part_mass = disc_mass / Npart;
368
369 using namespace shamrock::patch;
370
371 PatchScheduler &sched = shambase::get_check_ref(ctx.sched);
372
373 std::string log = "";
374
375 sched.for_each_local_patchdata([&](const Patch &ptch, PatchDataLayer &pdat) {
376 PatchCoordTransform<Tvec> ptransf = sched.get_sim_box().get_patch_transform<Tvec>();
377
378 shammath::CoordRange<Tvec> patch_coord = ptransf.to_obj_coord(ptch);
379
380 std::vector<Tvec> vec_pos;
381 std::vector<Tvec> vec_vel;
382 std::vector<Tscal> vec_u;
383 std::vector<Tscal> vec_h;
384
385 std::vector<Tscal> vec_cs;
386
387 Tscal G = solver.solver_config.get_constant_G();
388
389 for (Out o : part_list) {
390 vec_pos.push_back(o.pos + center);
391 vec_vel.push_back(o.velocity);
392
393 // for disc with P = \rho u (/gamma - 1)
394 // the scaleheight : H = \sqrt{u (\gamma -1)}/\Omega_K
395 // therefore the effective soundspeed is : \sqrt{(\gamma -1)u}
396 // whereas the real one is \sqrt{(\gamma -1)\gamma u}
397 vec_u.push_back(o.cs * o.cs / (/*solver.eos_gamma * */ (eos_gamma - 1)));
398 vec_h.push_back(shamrock::sph::h_rho(part_mass, o.rho, Kernel::hfactd));
399 vec_cs.push_back(o.cs);
400 }
401
402 log += shambase::format(
403 "\n patch id={}, add N={} particles", ptch.id_patch, vec_pos.size());
404
405 PatchDataLayer tmp(sched.get_layout_ptr_old());
406 tmp.resize(vec_pos.size());
407 tmp.fields_raz();
408
409 {
410 u32 len = vec_pos.size();
412 = tmp.get_field<Tvec>(sched.pdl_old().get_field_idx<Tvec>("xyz"));
413 sycl::buffer<Tvec> buf(vec_pos.data(), len);
414 f.override(buf, len);
415 }
416
417 {
418 u32 len = vec_pos.size();
420 = tmp.get_field<Tscal>(sched.pdl_old().get_field_idx<Tscal>("hpart"));
421 sycl::buffer<Tscal> buf(vec_h.data(), len);
422 f.override(buf, len);
423 }
424
425 {
426 u32 len = vec_pos.size();
428 = tmp.get_field<Tscal>(sched.pdl_old().get_field_idx<Tscal>("uint"));
429 sycl::buffer<Tscal> buf(vec_u.data(), len);
430 f.override(buf, len);
431 }
432
433 if (solver.solver_config.is_eos_locally_isothermal()) {
434 u32 len = vec_pos.size();
436 = tmp.get_field<Tscal>(sched.pdl_old().get_field_idx<Tscal>("soundspeed"));
437 sycl::buffer<Tscal> buf(vec_cs.data(), len);
438 f.override(buf, len);
439 }
440
441 {
442 u32 len = vec_pos.size();
444 = tmp.get_field<Tvec>(sched.pdl_old().get_field_idx<Tvec>("vxyz"));
445 sycl::buffer<Tvec> buf(vec_vel.data(), len);
446 f.override(buf, len);
447 }
448
449 pdat.insert_elements(tmp);
450 });
451
452 std::string log_gathered = "";
453 shamalgs::collective::gather_str(log, log_gathered);
454
455 if (shamcomm::world_rank() == 0) {
456 logger::info_ln("Model", "Push particles : ", log_gathered);
457 }
458
460 ctx, solver.solver_config, solver.storage)
461 .update_load_balancing();
462
463 sched.scheduler_step(false, false);
464
465 {
466 auto [m, M] = sched.get_box_tranform<Tvec>();
467
469 sched.patch_tree, sched.get_sim_box().get_patch_transform<Tvec>());
470
471 // sptree.print_status();
472
474
475 sptree.attach_buf();
476 // reatribute_particles(sched, sptree, periodic_mode);
477
478 reatrib.reatribute_patch_objects(sptree, "xyz");
479 }
480
481 sched.check_patchdata_locality_correctness();
482
483 sched.scheduler_step(true, true);
484
485 log = "";
486 sched.for_each_local_patchdata([&](const Patch &p, PatchDataLayer &pdat) {
487 log += shambase::format(
488 "\n patch id={}, N={} particles", p.id_patch, pdat.get_obj_cnt());
489 });
490
491 log_gathered = "";
492 shamalgs::collective::gather_str(log, log_gathered);
493
494 if (shamcomm::world_rank() == 0)
495 logger::info_ln("Model", "current particle counts : ", log_gathered);
496 return part_mass;
497 }
498
499 template<std::enable_if_t<dim == 3, int> = 0>
500 inline void add_cube_disc_3d(
501 Tvec center,
502 u32 Npart,
503 Tscal p,
504 Tscal rho_0,
505 Tscal m,
506 Tscal r_in,
507 Tscal r_out,
508 Tscal q,
509 Tscal cmass) {
510
511 Tscal eos_gamma;
512 using Config = SolverConfig;
513 using SolverConfigEOS = typename Config::EOSConfig;
514 using SolverEOS_Adiabatic = typename SolverConfigEOS::Adiabatic;
515 if (SolverEOS_Adiabatic *eos_config
516 = std::get_if<SolverEOS_Adiabatic>(&solver.solver_config.eos_config.config)) {
517
518 eos_gamma = eos_config->gamma;
519
520 } else {
522 }
523
524 auto cs = [&](Tscal u) {
525 return sycl::sqrt(eos_gamma * (eos_gamma - 1) * u);
526 };
527
528 auto U = [&](Tscal cs) {
529 return cs * cs / (eos_gamma * (eos_gamma - 1));
530 };
531
532 using namespace shamrock::patch;
533
534 PatchScheduler &sched = shambase::get_check_ref(ctx.sched);
535
536 std::string log = "";
537
538 sched.for_each_local_patchdata([&](const Patch &ptch, PatchDataLayer &pdat) {
539 PatchCoordTransform<Tvec> ptransf = sched.get_sim_box().get_patch_transform<Tvec>();
540
541 shammath::CoordRange<Tvec> patch_coord = ptransf.to_obj_coord(ptch);
542
543 std::vector<Tvec> vec_acc;
544 std::vector<Tvec> vec_vel;
545 std::vector<Tscal> vec_u;
546
547 Tscal G = solver.solver_config.get_constant_G();
548
550 Npart, p, rho_0, m, r_in, r_out, q, [&](Tvec r, Tscal h) {
551 vec_acc.push_back(r + center);
552
553 Tscal R = sycl::length(r);
554
555 Tscal V = sycl::sqrt(G * cmass / R);
556
557 Tvec etheta = {-r.z(), 0, r.x()};
558 etheta /= sycl::length(etheta);
559
560 vec_vel.push_back(V * etheta);
561
562 Tscal cs0 = 1;
563 Tscal cs = cs0 * sycl::pow(R, -q);
564
565 vec_u.push_back(U(cs));
566 });
567
568 log += shambase::format(
569 "\n patch id={}, add N={} particles", ptch.id_patch, vec_acc.size());
570
571 PatchDataLayer tmp(sched.get_layout_ptr_old());
572 tmp.resize(vec_acc.size());
573 tmp.fields_raz();
574
575 {
576 u32 len = vec_acc.size();
577 PatchDataField<Tvec> &f
578 = tmp.get_field<Tvec>(sched.pdl_old().get_field_idx<Tvec>("xyz"));
579 sycl::buffer<Tvec> buf(vec_acc.data(), len);
580 f.override(buf, len);
581 }
582
583 {
584 PatchDataField<Tscal> &f
585 = tmp.get_field<Tscal>(sched.pdl_old().get_field_idx<Tscal>("hpart"));
586 f.override(0.01);
587 }
588
589 {
590 u32 len = vec_acc.size();
591 PatchDataField<Tscal> &f
592 = tmp.get_field<Tscal>(sched.pdl_old().get_field_idx<Tscal>("uint"));
593 sycl::buffer<Tscal> buf(vec_u.data(), len);
594 f.override(buf, len);
595 }
596
597 {
598 u32 len = vec_acc.size();
599 PatchDataField<Tvec> &f
600 = tmp.get_field<Tvec>(sched.pdl_old().get_field_idx<Tvec>("vxyz"));
601 sycl::buffer<Tvec> buf(vec_vel.data(), len);
602 f.override(buf, len);
603 }
604
605 pdat.insert_elements(tmp);
606 });
607
608 std::string log_gathered = "";
609 shamalgs::collective::gather_str(log, log_gathered);
610
611 if (shamcomm::world_rank() == 0) {
612 logger::info_ln("Model", "Push particles : ", log_gathered);
613 }
614
615 modules::ComputeLoadBalanceValue<Tvec, SPHKernel>(
616 ctx, solver.solver_config, solver.storage)
617 .update_load_balancing();
618
619 sched.scheduler_step(false, false);
620
621 {
622 auto [m, M] = sched.get_box_tranform<Tvec>();
623
624 SerialPatchTree<Tvec> sptree(
625 sched.patch_tree, sched.get_sim_box().get_patch_transform<Tvec>());
626
627 // sptree.print_status();
628
629 shamrock::ReattributeDataUtility reatrib(sched);
630
631 sptree.attach_buf();
632 // reatribute_particles(sched, sptree, periodic_mode);
633
634 reatrib.reatribute_patch_objects(sptree, "xyz");
635 }
636
637 sched.check_patchdata_locality_correctness();
638
639 sched.scheduler_step(true, true);
640
641 log = "";
642 sched.for_each_local_patchdata([&](const Patch &p, PatchDataLayer &pdat) {
643 log += shambase::format(
644 "\n patch id={}, N={} particles", p.id_patch, pdat.get_obj_cnt());
645 });
646
647 log_gathered = "";
648 shamalgs::collective::gather_str(log, log_gathered);
649
650 if (shamcomm::world_rank() == 0)
651 logger::info_ln("Model", "current particle counts : ", log_gathered);
652 }
653
654 void remap_positions(std::function<Tvec(Tvec)> map);
655
656 void push_particle(
657 std::vector<Tvec> &part_pos_insert,
658 std::vector<Tscal> &part_hpart_insert,
659 std::vector<Tscal> &part_u_insert);
660
661 void push_particle_mhd(
662 std::vector<Tvec> &part_pos_insert,
663 std::vector<Tscal> &part_hpart_insert,
664 std::vector<Tscal> &part_u_insert,
665 std::vector<Tvec> &part_B_on_rho_insert,
666 std::vector<Tscal> &part_psi_on_ch_insert);
667
668 template<class T>
669 inline void set_value_in_a_box(
670 std::string field_name, T val, std::pair<Tvec, Tvec> box, u32 ivar) {
671 StackEntry stack_loc{};
672 PatchScheduler &sched = shambase::get_check_ref(ctx.sched);
673 sched.patch_data.for_each_patchdata(
674 [&](u64 patch_id, shamrock::patch::PatchDataLayer &pdat) {
675 PatchDataField<Tvec> &xyz
676 = pdat.template get_field<Tvec>(sched.pdl_old().get_field_idx<Tvec>("xyz"));
677
678 PatchDataField<T> &f
679 = pdat.template get_field<T>(sched.pdl_old().get_field_idx<T>(field_name));
680
681 if (ivar >= f.get_nvar()) {
683 "You are trying to set value in a box for field ({}) with "
684 "ivar ({}) >= f.get_nvar ({})",
685 field_name,
686 ivar,
687 f.get_nvar()));
688 }
689
690 u32 nvar = f.get_nvar();
691
692 {
693 auto acc = f.get_buf().template mirror_to<sham::host>();
694 auto acc_xyz = xyz.get_buf().template mirror_to<sham::host>();
695
696 for (u32 i = 0; i < f.get_obj_cnt(); i++) {
697 Tvec r = acc_xyz[i];
698
699 if (BBAA::is_coord_in_range(r, std::get<0>(box), std::get<1>(box))) {
700 acc[i * nvar + ivar] = val;
701 }
702 }
703 }
704 });
705 }
706
707 template<class T>
708 inline void set_value_in_sphere(std::string field_name, T val, Tvec center, Tscal radius) {
709 StackEntry stack_loc{};
710 PatchScheduler &sched = shambase::get_check_ref(ctx.sched);
711 sched.patch_data.for_each_patchdata(
712 [&](u64 patch_id, shamrock::patch::PatchDataLayer &pdat) {
713 PatchDataField<Tvec> &xyz
714 = pdat.template get_field<Tvec>(sched.pdl_old().get_field_idx<Tvec>("xyz"));
715
716 PatchDataField<T> &f
717 = pdat.template get_field<T>(sched.pdl_old().get_field_idx<T>(field_name));
718
719 if (f.get_nvar() != 1) {
721 }
722
723 Tscal r2 = radius * radius;
724 {
725 auto acc = f.get_buf().template mirror_to<sham::host>();
726 auto acc_xyz = xyz.get_buf().template mirror_to<sham::host>();
727
728 for (u32 i = 0; i < f.get_obj_cnt(); i++) {
729 Tvec dr = acc_xyz[i] - center;
730
731 if (sycl::dot(dr, dr) < r2) {
732 acc[i] = val;
733 }
734 }
735 }
736 });
737 }
738
739 template<class T>
740 inline void add_kernel_value(std::string field_name, T val, Tvec center, Tscal h_ker) {
741 StackEntry stack_loc{};
742 PatchScheduler &sched = shambase::get_check_ref(ctx.sched);
743 sched.patch_data.for_each_patchdata(
744 [&](u64 patch_id, shamrock::patch::PatchDataLayer &pdat) {
745 PatchDataField<Tvec> &xyz
746 = pdat.template get_field<Tvec>(sched.pdl_old().get_field_idx<Tvec>("xyz"));
747
748 PatchDataField<T> &f
749 = pdat.template get_field<T>(sched.pdl_old().get_field_idx<T>(field_name));
750
751 if (f.get_nvar() != 1) {
753 }
754
755 {
756 auto acc = f.get_buf().template mirror_to<sham::host>();
757 auto acc_xyz = xyz.get_buf().template mirror_to<sham::host>();
758
759 for (u32 i = 0; i < f.get_obj_cnt(); i++) {
760 Tvec dr = acc_xyz[i] - center;
761
762 Tscal r = sycl::length(dr);
763
764 acc[i] += val * Kernel::W_3d(r, h_ker);
765 }
766 }
767 });
768 }
769
770 template<class T>
771 inline T get_sum(std::string name) {
772 PatchScheduler &sched = shambase::get_check_ref(ctx.sched);
773 T sum = shambase::VectorProperties<T>::get_zero();
774
775 StackEntry stack_loc{};
776 sched.patch_data.for_each_patchdata(
777 [&](u64 patch_id, shamrock::patch::PatchDataLayer &pdat) {
778 PatchDataField<T> &xyz
779 = pdat.template get_field<T>(sched.pdl_old().get_field_idx<T>(name));
780
781 sum += xyz.compute_sum();
782 });
783
784 return shamalgs::collective::allreduce_sum(sum);
785 }
786
787 Tvec get_closest_part_to(Tvec pos);
788
789 inline void apply_momentum_offset(Tvec offset) {
790
791 PatchScheduler &sched = shambase::get_check_ref(ctx.sched);
792
793 u32 ivxyz = sched.pdl_old().get_field_idx<Tvec>("vxyz");
794
795 // compute the total mass
796 Tscal tot_mass = 0;
797
798 sched.for_each_patchdata_nonempty(
799 [&](shamrock::patch::Patch p, shamrock::patch::PatchDataLayer &pdat) {
800 tot_mass += solver.solver_config.gpart_mass * pdat.get_obj_cnt();
801 });
802
803 tot_mass = shamalgs::collective::allreduce_sum(tot_mass);
804
805 // add the mass of the sinks
806 auto &sync = sched.synchronized_data;
807 auto &mass = get_sink_mass<Tvec>(sync);
808 if (!mass.empty()) {
809 for (size_t i = 0; i < mass.size(); i++) {
810 tot_mass += mass[i];
811 }
812 }
813
814 // compute the offset velocity
815 Tvec offset_vel = (tot_mass > 0) ? (offset / tot_mass)
816 : shambase::VectorProperties<Tvec>::get_zero();
817
818 // apply the offset velocity to the sinks
819 auto &vel = get_sink_vel<Tvec>(sync);
820 if (!vel.empty()) {
821 for (size_t i = 0; i < vel.size(); i++) {
822 vel[i] += offset_vel;
823 }
824 }
825
826 // apply the offset velocity to the particles
827 sched.for_each_patchdata_nonempty(
828 [&](shamrock::patch::Patch p, shamrock::patch::PatchDataLayer &pdat) {
829 PatchDataField<Tvec> &vxyz = pdat.get_field<Tvec>(ivxyz);
830 vxyz.apply_offset(offset_vel);
831 });
832 }
833
834 inline void apply_position_offset(Tvec offset) {
835
836 PatchScheduler &sched = shambase::get_check_ref(ctx.sched);
837
838 u32 ixyz = sched.pdl_old().get_field_idx<Tvec>("xyz");
839
840 // apply the position offset to the sinks
841 auto &pos = get_sink_pos<Tvec>(sched.synchronized_data);
842 if (!pos.empty()) {
843 for (size_t i = 0; i < pos.size(); i++) {
844 pos[i] += offset;
845 }
846 }
847
848 // apply the position offset to the particles
849 sched.for_each_patchdata_nonempty(
850 [&](shamrock::patch::Patch p, shamrock::patch::PatchDataLayer &pdat) {
851 PatchDataField<Tvec> &xyz = pdat.get_field<Tvec>(ixyz);
852 xyz.apply_offset(offset);
853 });
854 }
855
856 // inline void enable_barotropic_mode(){
857 // sconfig.enable_barotropic();
858 // }
859 //
860 // inline void switch_internal_energy_mode(std::string name){
861 // sconfig.switch_internal_energy_mode(name);
862 // }
863
864 inline void set_solver_config(typename Solver::Config cfg) {
865 if (ctx.is_scheduler_initialized()) {
867 "Cannot change solver config after scheduler is initialized");
868 }
869 cfg.check_config();
870 solver.solver_config = cfg;
871 }
872
873 inline f64 solver_logs_last_rate() { return solver.solve_logs.get_last_rate(); }
874 inline u64 solver_logs_last_obj_count() { return solver.solve_logs.get_last_obj_count(); }
875 inline f64 solver_logs_cumulated_step_time() {
876 return solver.solve_logs.get_cumulated_step_time();
877 }
878 inline void solver_logs_reset_cumulated_step_time() {
879 solver.solve_logs.reset_cumulated_step_time();
880 }
881 inline u64 solver_logs_step_count() { return solver.solve_logs.get_step_count(); }
882 inline void solver_logs_reset_step_count() { solver.solve_logs.reset_step_count(); }
883
884 inline void change_htolerances(Tscal in_coarse, Tscal in_fine) {
885 if (in_coarse < in_fine) {
887 "in_coarse ({}) must be greater than in_fine ({})", in_coarse, in_fine));
888 }
889 solver.solver_config.htol_up_coarse_cycle = in_coarse;
890 solver.solver_config.htol_up_fine_cycle = in_fine;
891 }
892
896
900
906 inline void load_from_dump(std::string fname) {
907 if (shamcomm::world_rank() == 0) {
908 logger::info_ln("SPH", "Loading state from dump", fname);
909 }
910
911 // Load the context state and recover user metadata
912 std::string metadata_user{};
913 shamrock::load_shamrock_dump(fname, metadata_user, ctx);
914
916 nlohmann::json j = nlohmann::json::parse(metadata_user);
917 // std::cout << j << std::endl;
918 j.at("solver_config").get_to(solver.solver_config);
919
920 PatchScheduler &sched = shambase::get_check_ref(ctx.sched);
921
923 if (j.contains("sinks") && !j.at("sinks").is_null()) {
924 std::vector<SinkParticle<Tvec>> out;
925 j.at("sinks").get_to(out);
926 auto edges = get_sink_edges<Tvec>(sched.synchronized_data);
927 set_sink_particles(edges, out);
928 }
929
930 // Migrate old dumps that stored time/dt/cfl in solver_config.time_state
931 auto sync_names = sched.synchronized_data.get_edge_names();
932
933 // PR #1928 introduces time/dt/cfl synchronization edges
934 // so checking for time is equivalent to commit >= PR #1928
935 bool had_time_edge
936 = std::find(sync_names.begin(), sync_names.end(), "time") != sync_names.end();
937
938 // create time/dt/cfl synchronization edges if not present
939 solver.ensure_time_state_edges();
940
941 if (!had_time_edge) { // before PR #1928
942 if (j.at("solver_config").contains("time_state")) {
943 ON_RANK_0(
945 "SPH",
946 "Migrated time/dt/cfl from solver_config.time_state into scheduler "
947 "edges"));
948 const auto &ts = j.at("solver_config").at("time_state");
949 solver.set_time(ts.at("time").get<Tscal>());
950 solver.set_next_dt(ts.at("dt_sph").get<Tscal>());
951 solver.set_cfl_multipler(ts.at("cfl_multiplier").get<Tscal>());
952 } else {
954 "this should never happen: dump has neither time edges nor "
955 "solver_config.time_state");
956 }
957 }
958
959 solver.init_ghost_layout();
960
961 solver.init_solver_graph();
962
963 shamlog_debug_ln("Sys", "build local scheduler tables");
964 sched.owned_patch_id = sched.patch_list.build_local();
967 sched.update_local_load_value([&](shamrock::patch::Patch p) {
968 return sched.patch_data.owned_data.get(p.id_patch).get_obj_cnt();
969 });
970 }
971
977 inline void dump(std::string fname) {
978 if (shamcomm::world_rank() == 0) {
979 logger::info_ln("SPH", "Dumping state to", fname);
980 }
981
982 solver.update_sync_load_values();
983
984 nlohmann::json metadata;
985 metadata["solver_config"] = solver.solver_config;
986
987 // Dump the state of the SPH model to a file
990 fname, metadata.dump(4), shambase::get_check_ref(ctx.sched));
991 }
992
996
997 f64 evolve_once_time_expl(f64 t_curr, f64 dt_input);
998
999 TimestepLog timestep();
1000
1001 inline void evolve_once() {
1002 solver.evolve_once();
1003 solver.print_timestep_logs();
1004 }
1005
1006 inline EvolveUntilResults evolve_until(
1007 Tscal target_time, i32 niter_max, f64 max_walltime = -1) {
1008 return solver.evolve_until(target_time, niter_max, max_walltime);
1009 }
1010
1011 private:
1012 void add_pdat_to_phantom_block(
1013 PhantomDumpBlock &block, shamrock::patch::PatchDataLayer &pdat);
1014
1015 template<class Tscal>
1016 inline void warp_disc(
1017 std::vector<Tvec> &pos,
1018 std::vector<Tvec> &vel,
1019 Tscal posangle,
1020 Tscal incl,
1021 Tscal Rwarp,
1022 Tscal Hwarp) {
1023 Tvec k = Tvec(-std::sin(posangle), std::cos(posangle), 0.);
1024 Tscal inc;
1025 Tscal psi = 0.;
1026 u32 len = pos.size();
1027
1028 // convert to radians (sycl functions take radians)
1029 Tscal incl_rad = incl * shambase::constants::pi<Tscal> / 180.;
1030
1031 for (i32 i = 0; i < len; i++) {
1032 Tvec R_vec = pos[i];
1033 Tscal R = sycl::sqrt(sycl::dot(R_vec, R_vec));
1034 if (R < Rwarp - Hwarp) {
1035 inc = 0.;
1036 } else if (R < Rwarp + 3. * Hwarp && R > Rwarp - Hwarp) {
1037 inc = sycl::asin(
1038 0.5
1039 * (1.
1040 + sycl::sin(shambase::constants::pi<Tscal> / (2. * Hwarp) * (R - Rwarp)))
1041 * sycl::sin(incl_rad));
1042 psi = shambase::constants::pi<Tscal>
1043 * Rwarp / (4. * Hwarp) * sycl::sin(incl_rad)
1044 / sycl::sqrt(1. - (0.5 * sycl::pow(sycl::sin(incl_rad), 2)));
1045 Tscal psimax = sycl::max(psimax, psi);
1046 Tscal x = pos[i].x();
1047 Tscal y = pos[i].y();
1048 Tscal z = pos[i].z();
1049
1050 // Tscal xp = x * sycl::cos(inc) + y * sycl::sin(inc);
1051 // Tscal yp = - x * sycl::sin(inc) + y * sycl::cos(inc);
1052 // pos[i] = Tvec(xp, yp, z);
1053
1054 Tvec kk = Tvec(0., 0., 1.);
1055 Tvec w = sycl::cross(kk, pos[i]);
1056 // Rodrigues' rotation formula
1057 pos[i] = pos[i] * sycl::cos(inc) + w * sycl::sin(inc)
1058 + kk * sycl::dot(kk, pos[i]) * (1. - sycl::cos(inc));
1059
1060 } else {
1061 inc = 0.;
1062 }
1063 }
1064 }
1065
1066 inline void rotate_vector(Tvec &u, Tvec &v, Tscal theta) {
1067 // normalize the reference direction
1068 Tvec vunit = v / sycl::sqrt(sycl::dot(v, v));
1069 Tvec w = sycl::cross(vunit, u);
1070 // Rodrigues' rotation formula
1071 u = u * sycl::cos(theta) + w * sycl::sin(theta)
1072 + vunit * sycl::dot(vunit, u) * (1. - sycl::cos(theta));
1073 }
1074 };
1075
1076} // namespace shammodels::sph
constexpr const char * vxyz
3-velocity field
constexpr const char * xyz
Position field (3D coordinates).
Header file describing a Node Instance.
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.
SchedulerPatchData patch_data
handle the data of the patches of the scheduler
PatchTree patch_tree
handle the tree structure of the patches
void scheduler_step(bool do_split_merge, bool do_load_balancing)
scheduler step
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::unordered_set< u64 > build_local()
select owned patches owned by the node to rebuild local
void build_local_idx_map()
recompute id_patch_to_local_idx
void build_global_idx_map()
recompute id_patch_to_global_idx
void load_from_dump(std::string fname)
Load the state of the SPH model from a dump file.
Definition Model.hpp:906
void init()
Initialise the model and all the related data structures (patch scheduler in particular).
Definition Model.cpp:58
Tscal add_disc_3d(Tvec center, Tscal central_mass, u32 Npart, Tscal r_in, Tscal r_out, Tscal disc_mass, Tscal p, Tscal H_r_in, Tscal q)
Add a disc distribution.
Definition Model.hpp:299
void dump(std::string fname)
Dump the state of the SPH model to a file.
Definition Model.hpp:977
void init_scheduler(u32 crit_split, u32 crit_merge)
Definition Model.hpp:83
Utility class used to move the objects between patches.
void reatribute_patch_objects(SerialPatchTree< T > &sptree, std::string position_field)
Reattribute objects based on a given position field.
u32 get_field_idx(const std::string &field_name) const
Get the field id if matching name & type.
PatchDataLayer container class, the layout is described in patchdata_layout.
PatchCoordTransform< T > get_patch_transform() const
Get a PatchCoordTransform object that describes the conversion between patch coordinates and domain c...
Definition SimBox.hpp:285
shambase::DistributedData< PatchData > owned_data
map container for patchdata owned by the current node (layout : id_patch,data)
std::vector< std::string > get_edge_names() const
Returns edge registration keys in lexicographic order (deterministic).
bool has_edge(const std::string &name) const
Check whether an edge with the given name exists.
Class holding the value of numerous constants generated from the following source.
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 add_disc2(u32 Npart, flt r_in, flt r_out, std::function< flt(flt)> sigma_profile, std::function< flt(flt)> cs_profile, std::function< flt(flt)> rot_profile, std::function< void(DiscOutput< flt >)> pusher)
void add_disc(u32 Npart, flt p, flt rho_0, flt m, flt r_in, flt r_out, flt q, Tpred_pusher &&part_pusher)
void throw_with_loc(std::string message, SourceLocation loc=SourceLocation{})
Throw an exception and append the source location to it.
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.
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:40
namespace for the sph model
std::vector< Tvec > & get_sink_pos(shamrock::solvergraph::SolverGraphSerializable &sync)
Named SoA getters (edges must already exist; call ensure_sink_edges first). Prefer these when a funct...
void ensure_sink_edges(shamrock::solvergraph::SolverGraphSerializable &sync)
Register sink SoA synchronized edges if missing (idempotent).
SinkEdges< Tvec > get_sink_edges(shamrock::solvergraph::SolverGraphSerializable &sync)
Fetch mutable references to the sink SoA synchronized edges.
void add_sink(SinkEdges< Tvec > &e, typename SinkEdges< Tvec >::Tscal mass, Tvec pos, Tvec velocity, typename SinkEdges< Tvec >::Tscal accretion_radius)
Append one sink to the SoA edges.
void set_sink_particles(SinkEdges< Tvec > &e, const std::vector< SinkParticle< Tvec > > &sinks)
Replace SoA sink edge contents from an AoS sink list (legacy dump migration).
void load_shamrock_dump(std::string fname, std::string &metadata_user, ShamrockCtx &ctx)
Load a Shamrock dump file and restore the state of the patches and retreive user metadata.
void write_shamrock_dump(std::string fname, std::string metadata_user, PatchScheduler &sched)
Write a Shamrock dump file containing the current state of the patches and user supplied metadata.
void info_ln(std::string module_name, Types... var2)
Prints a log message with multiple arguments followed by a newline.
Definition logs.hpp:133
void warn_ln(std::string module_name, Types... var2)
Prints a log message with multiple arguments followed by a newline.
Definition logs.hpp:133
Helpers to access SPH sink particles stored as SoA synchronized data edges.
shambase::details::BasicStackEntry StackEntry
Alias for shambase::details::BasicStackEntry.
The configuration for a sph solver.
Patch object that contain generic patch information.
Definition Patch.hpp:33
u64 id_patch
unique key that identify the patch
Definition Patch.hpp:86
#define ON_RANK_0(x)
Macro to execute code only on rank 0.
Definition worldInfo.hpp:73