ryujin 2.1.1 revision 71cdc42292164f8095c0bd62c4ea75ebcfac58fa
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 "gpu.h"
11#include "loop.h"
12#include "observer_pointer.h"
13#include "offline_data.h"
14#include "state_vector.h"
15
16#include <algorithm>
17#include <functional>
18#include <string>
19#include <tuple>
20#include <utility>
21#include <vector>
22
23namespace ryujin
24{
25 template <typename Description,
26 int dim,
27 typename Number,
28 typename MemorySpace>
29 class SelectedComponentsExtractorView;
30
78 template <typename Description, int dim, typename Number>
80 {
81 public:
86
89
90 using View = typename HyperbolicSystem::template View<dim, Number>;
91
92 using StateVector = typename View::StateVector;
93 using InitialPrecomputedVector = typename View::InitialPrecomputedVector;
94
96
97 using HyperbolicVector = std::tuple_element_t<0, StateVector>;
98 using PrecomputedVector = std::tuple_element_t<1, StateVector>;
99 using ParabolicVector = std::tuple_element_t<2, StateVector>;
100
101 template <typename MemorySpace>
103 decltype(std::declval<const HyperbolicVector &>()
104 .template view<MemorySpace>());
105 template <typename MemorySpace>
107 decltype(std::declval<const PrecomputedVector &>()
108 .template view<MemorySpace>());
109 template <typename MemorySpace>
111 decltype(std::declval<const InitialPrecomputedVector &>()
112 .template view<MemorySpace>());
113 template <typename MemorySpace>
114 using ScalarVectorView = decltype(std::declval<const ScalarVector &>()
115 .template view<MemorySpace>());
116
117 /*
118 *
119 * A selected component is identified by an offset into the linear
120 * range formed by concatenating all conserved, primitive, precomputed,
121 * initial-precomputed, parabolic, and additional components (in this
122 * order). The following constants mark the beginning of the respective
123 * sections. The parabolic section has a run time size, the offset of
124 * the additional section is thus stored in additional_offset_.
125 */
126
127 static constexpr unsigned int conserved_offset = 0;
128 static constexpr unsigned int primitive_offset = View::problem_dimension;
129 static constexpr unsigned int precomputed_offset =
130 2 * View::problem_dimension;
131 static constexpr unsigned int initial_offset =
132 precomputed_offset + View::n_precomputed_values;
133 static constexpr unsigned int parabolic_offset =
134 initial_offset + View::n_initial_precomputed_values;
135
137
141
151 const OfflineData<dim, Number> &offline_data,
152 const HyperbolicSystem &hyperbolic_system,
153 const ParabolicSystem &parabolic_system,
154 const InitialPrecomputedVector &initial_precomputed,
155 const std::vector<std::string> &additional_names = {},
156 const std::vector<std::reference_wrapper<const ScalarVector>>
157 &additional_vectors = {});
158
167 void prepare(const std::vector<std::string> &selected);
168
184 template <typename MemorySpace = dealii::MemorySpace::Host>
185 void prepare_extraction(const StateVector &state_vector) const;
186
188
192
198 std::size_t n_selected() const;
199
201
205
211 template <typename MemorySpace = dealii::MemorySpace::Host>
213 view() const;
214
215 private:
217
221
222 dealii::ObserverPointer<const OfflineData<dim, Number>> offline_data_;
223 dealii::ObserverPointer<const HyperbolicSystem> hyperbolic_system_;
224 dealii::ObserverPointer<const ParabolicSystem> parabolic_system_;
225
226 const InitialPrecomputedVector &initial_precomputed_;
227
228 const std::vector<std::string> additional_names_;
229 const std::vector<std::reference_wrapper<const ScalarVector>>
230 additional_vectors_;
231
235 const unsigned int additional_offset_;
236
241 const unsigned int n_scalar_;
242
243 /*
244 * Index bookkeeping that is independent of the state vector and the
245 * memory space. All of the following is set up by prepare():
246 */
247
254 Mirrored<unsigned int *> selection_{"selected_components_selection"};
255
259 unsigned int n_selected_ = 0;
260
266 bool read_conserved_ = false;
267 bool read_primitive_ = false;
268 bool read_precomputed_ = false;
269 bool read_initial_ = false;
270 bool read_scalar_ = false;
271
276 template <typename MemorySpace>
277 struct Payload {
278 const unsigned int *selection_ = nullptr;
279
280 HyperbolicVectorView<MemorySpace> U_view_;
281 PrecomputedVectorView<MemorySpace> precomputed_view_;
282 InitialPrecomputedVectorView<MemorySpace> initial_view_;
283
284 /*
285 * One view per parabolic component and additional vector, indexed by
286 * `offset - parabolic_offset`: the parabolic components come first,
287 * the additional vectors last. Only the views of selected components
288 * are populated. The array is mirrored between the host and device
289 * memory spaces so that it can be captured in a computation loop.
290 */
291 Mirrored<ScalarVectorView<MemorySpace> *> scalar_views_storage_{
292 "selected_components_scalar_views"};
293 const ScalarVectorView<MemorySpace> *scalar_views_ = nullptr;
294
295 bool prepared_ = false;
296 };
297
298 mutable Payload<dealii::MemorySpace::Host> host_payload_;
299 mutable Payload<dealii::MemorySpace::Default> default_payload_;
300
304 template <typename MemorySpace>
305 Payload<MemorySpace> &payload() const;
306
307 template <typename, int, typename, typename>
309
311 };
312
313
326 template <typename Description,
327 int dim,
328 typename Number,
329 typename MemorySpace>
331 {
332 public:
333 static_assert(std::is_same_v<MemorySpace, dealii::MemorySpace::Host> ||
334 std::is_same_v<MemorySpace, dealii::MemorySpace::Default>,
335 "Unexpected memory space");
336
341
343
346
348
349 static constexpr auto problem_dimension = EquationView::problem_dimension;
350 static constexpr auto n_precomputed_values =
351 EquationView::n_precomputed_values;
352 static constexpr auto n_initial_precomputed_values =
353 EquationView::n_initial_precomputed_values;
354
356 typename Extractor::template HyperbolicVectorView<MemorySpace>;
358 typename Extractor::template PrecomputedVectorView<MemorySpace>;
360 typename Extractor::template InitialPrecomputedVectorView<MemorySpace>;
362 typename Extractor::template ScalarVectorView<MemorySpace>;
363
370 dealii::LinearAlgebra::distributed::Vector<Number, MemorySpace>;
371
373
377
385 std::vector<ScalarVector> extract() const;
386
391 DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int n_selected() const
392 {
393 return n_selected_;
394 }
395
413 template <typename T = Number>
414 DEAL_II_HOST_DEVICE_ALWAYS_INLINE void
415 extract_element(T *result, unsigned int i) const;
416
417 private:
419
423
424 using Payload = typename Extractor::template Payload<MemorySpace>;
425
430 const Payload &payload);
431
432 const Extractor *extractor_;
433
434 const unsigned int *selection_;
435 unsigned int n_selected_;
436
437 /* Record which vectors have views set up: */
438 bool read_conserved_;
439 bool read_primitive_;
440 bool read_precomputed_;
441 bool read_initial_;
442
443 HyperbolicVectorView U_view_;
444 PrecomputedVectorView precomputed_view_;
445 InitialPrecomputedVectorView initial_view_;
446
448
449 /*
450 * One view per parabolic component and additional vector, see the
451 * documentation of SelectedComponentsExtractor::Payload.
452 */
453 const ScalarVectorView *scalar_views_;
454
455 friend class SelectedComponentsExtractor<Description, dim, Number>;
456
458 };
459
460
461#ifndef DOXYGEN
462 /*
463 * -------------------------------------------------------------------------
464 * Inline function definitions
465 * -------------------------------------------------------------------------
466 */
467
468
469 template <typename Description, int dim, typename Number>
472 const OfflineData<dim, Number> &offline_data,
473 const HyperbolicSystem &hyperbolic_system,
474 const ParabolicSystem &parabolic_system,
475 const InitialPrecomputedVector &initial_precomputed,
476 const std::vector<std::string> &additional_names,
477 const std::vector<std::reference_wrapper<const ScalarVector>>
478 &additional_vectors)
479 : offline_data_(&offline_data)
480 , hyperbolic_system_(&hyperbolic_system)
481 , parabolic_system_(&parabolic_system)
482 , initial_precomputed_(initial_precomputed)
483 , additional_names_(additional_names)
484 , additional_vectors_(additional_vectors)
485 , additional_offset_(parabolic_offset +
486 parabolic_system.parabolic_component_names().size())
487 , n_scalar_(additional_offset_ - parabolic_offset +
488 additional_vectors.size())
489 {
490 Assert(additional_names_.size() == additional_vectors_.size(),
491 dealii::ExcMessage("The number of additional component names does "
492 "not match the number of additional vectors."));
493 }
494
495
496 template <typename Description, int dim, typename Number>
498 const std::vector<std::string> &selected)
499 {
500 std::vector<unsigned int> selection;
501 selection.reserve(selected.size());
502
503 for (const auto &entry : selected) {
504 const auto search = [&](const auto &names, const unsigned int offset) {
505 const auto pos = std::find(std::begin(names), std::end(names), entry);
506 if (pos == std::end(names))
507 return false;
508 const unsigned int index = std::distance(std::begin(names), pos);
509 selection.push_back(offset + index);
510 return true;
511 };
512
513 const bool found =
514 search(View::component_names, conserved_offset) ||
515 search(View::primitive_component_names, primitive_offset) ||
516 search(View::precomputed_names, precomputed_offset) ||
517 search(View::initial_precomputed_names, initial_offset) ||
518 search(parabolic_system_->parabolic_component_names(),
519 parabolic_offset) ||
520 search(additional_names_, additional_offset_);
521
522 AssertThrow(found,
523 dealii::ExcMessage(
524 "Invalid component name: \"" + entry +
525 "\" is not a valid conserved, primitive, precomputed, "
526 "initial, parabolic, or additional component name."));
527 }
528
529 n_selected_ = static_cast<unsigned int>(selection.size());
530
531 /*
532 * Record which sections of the linear range of offsets have selected
533 * components:
534 */
535
536 const auto selects = [&](const unsigned int begin, const unsigned int end) {
537 return std::any_of(
538 selection.begin(), selection.end(), [&](const auto offset) {
539 return begin <= offset && offset < end;
540 });
541 };
542
543 read_conserved_ = selects(conserved_offset, primitive_offset);
544 read_primitive_ = selects(primitive_offset, precomputed_offset);
545 read_precomputed_ = selects(precomputed_offset, initial_offset);
546 read_initial_ = selects(initial_offset, parabolic_offset);
547 read_scalar_ = selects(parabolic_offset, parabolic_offset + n_scalar_);
548
549 selection_.reinit(selection.size(), TransferPolicy::implicit_transfers);
550 std::copy(selection.begin(), selection.end(), selection_.view());
551
552 /*
553 * (Re)size the scalar view arrays and invalidate all views that we have
554 * handed out so far:
555 */
556
557 const std::size_t size = read_scalar_ ? n_scalar_ : 0;
558 host_payload_.scalar_views_storage_.reinit(
560 default_payload_.scalar_views_storage_.reinit(
562
563 host_payload_.prepared_ = false;
564 default_payload_.prepared_ = false;
565 }
566
567
568 template <typename Description, int dim, typename Number>
569 template <typename MemorySpace>
570 void
572 const StateVector &state_vector) const
573 {
574 using HostSpace = dealii::MemorySpace::Host;
575
576 auto &payload = this->template payload<MemorySpace>();
577
578 payload.selection_ = selection_.template view<MemorySpace>();
579
580 /*
581 * Only set up views for vectors that we are actually reading from:
582 * creating a view triggers a residency assertion (or an implicit memory
583 * transfer).
584 */
585
586 if (read_conserved_ || read_primitive_)
587 payload.U_view_ = std::get<0>(state_vector).template view<MemorySpace>();
588
589 if (read_precomputed_)
590 payload.precomputed_view_ =
591 std::get<1>(state_vector).template view<MemorySpace>();
592
593 if (read_initial_)
594 payload.initial_view_ = initial_precomputed_.template view<MemorySpace>();
595
596 /*
597 * Create a view for every selected parabolic component and additional
598 * vector and store them in an array residing on the memory space of the
599 * view:
600 */
601
602 if (read_scalar_) {
603 auto *scalar_views = payload.scalar_views_storage_.view();
604
605 /* We iterate over the selection on the host: */
606 const auto *selection = selection_.template view<HostSpace>();
607 const auto &parabolic = std::get<2>(state_vector);
608
609 for (unsigned int k = 0; k < n_selected_; ++k) {
610 const auto offset = selection[k];
611 if (offset < parabolic_offset)
612 continue;
613
614 const auto component = offset - parabolic_offset;
615 if (offset < additional_offset_)
616 scalar_views[component] =
617 parabolic[component].template view<MemorySpace>();
618 else
619 scalar_views[component] =
620 additional_vectors_[offset - additional_offset_]
621 .get()
622 .template view<MemorySpace>();
623 }
624
625 payload.scalar_views_ = std::as_const(payload.scalar_views_storage_)
626 .template view<MemorySpace>();
627 }
628
629 payload.prepared_ = true;
630 }
631
632
633 template <typename Description, int dim, typename Number>
634 std::size_t
636 {
637 return n_selected_;
638 }
639
640
641 template <typename Description, int dim, typename Number>
642 template <typename MemorySpace>
643 SelectedComponentsExtractorView<Description, dim, Number, MemorySpace>
645 {
646 Assert(this->template payload<MemorySpace>().prepared_,
647 dealii::ExcMessage(
648 "Invalid state: prepare_extraction() has to be called for the "
649 "selected memory space before a view can be created."));
650
651 return SelectedComponentsExtractorView<Description,
652 dim,
653 Number,
654 MemorySpace>(
655 *this, this->template payload<MemorySpace>());
656 }
657
658
659 template <typename Description, int dim, typename Number>
660 template <typename MemorySpace>
661 auto SelectedComponentsExtractor<Description, dim, Number>::payload() const
662 -> Payload<MemorySpace> &
663 {
664 static_assert(std::is_same_v<MemorySpace, dealii::MemorySpace::Host> ||
665 std::is_same_v<MemorySpace, dealii::MemorySpace::Default>,
666 "Unexpected memory space");
667
668 if constexpr (std::is_same_v<MemorySpace, dealii::MemorySpace::Host>)
669 return host_payload_;
670 else
671 return default_payload_;
672 }
673
674
675 template <typename Description,
676 int dim,
677 typename Number,
678 typename MemorySpace>
679 SelectedComponentsExtractorView<Description, dim, Number, MemorySpace>::
680 SelectedComponentsExtractorView(const Extractor &extractor,
681 const Payload &payload)
682 : extractor_(&extractor)
683 , selection_(payload.selection_)
684 , n_selected_(extractor.n_selected_)
685 , read_conserved_(extractor.read_conserved_)
686 , read_primitive_(extractor.read_primitive_)
687 , read_precomputed_(extractor.read_precomputed_)
688 , read_initial_(extractor.read_initial_)
689 , U_view_(payload.U_view_)
690 , precomputed_view_(payload.precomputed_view_)
691 , initial_view_(payload.initial_view_)
692 , system_views_(*extractor.hyperbolic_system_)
693 , scalar_views_(payload.scalar_views_)
694 {
695 }
696
697
698 template <typename Description,
699 int dim,
700 typename Number,
701 typename MemorySpace>
703 extract() const -> std::vector<ScalarVector>
704 {
705 using HostSpace = dealii::MemorySpace::Host;
706
707 const auto &offline_data = *extractor_->offline_data_;
708 const auto &scalar_partitioner = offline_data.scalar_partitioner();
709
710 /* We iterate over the selection on the host: */
711 const auto *selection = extractor_->selection_.template view<HostSpace>();
712
713 std::vector<ScalarVector> extracted_components(n_selected_);
714 for (auto &it : extracted_components)
715 it.reinit(scalar_partitioner);
716
717 for (unsigned int k = 0; k < n_selected_; ++k) {
718 const auto offset = selection[k];
719 auto &destination = extracted_components[k];
720
721 if (offset < Extractor::primitive_offset) {
722 U_view_.extract_component(destination,
723 offset - Extractor::conserved_offset);
724
725 } else if (offset < Extractor::precomputed_offset) {
726 /*
727 * Primitive components are computed from the conserved state:
728 */
729
730 const auto U_view = U_view_;
731 const auto system_views = system_views_;
732 const auto component = offset - Extractor::primitive_offset;
733 auto *data = destination.begin();
734
735 const auto body = [=](auto sentinel, unsigned int i) {
736 using T = decltype(sentinel);
737
738 const auto U_i = U_view.template read_tensor<T>(i);
739 const auto primitive_i =
740 system_views.template view<T>().to_primitive_state(U_i);
741
742 if constexpr (std::is_same_v<T, dealii::VectorizedArray<Number>>)
743 primitive_i[component].store(data + i);
744 else
745 data[i] = primitive_i[component];
746 };
747
748 loop<MemorySpace, Number>("extract_primitive_component",
749 body,
750 0,
751 offline_data.n_locally_internal(),
752 offline_data.n_locally_owned());
753
754 } else if (offset < Extractor::initial_offset) {
755 precomputed_view_.extract_component(
756 destination, offset - Extractor::precomputed_offset);
757
758 } else if (offset < Extractor::parabolic_offset) {
759 initial_view_.extract_component(destination,
760 offset - Extractor::initial_offset);
761
762 } else {
763 scalar_views_[offset - Extractor::parabolic_offset].extract_component(
764 destination, 0);
765 }
766 }
767
768 return extracted_components;
769 }
770
771
772 template <typename Description,
773 int dim,
774 typename Number,
775 typename MemorySpace>
776 template <typename T>
777 DEAL_II_HOST_DEVICE_ALWAYS_INLINE void
779 extract_element(T *result, const unsigned int i) const
780 {
781 /*
782 * Read all involved tensors once. Reading a single component out of a
783 * MultiComponentVector is not any cheaper than reading the full
784 * tensor...
785 */
786
787 T staging[Extractor::parabolic_offset];
788
789 if (read_conserved_ || read_primitive_) {
790 const auto U_i = U_view_.template read_tensor<T>(i);
791 for (unsigned int d = 0; d < problem_dimension; ++d)
792 staging[Extractor::conserved_offset + d] = U_i[d];
793
794 if (read_primitive_) {
795 const auto primitive_i =
796 system_views_.template view<T>().to_primitive_state(U_i);
797 for (unsigned int d = 0; d < problem_dimension; ++d)
798 staging[Extractor::primitive_offset + d] = primitive_i[d];
799 }
800 }
801
802 if (read_precomputed_) {
803 const auto precomputed_i = precomputed_view_.template read_tensor<T>(i);
804 for (unsigned int d = 0; d < n_precomputed_values; ++d)
805 staging[Extractor::precomputed_offset + d] = precomputed_i[d];
806 }
807
808 if (read_initial_) {
809 const auto initial_i = initial_view_.template read_tensor<T>(i);
810 for (unsigned int d = 0; d < n_initial_precomputed_values; ++d)
811 staging[Extractor::initial_offset + d] = initial_i[d];
812 }
813
814 /*
815 * Parabolic components and additional vectors are read out of the
816 * corresponding scalar vector directly:
817 */
818
819 for (unsigned int k = 0; k < n_selected_; ++k) {
820 const auto offset = selection_[k];
821
822 if (offset < Extractor::parabolic_offset)
823 result[k] = staging[offset];
824 else
825 result[k] = scalar_views_[offset - Extractor::parabolic_offset]
826 .template read_entry<T>(i);
827 }
828 }
829
830#endif
831} // namespace ryujin
typename Extractor::HyperbolicSystem HyperbolicSystem
typename Extractor::template PrecomputedVectorView< MemorySpace > PrecomputedVectorView
std::vector< ScalarVector > extract() const
DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int n_selected() const
dealii::LinearAlgebra::distributed::Vector< Number, MemorySpace > ScalarVector
typename Extractor::template InitialPrecomputedVectorView< MemorySpace > InitialPrecomputedVectorView
typename Extractor::template ScalarVectorView< MemorySpace > ScalarVectorView
typename Extractor::template HyperbolicVectorView< MemorySpace > HyperbolicVectorView
DEAL_II_HOST_DEVICE_ALWAYS_INLINE void extract_element(T *result, unsigned int i) const
typename Description::ParabolicSystem ParabolicSystem
std::tuple_element_t< 2, StateVector > ParabolicVector
typename Description::HyperbolicSystem HyperbolicSystem
SelectedComponentsExtractor(const OfflineData< dim, Number > &offline_data, const HyperbolicSystem &hyperbolic_system, const ParabolicSystem &parabolic_system, const InitialPrecomputedVector &initial_precomputed, const std::vector< std::string > &additional_names={}, const std::vector< std::reference_wrapper< const ScalarVector > > &additional_vectors={})
typename HyperbolicSystem::template View< dim, Number > View
SelectedComponentsExtractorView< Description, dim, Number, MemorySpace > view() const
void prepare_extraction(const StateVector &state_vector) const
void prepare(const std::vector< std::string > &selected)
std::tuple_element_t< 1, StateVector > PrecomputedVector
typename View::InitialPrecomputedVector InitialPrecomputedVector
std::tuple_element_t< 0, StateVector > HyperbolicVector
Euler::Description Description
Euler::HyperbolicSystem HyperbolicSystem
Definition description.h:34
ryujin::StubParabolicSystem ParabolicSystem
Definition description.h:36