Tatooine
autonomous_particle.h
Go to the documentation of this file.
1#ifndef TATOOINE_FIELDS_AUTONOMOUS_PARTICLE_H
2#define TATOOINE_FIELDS_AUTONOMOUS_PARTICLE_H
3//==============================================================================
5#include <tatooine/concepts.h>
12#include <tatooine/particle.h>
13#include <tatooine/random.h>
14#include <tatooine/tags.h>
15#include <tatooine/tensor.h>
18//==============================================================================
19namespace tatooine {
20//==============================================================================
21template <typename B> concept split_behavior = requires {
22 requires floating_point<decltype(B::split_cond)>;
23 requires range<decltype(B::radii)>;
24 requires range<decltype(B::offsets)>;
25 requires static_vec<typename decltype(B::radii)::value_type>;
26 requires static_vec<typename decltype(B::offsets)::value_type>;
27};
28//==============================================================================
29template <floating_point Real, std::size_t NumDimensions>
30struct autonomous_particle : geometry::hyper_ellipse<Real, NumDimensions> {
33 static constexpr auto num_dimensions() -> std::size_t {
34 return NumDimensions;
35 }
36 //============================================================================
37 // TYPEDEFS
38 //============================================================================
39public:
40 //----------------------------------------------------------------------------
41 constexpr static auto half = 1 / Real(2);
42
45 using real_type = Real;
49 using container_type = std::vector<this_type>;
50 using simple_particle_container_type = std::vector<simple_particle_type>;
58 using parent_type::S;
59 //============================================================================
60 // static members
61 //============================================================================
62 static constexpr auto default_max_split_depth = 6;
63 //============================================================================
64 // members
65 //============================================================================
66private:
70 std::uint8_t m_split_depth = 0;
72 std::uint64_t m_id = std::numeric_limits<std::uint64_t>::max();
73
74 static auto mutex() -> auto & {
75 static auto m = std::mutex{};
76 return m;
77 }
78 //============================================================================
79 // CTORS
80 //============================================================================
81public:
82 //----------------------------------------------------------------------------
85 autonomous_particle(autonomous_particle &&other) noexcept = default;
86 //----------------------------------------------------------------------------
87 auto operator=(autonomous_particle const &other)
88 -> autonomous_particle & = default;
89 auto operator=(autonomous_particle &&other) noexcept
90 -> autonomous_particle & = default;
91 //----------------------------------------------------------------------------
93 //----------------------------------------------------------------------------
95 std::uint64_t const id)
96 : parent_type{ell}, m_x0{ell.center()}, m_t{t},
97 m_nabla_phi{mat_type::eye()}, m_id{id} {}
98 //----------------------------------------------------------------------------
100 std::atomic_uint64_t &uuid_generator)
101 : autonomous_particle{ell, t, uuid_generator++} {}
102 //----------------------------------------------------------------------------
104 std::uint8_t max_split_depth, std::uint64_t const id)
105 : parent_type{ell}, m_x0{ell.center()}, m_t{t},
106 m_nabla_phi{mat_type::eye()},
108 //----------------------------------------------------------------------------
110 std::uint8_t max_split_depth,
111 std::atomic_uint64_t &uuid_generator)
112 : autonomous_particle{ell, t, max_split_depth, uuid_generator++} {}
113 //----------------------------------------------------------------------------
115 std::uint64_t const id)
116 : parent_type{x, r}, m_x0{x}, m_t{t},
117 m_nabla_phi{mat_type::eye()}, m_id{id} {}
118 //----------------------------------------------------------------------------
120 std::atomic_uint64_t &uuid_generator)
121 : autonomous_particle{x, t, r, uuid_generator++} {}
122 //----------------------------------------------------------------------------
124 std::uint8_t max_split_depth, std::uint64_t const id)
125 : parent_type{x, r}, m_x0{x}, m_t{t}, m_nabla_phi{mat_type::eye()},
127 //----------------------------------------------------------------------------
129 std::uint8_t max_split_depth,
130 std::atomic_uint64_t &uuid_generator)
131 : autonomous_particle{x, t, r, max_split_depth, uuid_generator++} {}
132 //----------------------------------------------------------------------------
134 pos_type const &x0, mat_type const &nabla_phi,
135 std::uint8_t const split_depth,
136 std::uint8_t const max_split_depth,
137 std::uint64_t const id)
141 //----------------------------------------------------------------------------
143 pos_type const &x0, mat_type const &nabla_phi,
144 std::uint8_t const split_depth,
145 std::uint8_t const max_split_depth,
146 std::atomic_uint64_t &uuid_generator)
148 t,
149 x0,
150 nabla_phi,
153 uuid_generator++} {}
155 //============================================================================
156 // GETTERS / SETTERS
157 //============================================================================
158 auto x0() -> auto & { return m_x0; }
159 auto x0() const -> auto const & { return m_x0; }
160 auto x0(std::size_t i) const { return x0()(i); }
161 //----------------------------------------------------------------------------
162 auto x() -> auto & { return parent_type::center(); }
163 auto x() const -> auto const & { return parent_type::center(); }
164 auto x(std::size_t const i) const { return parent_type::center()(i); }
165 //----------------------------------------------------------------------------
166 auto t() -> auto & { return m_t; }
167 auto t() const { return m_t; }
168 //----------------------------------------------------------------------------
169 auto nabla_phi() const -> auto const & { return m_nabla_phi; }
170 //----------------------------------------------------------------------------
171 auto S0() const {
172 auto sqrS = *inv(nabla_phi()) * S() * S() * *inv(transposed(nabla_phi()));
173 auto [eig_vecs, eig_vals] = eigenvectors_sym(sqrS);
174 for (std::size_t i = 0; i < NumDimensions; ++i) {
175 eig_vals(i) = gcem::sqrt(eig_vals(i));
176 }
177 return eig_vecs * diag(eig_vals) * transposed(eig_vecs);
178 }
179 //----------------------------------------------------------------------------
180 auto initial_ellipse() const { return ellipse_type{x0(), S0()}; }
181 //----------------------------------------------------------------------------
182 auto split_depth() const { return m_split_depth; }
183 auto max_split_depth() const { return m_max_split_depth; }
184 //----------------------------------------------------------------------------
185 auto id() const { return m_id; }
186 //============================================================================
187 // METHODS
188 //============================================================================
189 // template <
190 // split_behavior SplitBehavior = typename split_behaviors::three_splits,
191 // typename Flowmap>
192 // auto advect(Flowmap&& phi, real_type const stepwidth, real_type const
193 // t_end,
194 // filesystem::path const& path) const {
195 // return advect(std::forward<Flowmap>(phi), stepwidth, t_end, {*this},
196 // path);
197 //}
199 // template <
200 // split_behavior SplitBehavior = typename split_behaviors::three_splits,
201 // typename Flowmap>
202 // static auto advect(Flowmap&& phi, real_type const stepwidth, real_type
203 // const t_end,
204 // container_type const& particles,
205 // filesystem::path const& path) {
206 // // auto finished_particles = container_type{};
207 // auto const num_threads =
208 // static_cast<std::size_t>(std::thread::hardware_concurrency());
209 // if (filesystem::exists(path)) {
210 // filesystem::remove(path);
211 // }
212 // auto file = hdf5::file{path};
213 // auto hdd_data =
214 // std::array{file.create_dataset<typename container_type::value_type>(
215 // "ping", hdf5::unlimited),
216 // file.create_dataset<typename container_type::value_type>(
217 // "pong", hdf5::unlimited)};
218 // auto finished = file.create_dataset<typename container_type::value_type>(
219 // "finished", hdf5::unlimited);
220 // std::size_t reader = 0;
221 // std::size_t writer = 1;
222 // hdd_data[reader].write(particles);
223 //
224 // while (hdd_data[reader].dataspace().current_resolution()[0] > 0) {
225 // auto const num_particles =
226 // hdd_data[reader].dataspace().current_resolution()[0];
227 // auto thread_ranges =
228 // std::vector<aligned<std::pair<std::size_t, std::size_t>>>(
229 // num_threads);
230 // auto advected_particles =
231 // std::vector<aligned<container_type>>(num_threads); auto
232 // finished_particles = std::vector<aligned<container_type>>(num_threads);
233 // auto loaded_particles =
234 // std::vector<aligned<container_type>>(num_threads); std::size_t const
235 // num_particles_at_once = 10000000; for (auto& l : loaded_particles) {
236 // l->reserve(num_particles_at_once);
237 // }
238 // {
239 // // distribute particles
240 // auto thread_pool = std::vector<std::thread>{};
241 // thread_pool.reserve(num_threads);
242 // for (std::size_t i = 0; i < num_threads; ++i) {
243 // thread_pool.emplace_back(
244 // [&](auto const thr_id) {
245 // std::size_t const begin = thr_id * num_particles /
246 // num_threads; std::size_t const end =
247 // (thr_id + 1) * num_particles / num_threads;
248 //
249 // *thread_ranges[thr_id] = std::pair{begin, end};
250 // },
251 // i);
252 // }
253 // for (auto& thread : thread_pool) {
254 // thread.join();
255 // }
256 // }
257 // {
258 // // advect particle pools
259 // auto thread_pool = std::vector<std::thread>{};
260 // thread_pool.reserve(num_threads);
261 // for (std::size_t i = 0; i < num_threads; ++i) {
262 // thread_pool.emplace_back(
263 // [&](auto const thr_id) {
264 // auto const& range = *thread_ranges[thr_id];
265 // auto& particles = *loaded_particles[thr_id];
266 // std::size_t chunk_idx = 0;
267 // for (std::size_t i = range.first; i < range.second;
268 // i += num_particles_at_once, ++chunk_idx) {
269 // auto const cur_num_particles =
270 // std::min(num_particles_at_once, range.second - i);
271 // particles.resize(cur_num_particles);
272 // {
273 // auto lock = std::lock_guard{mutex()};
274 // hdd_data[reader].read(i, cur_num_particles, particles);
275 // }
276 // for (auto const& particle : particles) {
277 // particle.template advect_until_split<SplitBehavior>(
278 // std::forward<Flowmap>(phi), stepwidth, t_end,
279 // *advected_particles[thr_id],
280 // *finished_particles[thr_id]);
281 // if (advected_particles[thr_id]->size() > 10000000) {
282 // {
283 // auto lock = std::lock_guard{mutex()};
284 // hdd_data[writer].push_back(*advected_particles[thr_id]);
285 // }
286 // advected_particles[thr_id]->clear();
287 // }
288 // if (finished_particles[thr_id]->size() > 10000000) {
289 // {
290 // auto lock = std::lock_guard{mutex()};
291 // finished.push_back(*finished_particles[thr_id]);
292 // }
293 // finished_particles[thr_id]->clear();
294 // }
295 // if (!advected_particles[thr_id]->empty()) {
296 // {
297 // auto lock = std::lock_guard{mutex()};
298 // hdd_data[writer].push_back(*advected_particles[thr_id]);
299 // }
300 // advected_particles[thr_id]->clear();
301 // }
302 // if (!finished_particles[thr_id]->empty()) {
303 // {
304 // auto lock = std::lock_guard{mutex()};
305 // finished.push_back(*finished_particles[thr_id]);
306 // }
307 // finished_particles[thr_id]->clear();
308 // }
309 // }
310 // particles.clear();
311 // }
312 // },
313 // i);
314 // }
315 // for (auto& thread : thread_pool) {
316 // thread.join();
317 // }
318 // }
319 //
320 // reader = 1 - reader;
321 // writer = 1 - writer;
322 // hdd_data[writer].clear();
323 // }
324 // hdd_data[reader].clear();
325 // return path;
326 // }
327 //----------------------------------------------------------------------------
328 template <split_behavior SplitBehavior, typename Flowmap>
329 [[nodiscard]] auto advect(Flowmap &&phi, real_type const stepwidth,
330 real_type const t_end,
331 std::atomic_uint64_t &uuid_generator) const {
332 using namespace detail::autonomous_particle;
333 auto hierarchy_mutex = std::mutex{};
334 auto hierarchy_pairs = std::vector{hierarchy_pair{m_id, m_id}};
335 auto [advected_particles, advected_simple_particles] =
336 advect<SplitBehavior>(std::forward<Flowmap>(phi), stepwidth, t_end,
337 {*this}, hierarchy_pairs, hierarchy_mutex,
338 uuid_generator);
339 auto edges = edgeset<Real, NumDimensions>{};
340 // auto map = std::unordered_map<
341 // std::size_t, typename edgeset<Real, NumDimensions>::vertex_handle>{};
342 // for (auto const& p : advected_particles) {
343 // map[p.id()] = edges.insert_vertex(p.center());
344 // }
345 // triangulate(edges, hierarchy{hierarchy_pairs, map, edges});
346 return std::tuple{std::move(advected_particles),
347 std::move(advected_simple_particles), std::move(edges)};
348 }
349 //----------------------------------------------------------------------------
351 template <typename Flowmap>
352 auto advect_with_two_splits(Flowmap &&phi, real_type const stepwidth,
353 real_type const t_end,
354 std::atomic_uint64_t &uuid_generator) const {
355 return advect<typename split_behaviors::two_splits>(
356 std::forward<Flowmap>(phi), stepwidth, t_end, uuid_generator);
357 }
358 //----------------------------------------------------------------------------
360 template <typename Flowmap>
361 auto advect_with_three_splits(Flowmap &&phi, real_type const stepwidth,
362 real_type const t_end,
363 std::atomic_uint64_t &uuid_generator) const {
364 return advect<typename split_behaviors::three_splits>(
365 std::forward<Flowmap>(phi), stepwidth, t_end, uuid_generator);
366 }
367 //----------------------------------------------------------------------------
369 template <typename Flowmap>
371 Flowmap &&phi, real_type const stepwidth, real_type const t_end,
372 std::atomic_uint64_t &uuid_generator) const {
373 return advect<typename split_behaviors::three_and_four_splits>(
374 std::forward<Flowmap>(phi), stepwidth, t_end, uuid_generator);
375 }
376 //----------------------------------------------------------------------------
378 template <typename Flowmap>
379 auto advect_with_five_splits(Flowmap &&phi, real_type const stepwidth,
380 real_type const t_end,
381 std::atomic_uint64_t &uuid_generator) const {
382 return advect<typename split_behaviors::five_splits>(
383 std::forward<Flowmap>(phi), stepwidth, t_end, uuid_generator);
384 }
385 //----------------------------------------------------------------------------
387 template <typename Flowmap>
388 auto advect_with_seven_splits(Flowmap &&phi, real_type const stepwidth,
389 real_type const t_end,
390 std::atomic_uint64_t &uuid_generator) const {
391 return advect<typename split_behaviors::seven_splits>(
392 std::forward<Flowmap>(phi), stepwidth, t_end, uuid_generator);
393 }
394 //----------------------------------------------------------------------------
404 template <split_behavior SplitBehavior, typename Flowmap>
405 [[nodiscard]] static auto advect(Flowmap &&phi, real_type const stepwidth,
406 real_type const t_end,
407 container_type const &initial_particles,
408 std::atomic_uint64_t &uuid_generator) {
409 using namespace detail::autonomous_particle;
410 auto particles = container_type{};
411 auto hierarchy_mutex = std::mutex{};
412 auto hierarchy_pairs = std::vector<hierarchy_pair>{};
413 // hierarchy_pairs.reserve(particles.size());
414 // for (auto const& p : particles) {
415 // hierarchy_pairs.emplace_back(p.id(), p.id());
416 // }
417 auto [advected_particles, advected_simple_particles] =
418 advect<SplitBehavior>(std::forward<Flowmap>(phi), stepwidth, t_end,
419 initial_particles, hierarchy_pairs,
420 hierarchy_mutex, uuid_generator);
421
422 auto edges = edgeset<Real, NumDimensions>{};
423 auto map = std::unordered_map<
424 std::size_t, typename edgeset<Real, NumDimensions>::vertex_handle>{};
425 for (auto const &p : advected_particles) {
426 map[p.id()] = edges.insert_vertex(p.center());
427 }
428 // auto const h = hierarchy{hierarchy_pairs, map, edges};
429 // triangulate(edges, h);
430 //
431 // auto const s = initial_particle_distribution.size();
432 // for (std::size_t j = 0; j < s[1]; ++j) {
433 // for (std::size_t i = 0; i < s[0] - 1; ++i) {
434 // auto const id0 = i + j * s[0];
435 // auto const id1 = (i + 1) + j * s[0];
436 // triangulate(edges, h.find_by_id(id0), h.find_by_id(id1));
437 // }
438 // }
439 // for (std::size_t i = 0; i < s[0]; ++i) {
440 // for (std::size_t j = 0; j < s[1] - 1; ++j) {
441 // auto const id0 = i + j * s[0];
442 // auto const id1 = i + (j + 1) * s[0];
443 // triangulate(edges, h.find_by_id(id0), h.find_by_id(id1));
444 // }
445 // }
446
447 return std::tuple{std::move(advected_particles),
448 std::move(advected_simple_particles), std::move(edges)};
449 }
450 //----------------------------------------------------------------------------
451 template <typename Flowmap>
452 static auto advect_with_two_splits(Flowmap &&phi, real_type const stepwidth,
453 real_type const t_end,
454 container_type const &initial_particles,
455 std::atomic_uint64_t &uuid_generator) {
456 return advect<typename split_behaviors::two_splits>(
457 std::forward<Flowmap>(phi), stepwidth, t_end, initial_particles,
458 uuid_generator);
459 }
460 //----------------------------------------------------------------------------
461 template <typename Flowmap>
462 static auto advect_with_three_splits(Flowmap &&phi, real_type const stepwidth,
463 real_type const t_end,
464 container_type const &initial_particles,
465 std::atomic_uint64_t &uuid_generator) {
466 return advect<typename split_behaviors::three_splits>(
467 std::forward<Flowmap>(phi), stepwidth, t_end, initial_particles,
468 uuid_generator);
469 }
470 //----------------------------------------------------------------------------
471 template <typename Flowmap>
472 static auto
473 advect_with_three_and_four_splits(Flowmap &&phi, real_type const stepwidth,
474 real_type const t_end,
475 container_type const &initial_particles,
476 std::atomic_uint64_t &uuid_generator) {
477 return advect<typename split_behaviors::three_and_four_splits>(
478 std::forward<Flowmap>(phi), stepwidth, t_end, initial_particles,
479 uuid_generator);
480 }
481 //----------------------------------------------------------------------------
482 template <typename Flowmap>
483 static auto advect_with_five_splits(Flowmap &&phi, real_type const stepwidth,
484 real_type const t_end,
485 container_type const &initial_particles,
486 std::atomic_uint64_t &uuid_generator) {
487 return advect<typename split_behaviors::five_splits>(
488 std::forward<Flowmap>(phi), stepwidth, t_end, initial_particles,
489 uuid_generator);
490 }
491 //----------------------------------------------------------------------------
492 template <typename Flowmap>
493 static auto advect_with_seven_splits(Flowmap &&phi, real_type const stepwidth,
494 real_type const t_end,
495 container_type const &initial_particles,
496 std::atomic_uint64_t &uuid_generator) {
497 return advect<typename split_behaviors::seven_splits>(
498 std::forward<Flowmap>(phi), stepwidth, t_end, initial_particles,
499 uuid_generator);
500 }
501 //----------------------------------------------------------------------------
502 static auto
505 std::atomic_uint64_t &uuid_generator) {
506 return particles_from_grid(t0, g, default_max_split_depth, uuid_generator);
507 }
508 //----------------------------------------------------------------------------
509 static auto
512 std::uint8_t const max_split_depth,
513 std::atomic_uint64_t &uuid_generator) {
514 auto particles = container_type{};
515 auto initial_particle_distribution = g.copy_without_properties();
516 for (std::size_t i = 0; i < NumDimensions; ++i) {
517 auto const spacing = initial_particle_distribution.dimension(i).spacing();
518 initial_particle_distribution.dimension(i).pop_front();
519 initial_particle_distribution.dimension(i).front() -= spacing / 2;
520 initial_particle_distribution.dimension(i).back() -= spacing / 2;
521 }
522 initial_particle_distribution.vertices().iterate_indices(
523 [&](auto const... is) {
524 particles.emplace_back(
525 initial_particle_distribution.vertex_at(is...), t0,
526 initial_particle_distribution.dimension(0).spacing() / 2,
527 max_split_depth, uuid_generator);
528 });
529 return particles;
530 }
531 //------------------------------------------------------------------------------
533 real_type const t0,
535 std::atomic_uint64_t &uuid_generator) {
537 t0, g, default_max_split_depth, uuid_generator);
538 }
539 //----------------------------------------------------------------------------
541 real_type const t0,
543 std::uint8_t const max_split_depth,
544 std::atomic_uint64_t &uuid_generator) {
545 auto particles = container_type{};
546 auto initial_particle_distribution = g.copy_without_properties();
547 auto const radius =
548 initial_particle_distribution.dimension(0).spacing() / 2;
549 for (std::size_t i = 0; i < NumDimensions; ++i) {
550 auto const spacing = initial_particle_distribution.dimension(i).spacing();
551 initial_particle_distribution.dimension(i).pop_front();
552 initial_particle_distribution.dimension(i).front() -= spacing / 2;
553 initial_particle_distribution.dimension(i).back() -= spacing / 2;
554 }
555 initial_particle_distribution.vertices().iterate_indices(
556 [&](auto const... is) {
557 particles.emplace_back(initial_particle_distribution.vertex_at(is...),
558 t0, radius, max_split_depth, uuid_generator);
559 });
560 auto const small_radius =
561 (std::sqrt(2 * initial_particle_distribution.dimension(0).spacing() *
562 initial_particle_distribution.dimension(0).spacing()) -
563 initial_particle_distribution.dimension(0).spacing()) /
564 2;
565
566 for (std::size_t i = 0; i < NumDimensions; ++i) {
567 auto const spacing = initial_particle_distribution.dimension(i).spacing();
568 initial_particle_distribution.dimension(i).pop_front();
569 initial_particle_distribution.dimension(i).front() -= spacing / 2;
570 initial_particle_distribution.dimension(i).back() -= spacing / 2;
571 }
572 initial_particle_distribution.vertices().iterate_indices(
573 [&](auto const... is) {
574 particles.emplace_back(initial_particle_distribution.vertex_at(is...),
575 t0, small_radius, max_split_depth,
576 uuid_generator);
577 });
578 return particles;
579 }
580 //------------------------------------------------------------------------------
582 real_type const t0,
584 std::atomic_uint64_t &uuid_generator) {
586 uuid_generator);
587 }
588 //------------------------------------------------------------------------------
590 real_type const t0,
592 std::uint8_t const max_split_depth,
593 std::atomic_uint64_t &uuid_generator) {
595 t0, g, max_split_depth, uuid_generator,
596 std::make_index_sequence<num_dimensions()>{});
597 }
598 //----------------------------------------------------------------------------
599 template <std::size_t... Is>
601 real_type const t0,
603 std::uint8_t const max_split_depth, std::atomic_uint64_t &uuid_generator,
604 std::index_sequence<Is...> /*idx_seq*/) {
605 auto particles = container_type{};
606 auto initial_particle_distribution = g.copy_without_properties();
607 auto const radius =
608 initial_particle_distribution.dimension(0).spacing() / 2;
609 invoke([&] {
610 auto dim = initial_particle_distribution.template dimension<Is>();
611 auto const half_spacing = dim.spacing() / 2;
612 dim.pop_front();
613 dim.front() -= half_spacing;
614 dim.back() -= half_spacing;
615 initial_particle_distribution.template set_dimension<Is>(dim);
616 }...);
617 initial_particle_distribution.vertices().iterate_positions(
618 [&](auto const &x) {
619 particles.emplace_back(x, t0, radius, max_split_depth,
620 uuid_generator);
621 });
622
623 invoke([&] {
624 auto dim = initial_particle_distribution.template dimension<Is>();
625 auto const half_spacing = dim.spacing() / 2;
626 dim.pop_front();
627 dim.front() -= half_spacing;
628 dim.back() -= half_spacing;
629 initial_particle_distribution.template set_dimension<Is>(dim);
630 }...);
631 initial_particle_distribution.vertices().iterate_positions(
632 [&](auto const &x) {
633 particles.emplace_back(x, t0, radius, max_split_depth,
634 uuid_generator);
635 });
636 return particles;
637 }
638 //----------------------------------------------------------------------------
647 template <split_behavior SplitBehavior, typename Flowmap>
648 [[nodiscard]] static auto
649 advect(Flowmap &&phi, real_type const stepwidth, real_type const t0,
650 real_type const t_end,
652 using namespace detail::autonomous_particle;
653 auto uuid_generator = std::atomic_uint64_t{};
654 auto particles = particles_from_grid(t0, g, uuid_generator);
655 auto hierarchy_mutex = std::mutex{};
656 auto hierarchy_pairs = std::vector<hierarchy_pair>{};
657 // hierarchy_pairs.reserve(particles.size());
658 // for (auto const& p : particles) {
659 // hierarchy_pairs.emplace_back(p.id(), p.id());
660 //}
661 auto [advected_particles, advected_simple_particles] =
662 advect<SplitBehavior>(std::forward<Flowmap>(phi), stepwidth, t_end,
663 particles, hierarchy_pairs, hierarchy_mutex,
664 uuid_generator);
665
666 auto edges = edgeset<Real, NumDimensions>{};
667 auto map = std::unordered_map<
668 std::size_t, typename edgeset<Real, NumDimensions>::vertex_handle>{};
669 for (auto const &p : advected_particles) {
670 map[p.id()] = edges.insert_vertex(p.center());
671 }
672 auto const h = hierarchy{hierarchy_pairs, map, edges};
673 // triangulate(edges, h);
674 //
675 // auto const s = g.size();
676 //--s[0];
677 //--s[1];
678 // for (std::size_t j = 0; j < s[1]; ++j) {
679 // for (std::size_t i = 0; i < s[0] - 1; ++i) {
680 // auto const id0 = i + j * s[0];
681 // auto const id1 = (i + 1) + j * s[0];
682 // triangulate(edges, h.find_by_id(id0), h.find_by_id(id1));
683 // }
684 // }
685 // for (std::size_t i = 0; i < s[0]; ++i) {
686 // for (std::size_t j = 0; j < s[1] - 1; ++j) {
687 // auto const id0 = i + j * s[0];
688 // auto const id1 = i + (j + 1) * s[0];
689 // triangulate(edges, h.find_by_id(id0), h.find_by_id(id1));
690 // }
691 // }
692
693 return std::tuple{std::move(advected_particles),
694 std::move(advected_simple_particles), std::move(edges)};
695 }
696 //----------------------------------------------------------------------------
697 template <typename Flowmap>
699 Flowmap &&phi, real_type const stepwidth, real_type const t0,
700 real_type const t_end,
702 return advect<typename split_behaviors::two_splits>(
703 std::forward<Flowmap>(phi), stepwidth, t0, t_end, g);
704 }
705 //----------------------------------------------------------------------------
706 template <typename Flowmap>
708 Flowmap &&phi, real_type const stepwidth, real_type const t0,
709 real_type const t_end,
711 return advect<typename split_behaviors::three_splits>(
712 std::forward<Flowmap>(phi), stepwidth, t0, t_end, g);
713 }
714 //----------------------------------------------------------------------------
715 template <typename Flowmap>
717 Flowmap &&phi, real_type const stepwidth, real_type const t0,
718 real_type const t_end,
720 return advect<typename split_behaviors::three_and_four_splits>(
721 std::forward<Flowmap>(phi), stepwidth, t0, t_end, g);
722 }
723 //----------------------------------------------------------------------------
724 template <typename Flowmap>
726 Flowmap &&phi, real_type const stepwidth, real_type const t0,
727 real_type const t_end,
729 return advect<typename split_behaviors::five_splits>(
730 std::forward<Flowmap>(phi), stepwidth, t0, t_end, g);
731 }
732 //----------------------------------------------------------------------------
733 template <typename Flowmap>
735 Flowmap &&phi, real_type const stepwidth, real_type const t0,
736 real_type const t_end,
738 return advect<typename split_behaviors::seven_splits>(
739 std::forward<Flowmap>(phi), stepwidth, t0, t_end, g);
740 }
741 //----------------------------------------------------------------------------
742private:
743 //----------------------------------------------------------------------------
753 template <split_behavior SplitBehavior, typename Flowmap>
754 static auto [[nodiscard]] advect(Flowmap &&phi, real_type const stepwidth,
755 real_type const t_end,
756 container_type particles,
757 std::vector<hierarchy_pair> &hierarchy_pairs,
758 std::mutex &hierarchy_mutex,
759 std::atomic_uint64_t &uuid_generator) {
760 auto const num_threads = this_type::num_threads();
761
762 auto finished_particles = container_type{};
763 auto finished_simple_particles = simple_particle_container_type{};
764
765 // {particles_to_be_advected, advected_particles, finished_particles}
766 auto particles_per_thread = std::vector<
769 while (particles.size() > 0) {
771 particles_per_thread);
772 advect_particle_pools<SplitBehavior>(
773 num_threads, std::forward<Flowmap>(phi), stepwidth, t_end,
774 particles_per_thread, hierarchy_pairs, hierarchy_mutex,
775 uuid_generator);
776 gather_particles(particles, finished_particles, finished_simple_particles,
777 particles_per_thread);
778 }
779 return std::tuple{std::move(finished_particles),
780 std::move(finished_simple_particles)};
781 }
782 //----------------------------------------------------------------------------
783 static auto num_threads() {
784 auto num_threads = std::size_t{};
785#pragma omp parallel
786 {
787 if (omp_get_thread_num() == 0) {
788 num_threads = static_cast<std::size_t>(omp_get_num_threads());
789 }
790 }
791 return num_threads;
792 }
793 //----------------------------------------------------------------------------
794 static auto
796 container_type &particles,
797 auto &particles_per_thread) {
798 using namespace std::ranges;
799#pragma omp parallel
800 {
801 auto const thr_id = omp_get_thread_num();
802 auto const begin = std::size_t(thr_id * size(particles) / num_threads);
803 auto const end =
804 std::size_t((thr_id + 1) * size(particles) / num_threads);
805 auto &cont = std::get<0>(*particles_per_thread[thr_id]);
806 cont.reserve(end - begin);
807 copy(particles.begin() + begin, particles.begin() + end,
808 std::back_inserter(cont));
809 }
810 particles.clear();
811 }
812 //----------------------------------------------------------------------------
813 template <split_behavior SplitBehavior, typename Flowmap>
815 std::size_t const /*num_threads*/, Flowmap &&phi,
816 real_type const stepwidth, real_type const t_end,
817 auto &particles_per_thread, std::vector<hierarchy_pair> &hierarchy_pairs,
818 std::mutex &hierarchy_mutex, std::atomic_uint64_t &uuid_generator) {
819#pragma omp parallel
820 {
821 auto const thr_id = omp_get_thread_num();
822 auto &[particles_at_t0, particles_at_t1, finished, simple_particles] =
823 *particles_per_thread[thr_id];
824 for (auto const &particle : particles_at_t0) {
825 particle.template advect_until_split<SplitBehavior>(
826 std::forward<Flowmap>(phi), stepwidth, t_end, particles_at_t1,
827 finished, simple_particles, hierarchy_pairs, hierarchy_mutex,
828 uuid_generator);
829 }
830 }
831 }
832 //----------------------------------------------------------------------------
833 static auto gather_particles(container_type &particles,
834 container_type &finished_particles,
835 simple_particle_container_type &simple_particles,
836 auto &particles_per_thread) {
837 using namespace std::ranges;
838 for (auto &ps : particles_per_thread) {
839 auto &[base, advected, finished, simple] = *ps;
840 base.clear();
841 copy(advected, std::back_inserter(particles));
842 advected.clear();
843 copy(finished, std::back_inserter(finished_particles));
844 finished.clear();
845 copy(simple, std::back_inserter(simple_particles));
846 simple.clear();
847 }
848 }
849 //----------------------------------------------------------------------------
850public:
851 //----------------------------------------------------------------------------
866 // template <
867 // split_behavior SplitBehavior = typename split_behaviors::three_splits,
868 // typename Flowmap>
869 // auto advect_until_split(Flowmap phi, real_type stepwidth,
870 // real_type const t_end,
871 // container_type& splitted_particles,
872 // container_type& finished_particles,
873 // simple_particle_container_type& simple_particles,
874 // std::vector<hierarchy_pair>& hierarchy_pairs,
875 // std::mutex& hierarchy_mutex,
876 // std::atomic_uint64_t& uuid_generator) const {
877 // if constexpr (is_cacheable<std::decay_t<decltype(phi)>>()) {
878 // phi.use_caching(false);
879 // }
880 // static constexpr real_type min_tau_step = 1e-10;
881 // static constexpr real_type max_cond_overshoot = 1e-8;
882 // static constexpr auto split_cond =
883 // SplitBehavior::split_cond; static constexpr auto split_sqr_cond =
884 // split_cond * split_cond; static constexpr auto split_radii =
885 // SplitBehavior::radii; static constexpr auto split_offsets =
886 // SplitBehavior::offsets; auto const [eigvecs_S, eigvals_S] =
887 // this->main_axes(); auto const B = eigvecs_S * diag(eigvals_S); //
888 // current main axes auto const K = *solve(diag(eigvals_S),
889 // transposed(eigvecs_S));
890 //
891 // mat_type H, HHt, advected_nabla_phi, assembled_nabla_phi, advected_B,
892 // ghosts_positive_offset, ghosts_negative_offset, prev_ghosts_forward,
893 // prev_ghosts_backward;
894 // auto min_stepwidth_reached = false;
895 // auto advected_ellipse = ellipse_type{*this};
896 // auto current_radii = vec_type{};
897 // auto eig_HHt = std::pair<mat_type, vec_type>{};
898 // auto sqr_cond_H = real_type(1);
899 // auto const& eigvecs_HHt = eig_HHt.first;
900 // auto const& eigvals_HHt = eig_HHt.second;
901 //
902 // // initialize ghosts
903 // for (std::size_t i = 0; i < num_dimensions(); ++i) {
904 // ghosts_positive_offset.col(i) = x();
905 // }
906 // ghosts_negative_offset = ghosts_positive_offset;
907 //
908 // ghosts_positive_offset += B;
909 // ghosts_negative_offset -= B;
910 //
911 // // fields for backup
912 // auto t_prev = t();
913 // auto prev_center = advected_ellipse.center();
914 // prev_ghosts_forward = ghosts_positive_offset;
915 // prev_ghosts_backward = ghosts_negative_offset;
916 // auto prev_cond_HHt = sqr_cond_H;
917 //
918 // // repeat as long as particle's ellipse is not wide enough or t_end is
919 // not
920 // // reached or the ellipse gets too small. If the latter happens make it a
921 // // simple massless particle
922 // auto t_advected = t();
923 // while (sqr_cond_H < split_sqr_cond || t_advected < t_end) {
924 // if (!min_stepwidth_reached) {
925 // // backup state before advection
926 // prev_ghosts_forward = ghosts_positive_offset;
927 // prev_ghosts_backward = ghosts_negative_offset;
928 // prev_center = advected_ellipse.center();
929 // prev_cond_HHt = sqr_cond_H;
930 // t_prev = t_advected;
931 //
932 // // increase time
933 // if (t_advected + stepwidth > t_end) {
934 // stepwidth = t_end - t_advected;
935 // t_advected = t_end;
936 // } else {
937 // t_advected += stepwidth;
938 // }
939 // auto const cur_stepwidth = t_advected - t_prev;
940 //
941 // // advect center and ghosts
942 // advected_ellipse.center() =
943 // phi(advected_ellipse.center(), t_advected, cur_stepwidth);
944 // ghosts_positive_offset = phi(ghosts_positive_offset, t_advected,
945 // cur_stepwidth); ghosts_negative_offset = phi(ghosts_negative_offset,
946 // t_advected, cur_stepwidth);
947 //
948 // // make computations
949 // H = (ghosts_positive_offset - ghosts_negative_offset) *
950 // half; HHt = H * transposed(H); eig_HHt =
951 // eigenvectors_sym(HHt); sqr_cond_H = eigvals_HHt(num_dimensions() - 1)
952 // / eigvals_HHt(0);
953 //
954 // if (std::isnan(sqr_cond_H)) {
955 // simple_particles.emplace_back(x0(), advected_ellipse.center(),
956 // t_advected);
957 // }
958 // advected_nabla_phi = H * K;
959 // assembled_nabla_phi = advected_nabla_phi * m_nabla_phi;
960 //
961 // current_radii = sqrt(eigvals_HHt);
962 // advected_B = eigvecs_HHt * diag(current_radii);
963 // advected_ellipse.S() = advected_B * transposed(eigvecs_HHt);
964 // }
965 //
966 // // check if particle has reached t_end
967 // if (t_advected == t_end &&
968 // sqr_cond_H <= split_sqr_cond + max_cond_overshoot) {
969 // finished_particles.emplace_back(advected_ellipse, t_advected, x0(),
970 // assembled_nabla_phi, m_id);
971 // return;
972 // }
973 //
974 // // check if particle's ellipse has reached its splitting width
975 // if ((sqr_cond_H >= split_sqr_cond &&
976 // sqr_cond_H <= split_sqr_cond + max_cond_overshoot) ||
977 // min_stepwidth_reached) {
978 // for (std::size_t i = 0; i < size(split_radii); ++i) {
979 // auto const new_eigvals = current_radii * split_radii[i];
980 // auto const offset2 = advected_B * split_offsets[i];
981 // auto const offset0 = *solve(assembled_nabla_phi, offset2);
982 // auto offset_ellipse = ellipse_type{
983 // advected_ellipse.center() + offset2,
984 // eigvecs_HHt * diag(new_eigvals) * transposed(eigvecs_HHt)};
985 //
986 // splitted_particles.emplace_back(offset_ellipse, t_advected,
987 // x0() + offset0,
988 // assembled_nabla_phi,
989 // uuid_generator);
990 // auto lock = std::lock_guard{hierarchy_mutex};
991 // hierarchy_pairs.emplace_back(splitted_particles.back().m_id, m_id);
992 // }
993 // return;
994 // }
995 // // check if particle's ellipse is wider than its splitting width
996 // if (sqr_cond_H > split_sqr_cond + max_cond_overshoot) {
997 // auto const prev_stepwidth = stepwidth;
998 // stepwidth *= half;
999 // min_stepwidth_reached = stepwidth == prev_stepwidth;
1000 // if (stepwidth < min_tau_step) {
1001 // min_stepwidth_reached = true;
1002 // }
1003 // if (!min_stepwidth_reached) {
1004 // sqr_cond_H = prev_cond_HHt;
1005 // ghosts_positive_offset = prev_ghosts_forward;
1006 // ghosts_negative_offset = prev_ghosts_backward;
1007 // t_advected = t_prev;
1008 // advected_ellipse.center() = prev_center;
1009 // }
1010 // }
1011 // }
1012 // }
1013 //----------------------------------------------------------------------------
1028 template <
1029 split_behavior SplitBehavior = typename split_behaviors::three_splits,
1030 typename Flowmap>
1031 auto advect_until_split(Flowmap phi, real_type stepwidth,
1032 real_type const t_end,
1033 container_type &splitted_particles,
1034 container_type &finished_particles,
1035 simple_particle_container_type &simple_particles,
1036 std::vector<hierarchy_pair> & /*hierarchy_pairs*/,
1037 std::mutex & /*hierarchy_mutex*/,
1038 std::atomic_uint64_t &uuid_generator) const {
1039 if constexpr (is_cacheable<std::decay_t<decltype(phi)>>()) {
1040 phi.use_caching(false);
1041 }
1042 // static constexpr real_type min_tau_step = 1e-10;
1043 // static constexpr real_type max_cond_overshoot = 1e-8;
1044 // static constexpr auto split_cond =
1045 // SplitBehavior::split_cond;
1046 static constexpr auto split_radii = SplitBehavior::radii;
1047 static constexpr auto split_offsets = SplitBehavior::offsets;
1048 auto const [eigvecs_S, eigvals_S] = this->main_axes();
1049 auto const B = eigvecs_S * diag(eigvals_S); // current main axes
1050 auto const K = *solve(diag(eigvals_S), transposed(eigvecs_S));
1051
1052 mat_type H, HHt, D, advected_nabla_phi, assembled_nabla_phi, advected_B,
1053 ghosts_positive_offset, ghosts_negative_offset, prev_ghosts_forward,
1054 prev_ghosts_backward;
1055 auto advected_ellipse = ellipse_type{*this};
1056 auto current_radii = vec_type{};
1057 auto eig_HHt = std::pair<mat_type, vec_type>{};
1058 auto sqr_cond_H = real_type(1);
1059 auto linearity = real_type(0);
1060 auto const &eigvecs_HHt = eig_HHt.first;
1061 auto const &eigvals_HHt = eig_HHt.second;
1062
1063 // initialize ghosts
1064 for (std::size_t i = 0; i < num_dimensions(); ++i) {
1065 ghosts_positive_offset.col(i) = x();
1066 ghosts_negative_offset.col(i) = x();
1067 }
1068 ghosts_positive_offset += B;
1069 ghosts_negative_offset -= B;
1070
1071 // repeat as long as particle's ellipse is not wide enough or t_end is not
1072 // reached or the ellipse gets too small. If the latter happens make it a
1073 // simple massless particle
1074 auto t_advected = t();
1075 while (t_advected < t_end) {
1076 if (t_advected + stepwidth > t_end) {
1077 stepwidth = t_end - t_advected;
1078 }
1079
1080 // advect center and ghosts
1081 advected_ellipse.center() =
1082 phi(advected_ellipse.center(), t_advected, stepwidth);
1083 ghosts_positive_offset =
1084 phi(ghosts_positive_offset, t_advected, stepwidth);
1085 ghosts_negative_offset =
1086 phi(ghosts_negative_offset, t_advected, stepwidth);
1087
1088 // increase time
1089 if (t_advected + stepwidth + 1e-6 > t_end) {
1090 t_advected = t_end;
1091 } else {
1092 t_advected += stepwidth;
1093 }
1094
1095 // make computations
1096 H = (ghosts_positive_offset - ghosts_negative_offset) * half;
1097 D = (ghosts_negative_offset + ghosts_positive_offset) * half;
1098 for (std::size_t i = 0; i < num_dimensions(); ++i) {
1099 D.col(i) -= advected_ellipse.center();
1100 }
1101 linearity = 0;
1102 for (std::size_t i = 0; i < num_dimensions(); ++i) {
1103 linearity += dot(D.col(i), D.col(i)) / dot(H.col(i), H.col(i));
1104 }
1105 HHt = H * transposed(H);
1106 eig_HHt = eigenvectors_sym(HHt);
1107
1108 if (std::isnan(linearity)) {
1109 simple_particles.emplace_back(x0(), advected_ellipse.center(),
1110 t_advected);
1111 return;
1112 }
1113 sqr_cond_H = eigvals_HHt(num_dimensions() - 1) / eigvals_HHt(0);
1114
1115 advected_nabla_phi = H * K;
1116 assembled_nabla_phi = advected_nabla_phi * m_nabla_phi;
1117
1118 current_radii = sqrt(eigvals_HHt);
1119 advected_B = eigvecs_HHt * diag(current_radii);
1120 advected_ellipse.S() = advected_B * transposed(eigvecs_HHt);
1121
1122 // check if particle has reached t_end
1123 if (t_advected == t_end) {
1124 finished_particles.emplace_back(advected_ellipse, t_advected, x0(),
1125 assembled_nabla_phi, split_depth(),
1126 max_split_depth(), id());
1127 return;
1128 }
1129
1130 // check if particle's ellipse has reached its splitting width
1131 static auto constexpr linearity_threshold = 1e-6;
1132 static auto constexpr distortion_threshold = log(6);
1133 auto const distortion = log(sqr_cond_H);
1134 if (split_depth() != max_split_depth() &&
1135 (linearity >= linearity_threshold || distortion >= distortion_threshold)) {
1136 for (std::size_t i = 0; i < size(split_radii); ++i) {
1137 auto const new_eigvals = current_radii * split_radii[i];
1138 auto const offset2 = advected_B * split_offsets[i];
1139 auto const offset0 = *solve(assembled_nabla_phi, offset2);
1140 auto offset_ellipse = ellipse_type{
1141 advected_ellipse.center() + offset2,
1142 eigvecs_HHt * diag(new_eigvals) * transposed(eigvecs_HHt)};
1143
1144 splitted_particles.emplace_back(
1145 offset_ellipse, t_advected, x0() + offset0, assembled_nabla_phi,
1146 split_depth() + 1, max_split_depth(), uuid_generator);
1147 // auto lock = std::lock_guard{hierarchy_mutex};
1148 // hierarchy_pairs.emplace_back(splitted_particles.back().m_id, m_id);
1149 }
1150 return;
1151 }
1152 }
1153 }
1154 //----------------------------------------------------------------------------
1155 auto sampler() const {
1156 return sampler_type{initial_ellipse(), *this, m_nabla_phi};
1157 }
1158 //----------------------------------------------------------------------------
1159 auto discretize(std::size_t const n, forward_tag const /*tag*/) const {
1160 return initial_ellipse().discretize(n);
1161 }
1162 //----------------------------------------------------------------------------
1163 auto discretize(std::size_t const n, backward_tag const /*tag*/) const {
1164 return discretize(n);
1165 }
1166};
1167//------------------------------------------------------------------------------
1168template <floating_point Real>
1169auto write_vtp(std::vector<autonomous_particle<Real, 2>> const &particles,
1170 std::size_t const num_points, filesystem::path const &path,
1171 backward_tag const /*tag*/) {
1172 auto file = std::ofstream{path, std::ios::binary};
1173 if (!file.is_open()) {
1174 throw std::runtime_error{"Could not write " + path.string()};
1175 }
1176 auto offset = std::size_t{};
1177 using header_type = std::uint64_t;
1178 using connectivity_int = std::int32_t;
1179 using offset_int = connectivity_int;
1180 file << "<VTKFile"
1181 << " type=\"PolyData\""
1182 << " version=\"1.0\" "
1183 "byte_order=\"LittleEndian\""
1184 << " header_type=\"" << vtk::xml::to_data_type<header_type>() << "\">";
1185 file << "<PolyData>\n";
1186 for (std::size_t i = 0; i < size(particles); ++i) {
1187 file << "<Piece"
1188 << " NumberOfPoints=\"" << num_points << "\""
1189 << " NumberOfPolys=\"0\""
1190 << " NumberOfVerts=\"0\""
1191 << " NumberOfLines=\"" << num_points - 1 << "\""
1192 << " NumberOfStrips=\"0\""
1193 << ">\n";
1194
1195 // Points
1196 file << "<Points>";
1197 file << "<DataArray"
1198 << " format=\"appended\""
1199 << " offset=\"" << offset << "\""
1200 << " type=\"" << vtk::xml::to_data_type<Real>()
1201 << "\" NumberOfComponents=\"" << 3 << "\"/>";
1202 auto const num_bytes_points = header_type(sizeof(Real) * 3 * num_points);
1203 offset += num_bytes_points + sizeof(header_type);
1204 file << "</Points>\n";
1205
1206 // Lines
1207 file << "<Lines>\n";
1208 // Lines - connectivity
1209 file << "<DataArray format=\"appended\" offset=\"" << offset << "\" type=\""
1210 << vtk::xml::to_data_type<connectivity_int>()
1211 << "\" Name=\"connectivity\"/>\n";
1212 auto const num_bytes_connectivity =
1213 (num_points - 1) * 2 * sizeof(connectivity_int);
1214 offset += num_bytes_connectivity + sizeof(header_type);
1215 // Lines - offsets
1216 file << "<DataArray format=\"appended\" offset=\"" << offset << "\" type=\""
1217 << vtk::xml::to_data_type<offset_int>()
1218 << "\" Name=\"offsets\"/>\n";
1219 auto const num_bytes_offsets =
1220 sizeof(offset_int) * (num_points - 1) * 2;
1221 offset += num_bytes_offsets + sizeof(header_type);
1222 file << "</Lines>\n";
1223 file << "</Piece>\n";
1224 }
1225 file << "</PolyData>\n";
1226 file << "<AppendedData encoding=\"raw\">_";
1227 // Writing vertex data to appended data section
1228 for (auto const &particle : particles) {
1229 auto const num_bytes_points = header_type(sizeof(Real) * 3 * num_points);
1230 using namespace std::ranges;
1231 auto radial = tatooine::linspace<Real>{0, M_PI * 2, num_points + 1};
1232 radial.pop_back();
1233
1234 auto discretization = tatooine::line<Real, 3>{};
1235 auto radian_to_cartesian = [](auto const t) {
1236 return tatooine::vec{gcem::cos(t), gcem::sin(t), 0};
1237 };
1238 auto out_it = std::back_inserter(discretization);
1239 copy(radial | views::transform(radian_to_cartesian), out_it);
1240 discretization.set_closed(true);
1241 for (auto const v : discretization.vertices()) {
1242 auto v2 = particle.S() * discretization[v].xy() + particle.center();
1243 discretization[v].x() = v2.x();
1244 discretization[v].y() = v2.y();
1245 }
1246
1247 // Writing points
1248 file.write(reinterpret_cast<char const *>(&num_bytes_points),
1249 sizeof(header_type));
1250 for (auto const v : discretization.vertices()) {
1251 file.write(reinterpret_cast<char const *>(discretization.at(v).data()),
1252 sizeof(Real) * 3);
1253 }
1254
1255 // Writing lines connectivity data to appended data section
1256 {
1257 auto connectivity_data = std::vector<connectivity_int>{};
1258 connectivity_data.reserve((num_points - 1) * 2);
1259 for (std::size_t i = 0; i < num_points - 1; ++i) {
1260 connectivity_data.push_back(static_cast<connectivity_int>(i));
1261 connectivity_data.push_back(
1262 static_cast<connectivity_int>(i + 1));
1263 }
1264
1265 auto const num_bytes_connectivity =
1266 header_type((num_points - 1) * 2 * sizeof(connectivity_int));
1267 file.write(reinterpret_cast<char const *>(&num_bytes_connectivity),
1268 sizeof(header_type));
1269 file.write(reinterpret_cast<char const *>(connectivity_data.data()),
1270 static_cast<std::streamsize>(num_bytes_connectivity));
1271 }
1272
1273 // Writing lines offsets to appended data section
1274 {
1275 auto offsets = std::vector<offset_int>(num_points, 2);
1276 for (std::size_t i = 1; i < size(offsets); ++i) {
1277 offsets[i] += offsets[i - 1];
1278 }
1279 auto const num_bytes_offsets =
1280 header_type(sizeof(offset_int) * (num_points - 1) * 2);
1281 file.write(reinterpret_cast<char const *>(&num_bytes_offsets),
1282 sizeof(header_type));
1283 file.write(reinterpret_cast<char const *>(offsets.data()),
1284 static_cast<std::streamsize>(num_bytes_offsets));
1285 }
1286 }
1287
1288 file << "</AppendedData>";
1289 file << "</VTKFile>";
1290}
1291//==============================================================================
1292// typedefs
1293//==============================================================================
1294template <std::size_t NumDimensions>
1296template <floating_point Real>
1298template <floating_point Real>
1302//==============================================================================
1303} // namespace tatooine
1304//==============================================================================
1305//#include <tatooine/reflection.h>
1306// namespace tatooine::reflection {
1307//==============================================================================
1308// template <floating_point Real, std::size_t NumDimensions>
1309// TATOOINE_MAKE_TEMPLATED_ADT_REFLECTABLE(
1310// (autonomous_particle<Real, NumDimensions>),
1311// TATOOINE_REFLECTION_INSERT_METHOD(center, center()),
1312// TATOOINE_REFLECTION_INSERT_METHOD(S, S()),
1313// TATOOINE_REFLECTION_INSERT_METHOD(x, x()),
1314// TATOOINE_REFLECTION_INSERT_METHOD(t, t()),
1315// TATOOINE_REFLECTION_INSERT_METHOD(nabla_phi, nabla_phi()))
1316//==============================================================================
1317//} // namespace tatooine::reflection
1318//==============================================================================
1319#endif
Definition: cache_alignment.h:21
Definition: concepts.h:30
Definition: concepts.h:84
Definition: autonomous_particle.h:21
Definition: tensor_concepts.h:23
Definition: algorithm.h:6
constexpr auto is_cacheable()
Definition: is_cacheable.h:19
auto begin(Range &&range)
Definition: iterator_facade.h:318
typename value_type_impl< T >::type value_type
Definition: type_traits.h:280
auto end(Range &&range)
Definition: iterator_facade.h:322
constexpr auto inv(diag_static_tensor< Tensor, N, N > const &A) -> std::optional< diag_static_tensor< vec< tatooine::value_type< Tensor >, N >, N, N > >
Definition: diag_tensor.h:109
constexpr auto log(arithmetic auto const x)
Definition: math.h:23
auto eigenvectors_sym(Mat &&A)
Definition: eigenvalues.h:44
constexpr auto invoke(invocable auto &&...funcs)
Definition: functional.h:8
auto solve(polynomial< Real, 1 > const &p) -> std::vector< Real >
solve a + b*x
Definition: polynomial.h:187
auto write_vtp(Lines const &lines, filesystem::path const &path) -> void
Definition: write.h:38
detail::rectilinear_grid::creator_t< linspace< Real >, N > uniform_rectilinear_grid
Definition: rectilinear_grid.h:1904
auto constexpr map(F &&f, Ts &&... ts)
maps unary function f to all single parameters of parameter pack ts
Definition: map.h:10
auto size(vec< ValueType, N > const &v)
Definition: vec.h:148
constexpr auto dot(base_tensor< Tensor0, T0, N > const &lhs, base_tensor< Tensor1, T1, N > const &rhs)
Definition: tensor_operations.h:120
auto transposed(dynamic_tensor auto &&t)
Definition: transposed_tensor.h:170
constexpr auto sqrt(arithmetic auto const x)
Definition: math.h:29
Definition: autonomous_particle.h:30
static auto particles_from_grid_small_filling_gaps(real_type const t0, uniform_rectilinear_grid< Real, NumDimensions > const &g, std::atomic_uint64_t &uuid_generator)
Definition: autonomous_particle.h:532
static auto distribute_particles_to_thread_containers(std::size_t const num_threads, container_type &particles, auto &particles_per_thread)
Definition: autonomous_particle.h:795
autonomous_particle(ellipse_type const &ell, real_type const t, pos_type const &x0, mat_type const &nabla_phi, std::uint8_t const split_depth, std::uint8_t const max_split_depth, std::uint64_t const id)
Definition: autonomous_particle.h:133
static auto particles_from_grid_filling_gaps(real_type const t0, uniform_rectilinear_grid< Real, NumDimensions > const &g, std::atomic_uint64_t &uuid_generator)
Definition: autonomous_particle.h:581
autonomous_particle(ellipse_type const &ell, real_type const t, std::uint8_t max_split_depth, std::atomic_uint64_t &uuid_generator)
Definition: autonomous_particle.h:109
static constexpr auto num_dimensions() -> std::size_t
Definition: autonomous_particle.h:33
static auto advect_with_three_and_four_splits(Flowmap &&phi, real_type const stepwidth, real_type const t_end, container_type const &initial_particles, std::atomic_uint64_t &uuid_generator)
Definition: autonomous_particle.h:473
auto center() const -> auto const &
Definition: hyper_ellipse.h:91
std::vector< this_type > container_type
Definition: autonomous_particle.h:49
vec< real_type, NumDimensions > vec_type
Definition: autonomous_particle.h:46
static auto advect(Flowmap &&phi, real_type const stepwidth, real_type const t_end, container_type const &initial_particles, std::atomic_uint64_t &uuid_generator)
Definition: autonomous_particle.h:405
std::uint8_t m_max_split_depth
Definition: autonomous_particle.h:71
static auto advect_with_seven_splits(Flowmap &&phi, real_type const stepwidth, real_type const t_end, container_type const &initial_particles, std::atomic_uint64_t &uuid_generator)
Definition: autonomous_particle.h:493
auto operator=(autonomous_particle &&other) noexcept -> autonomous_particle &=default
auto x() -> auto &
Definition: autonomous_particle.h:162
auto initial_ellipse() const
Definition: autonomous_particle.h:180
auto x0() -> auto &
Definition: autonomous_particle.h:158
real_type m_t
Definition: autonomous_particle.h:68
static auto advect_with_three_splits(Flowmap &&phi, real_type const stepwidth, real_type const t0, real_type const t_end, uniform_rectilinear_grid< Real, NumDimensions > const &g)
Definition: autonomous_particle.h:707
static auto gather_particles(container_type &particles, container_type &finished_particles, simple_particle_container_type &simple_particles, auto &particles_per_thread)
Definition: autonomous_particle.h:833
auto sampler() const
Definition: autonomous_particle.h:1155
static constexpr auto default_max_split_depth
Definition: autonomous_particle.h:62
autonomous_particle(ellipse_type const &ell, real_type const t, pos_type const &x0, mat_type const &nabla_phi, std::uint8_t const split_depth, std::uint8_t const max_split_depth, std::atomic_uint64_t &uuid_generator)
Definition: autonomous_particle.h:142
static auto advect_with_five_splits(Flowmap &&phi, real_type const stepwidth, real_type const t0, real_type const t_end, uniform_rectilinear_grid< Real, NumDimensions > const &g)
Definition: autonomous_particle.h:725
static auto particles_from_grid_filling_gaps(real_type const t0, uniform_rectilinear_grid< Real, NumDimensions > const &g, std::uint8_t const max_split_depth, std::atomic_uint64_t &uuid_generator)
Definition: autonomous_particle.h:589
auto nabla_phi() const -> auto const &
Definition: autonomous_particle.h:169
auto split_depth() const
Definition: autonomous_particle.h:182
mat_type m_nabla_phi
Definition: autonomous_particle.h:69
static auto particles_from_grid_small_filling_gaps(real_type const t0, uniform_rectilinear_grid< Real, NumDimensions > const &g, std::uint8_t const max_split_depth, std::atomic_uint64_t &uuid_generator)
Definition: autonomous_particle.h:540
autonomous_particle(ellipse_type const &ell, real_type const t, std::uint8_t max_split_depth, std::uint64_t const id)
Definition: autonomous_particle.h:103
auto advect_with_five_splits(Flowmap &&phi, real_type const stepwidth, real_type const t_end, std::atomic_uint64_t &uuid_generator) const
Advects single particle.
Definition: autonomous_particle.h:379
autonomous_particle(pos_type const &x, real_type const t, real_type const r, std::atomic_uint64_t &uuid_generator)
Definition: autonomous_particle.h:119
static auto advect_with_two_splits(Flowmap &&phi, real_type const stepwidth, real_type const t0, real_type const t_end, uniform_rectilinear_grid< Real, NumDimensions > const &g)
Definition: autonomous_particle.h:698
autonomous_particle(autonomous_particle &&other) noexcept=default
auto id() const
Definition: autonomous_particle.h:185
static auto advect(Flowmap &&phi, real_type const stepwidth, real_type const t_end, container_type particles, std::vector< hierarchy_pair > &hierarchy_pairs, std::mutex &hierarchy_mutex, std::atomic_uint64_t &uuid_generator)
Definition: autonomous_particle.h:754
static constexpr auto half
Definition: autonomous_particle.h:41
static auto particles_from_grid(real_type const t0, uniform_rectilinear_grid< Real, NumDimensions > const &g, std::atomic_uint64_t &uuid_generator)
Definition: autonomous_particle.h:503
autonomous_particle(ellipse_type const &ell, real_type const t, std::uint64_t const id)
Definition: autonomous_particle.h:94
autonomous_particle(pos_type const &x, real_type const t, real_type const r, std::uint8_t max_split_depth, std::uint64_t const id)
Definition: autonomous_particle.h:123
std::uint64_t m_id
Definition: autonomous_particle.h:72
auto x0(std::size_t i) const
Definition: autonomous_particle.h:160
static auto advect(Flowmap &&phi, real_type const stepwidth, real_type const t0, real_type const t_end, uniform_rectilinear_grid< Real, NumDimensions > const &g)
Definition: autonomous_particle.h:649
autonomous_particle(pos_type const &x, real_type const t, real_type const r, std::uint64_t const id)
Definition: autonomous_particle.h:114
static auto advect_with_three_and_four_splits(Flowmap &&phi, real_type const stepwidth, real_type const t0, real_type const t_end, uniform_rectilinear_grid< Real, NumDimensions > const &g)
Definition: autonomous_particle.h:716
static auto particles_from_grid(real_type const t0, uniform_rectilinear_grid< Real, NumDimensions > const &g, std::uint8_t const max_split_depth, std::atomic_uint64_t &uuid_generator)
Definition: autonomous_particle.h:510
static auto mutex() -> auto &
Definition: autonomous_particle.h:74
auto discretize(std::size_t const n, backward_tag const) const
Definition: autonomous_particle.h:1163
auto x(std::size_t const i) const
Definition: autonomous_particle.h:164
auto advect_with_three_splits(Flowmap &&phi, real_type const stepwidth, real_type const t_end, std::atomic_uint64_t &uuid_generator) const
Advects single particle.
Definition: autonomous_particle.h:361
pos_type m_x0
Definition: autonomous_particle.h:67
std::vector< simple_particle_type > simple_particle_container_type
Definition: autonomous_particle.h:50
static auto particles_from_grid_filling_gaps(real_type const t0, uniform_rectilinear_grid< Real, NumDimensions > const &g, std::uint8_t const max_split_depth, std::atomic_uint64_t &uuid_generator, std::index_sequence< Is... >)
Definition: autonomous_particle.h:600
auto x() const -> auto const &
Definition: autonomous_particle.h:163
static auto advect_with_two_splits(Flowmap &&phi, real_type const stepwidth, real_type const t_end, container_type const &initial_particles, std::atomic_uint64_t &uuid_generator)
Definition: autonomous_particle.h:452
auto x0() const -> auto const &
Definition: autonomous_particle.h:159
static auto advect_with_seven_splits(Flowmap &&phi, real_type const stepwidth, real_type const t0, real_type const t_end, uniform_rectilinear_grid< Real, NumDimensions > const &g)
Definition: autonomous_particle.h:734
autonomous_particle(pos_type const &x, real_type const t, real_type const r, std::uint8_t max_split_depth, std::atomic_uint64_t &uuid_generator)
Definition: autonomous_particle.h:128
static auto advect_particle_pools(std::size_t const, Flowmap &&phi, real_type const stepwidth, real_type const t_end, auto &particles_per_thread, std::vector< hierarchy_pair > &hierarchy_pairs, std::mutex &hierarchy_mutex, std::atomic_uint64_t &uuid_generator)
Definition: autonomous_particle.h:814
auto advect_with_seven_splits(Flowmap &&phi, real_type const stepwidth, real_type const t_end, std::atomic_uint64_t &uuid_generator) const
Advects single particle.
Definition: autonomous_particle.h:388
auto S0() const
Definition: autonomous_particle.h:171
geometry::hyper_ellipse< Real, NumDimensions > ellipse_type
Definition: autonomous_particle.h:51
auto discretize(std::size_t const n, forward_tag const) const
Definition: autonomous_particle.h:1159
static auto advect_with_three_splits(Flowmap &&phi, real_type const stepwidth, real_type const t_end, container_type const &initial_particles, std::atomic_uint64_t &uuid_generator)
Definition: autonomous_particle.h:462
auto advect(Flowmap &&phi, real_type const stepwidth, real_type const t_end, std::atomic_uint64_t &uuid_generator) const
Definition: autonomous_particle.h:329
auto t() const
Definition: autonomous_particle.h:167
autonomous_particle(autonomous_particle const &other)=default
{
Real real_type
Definition: autonomous_particle.h:45
auto advect_with_three_and_four_splits(Flowmap &&phi, real_type const stepwidth, real_type const t_end, std::atomic_uint64_t &uuid_generator) const
Advects single particle.
Definition: autonomous_particle.h:370
static auto advect_with_five_splits(Flowmap &&phi, real_type const stepwidth, real_type const t_end, container_type const &initial_particles, std::atomic_uint64_t &uuid_generator)
Definition: autonomous_particle.h:483
autonomous_particle(ellipse_type const &ell, real_type const t, std::atomic_uint64_t &uuid_generator)
Definition: autonomous_particle.h:99
auto max_split_depth() const
Definition: autonomous_particle.h:183
auto t() -> auto &
Definition: autonomous_particle.h:166
auto advect_with_two_splits(Flowmap &&phi, real_type const stepwidth, real_type const t_end, std::atomic_uint64_t &uuid_generator) const
Advects single particle.
Definition: autonomous_particle.h:352
auto advect_until_split(Flowmap phi, real_type stepwidth, real_type const t_end, container_type &splitted_particles, container_type &finished_particles, simple_particle_container_type &simple_particles, std::vector< hierarchy_pair > &, std::mutex &, std::atomic_uint64_t &uuid_generator) const
Definition: autonomous_particle.h:1031
auto S() const -> auto const &
Definition: hyper_ellipse.h:88
std::uint8_t m_split_depth
Definition: autonomous_particle.h:70
static auto num_threads()
Definition: autonomous_particle.h:783
auto operator=(autonomous_particle const &other) -> autonomous_particle &=default
Definition: tags.h:10
Definition: edgeset.h:9
Definition: tags.h:8
Definition: hyper_ellipse.h:12
auto center() const -> auto const &
Definition: hyper_ellipse.h:91
auto discretize(std::size_t const num_vertices=32) const
Definition: hyper_ellipse.h:178
auto main_axes() const
Definition: hyper_ellipse.h:259
auto S() const -> auto const &
Definition: hyper_ellipse.h:88
Definition: line.h:35
Definition: linspace.h:26
constexpr auto pop_back()
Definition: linspace.h:107
auto constexpr col(std::size_t i)
Definition: mat.h:175
Definition: particle.h:10
Definition: vec.h:12