ryujin 2.1.1 revision b04fb4aba7bd97ddfa5994a6b4b6e8586eb15db2
Loading...
Searching...
No Matches
selected_components_extractor.h
Go to the documentation of this file.
1//
2// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3// Copyright (C) 2024 - 2026 by the ryujin authors
4//
5
6#pragma once
7
8#include <compile_time_options.h>
9
10#include <ryujin/interface/parabolic_system.h>
11
12#include "gpu.h"
13#include "loop.h"
14#include "observer_pointer.h"
15#include "offline_data.h"
16#include "state_vector.h"
17
18#include <algorithm>
19#include <functional>
20#include <string>
21#include <tuple>
22#include <utility>
23#include <variant>
24#include <vector>
25
26namespace ryujin
27{
28 template <typename Description,
29 int dim,
30 typename Number,
31 typename MemorySpace>
32 class SelectedComponentsExtractorView;
33
78 template <typename Description, int dim, typename Number>
80 {
81 public:
86
87 using HyperbolicSystem = typename Description::HyperbolicSystem;
88
89 using View = typename HyperbolicSystem::template View<dim, Number>;
90
91 /*
92 * A selected component is identified by an offset into the linear
93 * range formed by concatenating all conserved, primitive, precomputed,
94 * initial-precomputed, parabolic, and additional components (in this
95 * order). The following constants mark the beginning of the respective
96 * sections. The parabolic section has a run time size, the offset of
97 * the additional section is thus stored in additional_offset_.
98 */
99
100 static constexpr unsigned int conserved_offset = 0;
101 static constexpr unsigned int primitive_offset = View::problem_dimension;
102 static constexpr unsigned int precomputed_offset =
103 2 * View::problem_dimension;
104 static constexpr unsigned int initial_offset =
105 precomputed_offset + View::n_precomputed_values;
106 static constexpr unsigned int parabolic_offset =
107 initial_offset + View::n_initial_precomputed_values;
108
110
114
124 const OfflineData<dim, Number> &offline_data,
125 const HyperbolicSystem &hyperbolic_system,
126 const Interface::ParabolicSystem &parabolic_system,
127 const MultiComponentVector<Number> &initial_precomputed,
128 const std::vector<std::string> &additional_names = {},
129 const std::vector<std::reference_wrapper<const ScalarVector<Number>>>
130 &additional_vectors = {});
131
140 void prepare(const std::vector<std::string> &selected);
141
158 template <typename MemorySpace = dealii::MemorySpace::Host>
159 void prepare_extraction(const StateVector<Number> &state_vector) const;
160
162
166
172 unsigned int n_selected() const;
173
175
179
185 template <typename MemorySpace = dealii::MemorySpace::Host>
187 view() const;
188
189 private:
191
195
196 dealii::ObserverPointer<const OfflineData<dim, Number>> offline_data_;
197 dealii::ObserverPointer<const HyperbolicSystem> hyperbolic_system_;
198 dealii::ObserverPointer<const Interface::ParabolicSystem> parabolic_system_;
199
200 const MultiComponentVector<Number> &initial_precomputed_;
201
202 const std::vector<std::string> additional_names_;
203 const std::vector<std::reference_wrapper<const ScalarVector<Number>>>
204 additional_vectors_;
205
209 const unsigned int additional_offset_;
210
215 std::vector<unsigned int> selection_;
216
222 bool read_conserved_ = false;
223 bool read_primitive_ = false;
224 bool read_precomputed_ = false;
225 bool read_initial_ = false;
226
227 template <typename MemorySpace>
228 using ExtractorView =
230
235 mutable std::variant<std::monostate,
238 view_;
239
241 };
242
243
256 template <typename Description,
257 int dim,
258 typename Number,
259 typename MemorySpace>
261 {
262 public:
263 static_assert(std::is_same_v<MemorySpace, dealii::MemorySpace::Host> ||
264 std::is_same_v<MemorySpace, dealii::MemorySpace::Default>,
265 "Unexpected memory space");
266
271
273
280 dealii::LinearAlgebra::distributed::Vector<Number, MemorySpace>;
281
283
287
295 std::vector<ScalarVector> extract() const;
296
301 DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int n_selected() const
302 {
303 return static_cast<unsigned int>(entries_.extent(0));
304 }
305
324 template <typename T = Number, typename Writer>
325 DEAL_II_HOST_DEVICE_ALWAYS_INLINE void
326 extract_element(unsigned int i, const Writer &write) const;
327
328 private:
330
334
335 using HyperbolicSystem = typename Description::HyperbolicSystem;
336 using EquationView = typename HyperbolicSystem::template View<dim, Number>;
337
338 template <int n_comp>
340 n_comp,
341 MemorySpace,
342 /*writable=*/false>;
343
350
351 /*
352 * A selected component: its offset, and for a parabolic component or
353 * an additional vector a view of the corresponding scalar vector.
354 */
355 struct Entry {
356 unsigned int offset;
357 ScalarVectorView scalar_view;
358 };
359
365 const OfflineData<dim, Number> &offline_data,
366 const HyperbolicSystem &hyperbolic_system);
367
368 const OfflineData<dim, Number> *offline_data_;
369
370 /* Record which vectors have views set up: */
371 bool read_conserved_ = false;
372 bool read_primitive_ = false;
373 bool read_precomputed_ = false;
374 bool read_initial_ = false;
375
377
378 HyperbolicVectorView U_view_;
379 PrecomputedVectorView precomputed_view_;
380 InitialPrecomputedVectorView initial_view_;
381
382 /*
383 * One entry per selected component, in the order in which the
384 * components have been selected.
385 */
386 Kokkos::View<const Entry *, typename MemorySpace::kokkos_space> entries_;
387
388 friend class SelectedComponentsExtractor<Description, dim, Number>;
389
391 };
392
393
394#ifndef DOXYGEN
395 /*
396 * -------------------------------------------------------------------------
397 * Inline function definitions
398 * -------------------------------------------------------------------------
399 */
400
401
402 template <typename Description, int dim, typename Number>
405 const OfflineData<dim, Number> &offline_data,
406 const HyperbolicSystem &hyperbolic_system,
407 const Interface::ParabolicSystem &parabolic_system,
408 const MultiComponentVector<Number> &initial_precomputed,
409 const std::vector<std::string> &additional_names,
410 const std::vector<std::reference_wrapper<const ScalarVector<Number>>>
411 &additional_vectors)
412 : offline_data_(&offline_data)
413 , hyperbolic_system_(&hyperbolic_system)
414 , parabolic_system_(&parabolic_system)
415 , initial_precomputed_(initial_precomputed)
416 , additional_names_(additional_names)
417 , additional_vectors_(additional_vectors)
418 , additional_offset_(parabolic_offset +
419 parabolic_system.state_names(dim).size())
420 {
421 Assert(additional_names_.size() == additional_vectors_.size(),
422 dealii::ExcMessage("The number of additional component names does "
423 "not match the number of additional vectors."));
424 }
425
426
427 template <typename Description, int dim, typename Number>
429 const std::vector<std::string> &selected)
430 {
431 std::vector<unsigned int> selection;
432 selection.reserve(selected.size());
433
434 for (const auto &entry : selected) {
435 const auto search = [&](const auto &names, const unsigned int offset) {
436 const auto pos = std::find(std::begin(names), std::end(names), entry);
437 if (pos == std::end(names))
438 return false;
439 const unsigned int index = std::distance(std::begin(names), pos);
440 selection.push_back(offset + index);
441 return true;
442 };
443
444 const bool found =
445 search(hyperbolic_system_->state_names(dim), conserved_offset) ||
446 search(hyperbolic_system_->primitive_names(dim), primitive_offset) ||
447 search(hyperbolic_system_->precomputed_names(dim),
448 precomputed_offset) ||
449 search(hyperbolic_system_->initial_precomputed_names(dim),
450 initial_offset) ||
451 search(parabolic_system_->state_names(dim), parabolic_offset) ||
452 search(additional_names_, additional_offset_);
453
454 AssertThrow(found,
455 dealii::ExcMessage(
456 "Invalid component name: \"" + entry +
457 "\" is not a valid conserved, primitive, precomputed, "
458 "initial, parabolic, or additional component name."));
459 }
460
461 /*
462 * Record which sections of the linear range of offsets have selected
463 * components:
464 */
465
466 const auto selects = [&](const unsigned int begin, const unsigned int end) {
467 return std::any_of(
468 selection.begin(), selection.end(), [&](const auto offset) {
469 return begin <= offset && offset < end;
470 });
471 };
472
473 read_conserved_ = selects(conserved_offset, primitive_offset);
474 read_primitive_ = selects(primitive_offset, precomputed_offset);
475 read_precomputed_ = selects(precomputed_offset, initial_offset);
476 read_initial_ = selects(initial_offset, parabolic_offset);
477
478 selection_ = std::move(selection);
479
480 /* Invalidate all views: */
481 view_ = std::monostate{};
482 }
483
484
485 template <typename Description, int dim, typename Number>
486 template <typename MemorySpace>
487 void
489 const StateVector<Number> &state_vector) const
490 {
491 ExtractorView<MemorySpace> view(*offline_data_, *hyperbolic_system_);
492
493 view.read_conserved_ = read_conserved_;
494 view.read_primitive_ = read_primitive_;
495 view.read_precomputed_ = read_precomputed_;
496 view.read_initial_ = read_initial_;
497
498 /*
499 * Only set up views for vectors that we are actually reading from,
500 * and only ensure residency of those parts of the state vector:
501 */
502
503 if (read_conserved_ || read_primitive_) {
504 copy_to_memory_space<MemorySpace>(state_vector,
506 view.U_view_ =
507 state_vector.U.template view<View::problem_dimension, MemorySpace>();
508 }
509
510 if (read_precomputed_) {
511 copy_to_memory_space<MemorySpace>(state_vector,
513 view.precomputed_view_ =
514 state_vector.precomputed
515 .template view<View::n_precomputed_values, MemorySpace>();
516 }
517
518 if (read_initial_) {
519 copy_to_memory_space<MemorySpace>(initial_precomputed_);
520 view.initial_view_ =
521 initial_precomputed_
522 .template view<View::n_initial_precomputed_values, MemorySpace>();
523 }
524
525 /*
526 * Set up an entry for every selected component on the host, with a
527 * view for every selected parabolic component and additional vector,
528 * and copy them into an array residing on the memory space of the view:
529 */
530
531 Kokkos::View<typename ExtractorView<MemorySpace>::Entry *,
532 Kokkos::HostSpace>
533 entries("selected_components_entries", n_selected());
534
535 const auto &parabolic = state_vector.V;
536
537 for (unsigned int k = 0; k < n_selected(); ++k) {
538 const auto offset = selection_[k];
539 entries[k].offset = offset;
540
541 if (offset < parabolic_offset)
542 continue;
543
544 const auto &vector =
545 offset < additional_offset_
546 ? parabolic[offset - parabolic_offset]
547 : additional_vectors_[offset - additional_offset_].get();
548
549 copy_to_memory_space<MemorySpace>(vector);
550 entries[k].scalar_view = vector.template view<1, MemorySpace>();
551 }
552
553 view.entries_ = Kokkos::create_mirror_view_and_copy(
554 typename MemorySpace::kokkos_space{}, entries);
555
556 /* Discard all information stored for a previous extraction: */
557 view_.template emplace<ExtractorView<MemorySpace>>(std::move(view));
558 }
559
560
561 template <typename Description, int dim, typename Number>
562 unsigned int
564 {
565 return static_cast<unsigned int>(selection_.size());
566 }
567
568
569 template <typename Description, int dim, typename Number>
570 template <typename MemorySpace>
571 SelectedComponentsExtractorView<Description, dim, Number, MemorySpace>
573 {
574 Assert(std::holds_alternative<ExtractorView<MemorySpace>>(view_),
575 dealii::ExcMessage(
576 "Invalid state: prepare_extraction() has to be called for the "
577 "selected memory space before a view can be created."));
578
579 return std::get<ExtractorView<MemorySpace>>(view_);
580 }
581
582
583 template <typename Description,
584 int dim,
585 typename Number,
586 typename MemorySpace>
587 SelectedComponentsExtractorView<Description, dim, Number, MemorySpace>::
588 SelectedComponentsExtractorView(
589 const OfflineData<dim, Number> &offline_data,
590 const HyperbolicSystem &hyperbolic_system)
591 : offline_data_(&offline_data)
592 , system_views_(hyperbolic_system)
593 {
594 }
595
596
597 template <typename Description,
598 int dim,
599 typename Number,
600 typename MemorySpace>
602 extract() const -> std::vector<ScalarVector>
603 {
604 const auto &offline_data = *offline_data_;
605
606 /*
607 * Set up all destination vectors and an array of pointers to their
608 * data residing on the memory space of the view:
609 */
610
611 std::vector<ScalarVector> extracted_components(n_selected());
612
613 struct Destination {
614 Number *data;
615 };
616
617 Kokkos::View<Destination *, Kokkos::HostSpace> host_destinations(
618 "selected_components_destinations", n_selected());
619
620 for (unsigned int k = 0; k < n_selected(); ++k) {
621 extracted_components[k].reinit(offline_data.scalar_partitioner());
622 host_destinations[k].data = extracted_components[k].begin();
623 }
624
625 const auto destinations = Kokkos::create_mirror_view_and_copy(
626 typename MemorySpace::kokkos_space{}, host_destinations);
627
628 /*
629 * Extract all selected components in a single sweep:
630 */
631
632 const auto view = *this;
633
634 const auto body = [=](auto sentinel, unsigned int i) {
635 using T = decltype(sentinel);
636
637 view.template extract_element<T>(
638 i, [&](const unsigned int k, const T &value) {
639 if constexpr (std::is_same_v<T, dealii::VectorizedArray<Number>>)
640 value.store(destinations[k].data + i);
641 else
642 destinations[k].data[i] = value;
643 });
644 };
645
646 loop<MemorySpace, Number>("extract_selected_components",
647 body,
648 0,
649 offline_data.n_locally_internal(),
650 offline_data.n_locally_owned());
651
652 for (auto &it : extracted_components)
653 it.update_ghost_values();
654
655 return extracted_components;
656 }
657
658
659 template <typename Description,
660 int dim,
661 typename Number,
662 typename MemorySpace>
663 template <typename T, typename Writer>
664 DEAL_II_HOST_DEVICE_ALWAYS_INLINE void
666 extract_element(const unsigned int i, const Writer &write) const
667 {
668 /*
669 * Read all involved tensors once. Reading a single component out of a
670 * MultiComponentVector is not any cheaper than reading the full
671 * tensor...
672 */
673
674 T staging[Extractor::parabolic_offset];
675
676 if (read_conserved_ || read_primitive_) {
677 const auto U_i = U_view_.template read_tensor<T>(i);
678 for (unsigned int d = 0; d < EquationView::problem_dimension; ++d)
679 staging[Extractor::conserved_offset + d] = U_i[d];
680
681 if (read_primitive_) {
682 const auto primitive_i =
683 system_views_.template view<T>().to_primitive_state(U_i);
684 for (unsigned int d = 0; d < EquationView::problem_dimension; ++d)
685 staging[Extractor::primitive_offset + d] = primitive_i[d];
686 }
687 }
688
689 if (read_precomputed_) {
690 const auto precomputed_i = precomputed_view_.template read_tensor<T>(i);
691 for (unsigned int d = 0; d < EquationView::n_precomputed_values; ++d)
692 staging[Extractor::precomputed_offset + d] = precomputed_i[d];
693 }
694
695 if (read_initial_) {
696 const auto initial_i = initial_view_.template read_tensor<T>(i);
697 for (unsigned int d = 0; d < EquationView::n_initial_precomputed_values;
698 ++d)
699 staging[Extractor::initial_offset + d] = initial_i[d];
700 }
701
702 /*
703 * Parabolic components and additional vectors are read out of the
704 * corresponding scalar vector directly:
705 */
706
707 for (unsigned int k = 0; k < n_selected(); ++k) {
708 const auto &entry = entries_[k];
709
710 if (entry.offset < Extractor::parabolic_offset)
711 write(k, staging[entry.offset]);
712 else
713 write(k, entry.scalar_view.template read_entry<T>(i));
714 }
715 }
716
717#endif
718} // namespace ryujin
std::vector< ScalarVector > extract() const
DEAL_II_HOST_DEVICE_ALWAYS_INLINE void extract_element(unsigned int i, const Writer &write) const
DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int n_selected() const
dealii::LinearAlgebra::distributed::Vector< Number, MemorySpace > ScalarVector
typename Description::HyperbolicSystem HyperbolicSystem
typename HyperbolicSystem::template View< dim, Number > View
SelectedComponentsExtractorView< Description, dim, Number, MemorySpace > view() const
void prepare(const std::vector< std::string > &selected)
SelectedComponentsExtractor(const OfflineData< dim, Number > &offline_data, const HyperbolicSystem &hyperbolic_system, const Interface::ParabolicSystem &parabolic_system, const MultiComponentVector< Number > &initial_precomputed, const std::vector< std::string > &additional_names={}, const std::vector< std::reference_wrapper< const ScalarVector< Number > > > &additional_vectors={})
void prepare_extraction(const StateVector< Number > &state_vector) const
MultiComponentVector< Number > ScalarVector