ryujin 2.1.1 revision b04fb4aba7bd97ddfa5994a6b4b6e8586eb15db2
Loading...
Searching...
No Matches
hyperbolic_system.h
Go to the documentation of this file.
1//
2// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3// Copyright (C) 2023 - 2026 by the ryujin authors
4//
5
6#pragma once
7
8#include <compile_time_options.h>
9
10#include <ryujin/interface/hyperbolic_system.h>
11
12#include "flux_library.h"
13
14#include <convenience_macros.h>
15#include <discretization.h>
16#include <gpu.h>
17#include <loop.h>
19#include <patterns_conversion.h>
20#include <simd.h>
21#include <state_vector.h>
22
23#include <deal.II/base/memory_space.h>
24#include <deal.II/base/parameter_acceptor.h>
25#include <deal.II/base/tensor.h>
26
27#include <array>
28
29namespace ryujin
30{
31 namespace ScalarConservation
32 {
33 template <int dim,
34 typename Number,
35 typename MemorySpace = dealii::MemorySpace::Host>
36 class HyperbolicSystemView;
37
44 class HyperbolicSystem final : public Interface::HyperbolicSystem
45 {
46 public:
54
58 HyperbolicSystem(const std::string &subsection = "/HyperbolicSystem");
59
65 template <int dim,
66 typename Number = double,
67 typename MemorySpace = dealii::MemorySpace::Host>
69
77 template <int dim,
78 typename Number,
79 typename MemorySpace = dealii::MemorySpace::Host>
80 auto view() const
81 {
83 }
84
98 template <typename MemorySpace = dealii::MemorySpace::Host,
99 int dim,
100 typename ScalarNumber>
102 const OfflineData<dim, ScalarNumber> &offline_data,
103 StateVector<ScalarNumber> &state_vector,
104 const bool skip_constrained_dofs = true) const;
105
106 private:
111
116 void update_parameters();
117
118 Mirrored<Parameters> parameters_;
119
120 std::string flux_;
121
123
124 using Flux = FluxLibrary::Flux;
125 std::shared_ptr<Flux> selected_flux_;
126
127 template <int, typename, typename>
129
131 }; /* HyperbolicSystem */
132
133
144 template <int dim, typename Number, typename MemorySpace>
146 {
147 public:
148 static_assert(
149 std::is_same_v<MemorySpace, dealii::MemorySpace::Host> ||
150 std::is_same_v<MemorySpace, dealii::MemorySpace::Default>,
151 "Unexpected memory space");
152
157
162
166 static constexpr unsigned int problem_dimension = 1;
167
171 using state_type = dealii::Tensor<1, problem_dimension, Number>;
172
176 using flux_type =
177 dealii::Tensor<1, problem_dimension, dealii::Tensor<1, dim, Number>>;
178
183
187 static constexpr unsigned int n_precomputed_values = 2 * dim;
188
192 using precomputed_type = std::array<Number, n_precomputed_values>;
193
197 static constexpr unsigned int n_initial_precomputed_values = 0;
198
203 std::array<Number, n_initial_precomputed_values>;
204
205 template <int n_comp>
208
214
221
223
227
232 HyperbolicSystemView(const HyperbolicSystem &hyperbolic_system)
233 : parameters_(
234 hyperbolic_system.parameters_.template view<MemorySpace>())
235 , flux_(hyperbolic_system.selected_flux_.get())
236 {
237 }
238
240
244
250 DEAL_II_ALWAYS_INLINE inline const std::string &flux() const
251 {
252 return flux_->name();
253 }
254
255 DEAL_II_HOST_DEVICE_ALWAYS_INLINE ScalarNumber
260
262
270
274 DEAL_II_ALWAYS_INLINE inline dealii::Tensor<1, dim, Number>
275 flux_function(const Number &u) const;
276
280 DEAL_II_ALWAYS_INLINE inline dealii::Tensor<1, dim, Number>
281 flux_gradient_function(const Number &u) const;
282
284
288
295 static DEAL_II_HOST_DEVICE Number state(const state_type &U);
296
303 DEAL_II_HOST_DEVICE Number square_entropy(const Number &u) const;
304
312 DEAL_II_HOST_DEVICE Number
313 square_entropy_derivative(const Number &u) const;
314
321 DEAL_II_HOST_DEVICE Number kruzkov_entropy(const Number &k,
322 const Number &u) const;
323
331 DEAL_II_HOST_DEVICE Number
332 kruzkov_entropy_derivative(const Number &k, const Number &u) const;
333
339 DEAL_II_HOST_DEVICE_ALWAYS_INLINE bool
340 is_admissible(const state_type & /*U*/) const
341 {
342 return true;
343 }
344
346
350
354 template <typename Lambda>
355 DEAL_II_HOST_DEVICE_ALWAYS_INLINE state_type
356 apply_boundary_conditions(const dealii::types::boundary_id id,
357 const state_type &U,
358 const dealii::Tensor<1, dim, Number> &normal,
359 const Lambda &get_dirichlet_data) const;
360
362
366
371 DEAL_II_HOST_DEVICE dealii::Tensor<1, dim, Number>
372 construct_flux_tensor(const precomputed_type &precomputed_state) const;
373
379 DEAL_II_HOST_DEVICE dealii::Tensor<1, dim, Number>
381 const precomputed_type &precomputed_state) const;
382
402 DEAL_II_HOST_DEVICE
405 const InitialPrecomputedVectorView & /*piv*/,
406 const unsigned int i,
407 const state_type & /*U_i*/) const;
408
409 DEAL_II_HOST_DEVICE
412 const InitialPrecomputedVectorView & /*piv*/,
413 const unsigned int *js,
414 const state_type & /*U_j*/) const;
415
420 DEAL_II_HOST_DEVICE
423 const flux_contribution_type &flux_j,
424 const dealii::Tensor<1, dim, Number> &c_ij) const;
425
427 static constexpr bool have_high_order_flux = false;
428
432 const dealii::Tensor<1, dim, Number> &c_ij) const = delete;
433
435
439
441 static constexpr bool have_source_terms = false;
442
444 const unsigned int i,
445 const state_type &U_i,
446 const ScalarNumber tau) const = delete;
447
449 const unsigned int *js,
450 const state_type &U_j,
451 const ScalarNumber tau) const = delete;
452
454
458
469 template <typename ST>
470 DEAL_II_HOST_DEVICE_ALWAYS_INLINE state_type
471 expand_state(const ST &state) const
472 {
473 return state;
474 }
475
480 DEAL_II_HOST_DEVICE_ALWAYS_INLINE state_type
481 from_primitive_state(const state_type &primitive_state) const
482 {
483 return primitive_state;
484 }
485
490 DEAL_II_HOST_DEVICE_ALWAYS_INLINE state_type
492 {
493 return state;
494 }
495
501 template <typename Lambda>
502 DEAL_II_HOST_DEVICE_ALWAYS_INLINE state_type apply_galilei_transform(
503 const state_type &state, const Lambda & /*lambda*/) const
504 {
505 return state;
506 }
507
508 private:
510
514
515 const HyperbolicSystem::Parameters *const parameters_;
516
517 /* Only valid (and dereferenced) on the host: */
518 const FluxLibrary::Flux *const flux_;
519
521 }; /* HyperbolicSystemView */
522
523
524 /*
525 * -------------------------------------------------------------------------
526 * Inline definitions
527 * -------------------------------------------------------------------------
528 */
529
530
531 inline HyperbolicSystem::HyperbolicSystem(const std::string &subsection)
532 : Interface::HyperbolicSystem(
533 "Scalar conservation equation",
534 {{{"u"}, {"u"}, {"u"}}},
535 {{{"u"}, {"u"}, {"u"}}},
536 {{{"f", "df"},
537 {"f_1", "f_2", "df_1", "df_2"},
538 {"f_1", "f_2", "f_3", "df_1", "df_2", "df_3"}}},
539 {},
540 subsection)
541 , parameters_("scalar_conservation_hyperbolic_system_parameters",
543 {
544 flux_ = "burgers";
545 add_parameter("flux",
546 flux_,
547 "The scalar flux. Valid names are given by any of the "
548 "subsections defined below");
549
550 /*
551 * And finally populate the flux list with all flux configurations
552 * defined in the FluxLibrary namespace:
553 */
554 FluxLibrary::populate_flux_list(flux_list_, subsection);
555
556 /*
557 * The flux classes update their parameters in their own
558 * parse_parameters_call_back signal, which is invoked after ours.
559 * Thus, also connect to theirs:
560 */
561 ParameterAcceptor::parse_parameters_call_back.connect(
562 [this] { update_parameters(); });
563 for (auto &it : flux_list_)
564 it->parse_parameters_call_back.connect([this] { update_parameters(); });
565
566 update_parameters();
567 }
568
569
570 inline void HyperbolicSystem::update_parameters()
571 {
572 bool initialized = false;
573 for (auto &it : flux_list_)
574
575 /* Populate flux functions: */
576 if (it->name() == flux_) {
577 selected_flux_ = it;
578 problem_name_ = "Scalar conservation equation (" + it->name() + ": " +
579 it->flux_formula() + ")";
580 initialized = true;
581 break;
582 }
583
584 AssertThrow(
585 initialized,
586 dealii::ExcMessage("Could not find a flux description with name \"" +
587 flux_ + "\""));
588
589 /* invalidates view on default memory space */
590 auto &parameters = *parameters_.view();
591
592 parameters.derivative_approximation_delta =
593 selected_flux_->derivative_approximation_delta();
594 }
595
596
597 template <typename MemorySpace, int dim, typename ScalarNumber>
599 const OfflineData<dim, ScalarNumber> &offline_data,
600 StateVector<ScalarNumber> &state_vector,
601 const bool skip_constrained_dofs) const
602 {
603 using HostSpace = dealii::MemorySpace::Host;
604
605 const unsigned int n_internal = offline_data.n_locally_internal();
606 const unsigned int n_owned = offline_data.n_locally_owned();
607
609
610 auto &U = state_vector.U;
611 auto &precomputed = state_vector.precomputed;
612
613 /*
614 * Compute the flux and its gradient. This requires calling into the
615 * selected flux, which is only possible on the host memory space.
616 * Thus, temporarily transfer the state vector and the precomputed
617 * values to the host memory space:
618 */
619
620 constexpr bool transfer =
622 !std::is_same_v<MemorySpace, dealii::MemorySpace::Host>;
623
624 [[maybe_unused]] const bool host_resident =
625 U.template is_resident<HostSpace>();
626
627 if constexpr (transfer) {
628 U.template copy_to_memory_space<HostSpace>();
629 precomputed.template move_to_memory_space<HostSpace>();
630 }
631
632 {
633 const auto sparsity_simd_view =
634 offline_data.sparsity_pattern_simd().template view<HostSpace>();
635
636 /* We only read the hyperbolic state vector: */
637 const auto U_view =
638 std::as_const(U)
639 .template view<ScalarView::problem_dimension, HostSpace>();
640 const auto precomputed_view =
642 .template view<ScalarView::n_precomputed_values, HostSpace>();
643
644 const auto hyperbolic_system_views =
645 make_select_view<dim, ScalarNumber, HostSpace>(*this);
646
647 const auto body = [=](auto sentinel, unsigned int i) {
648 using T = decltype(sentinel);
650 using precomputed_type = typename View::precomputed_type;
651
652 const unsigned int row_length = sparsity_simd_view.row_length(i);
653 if (skip_constrained_dofs && row_length == 1)
654 return;
655
656 const auto view = hyperbolic_system_views.template view<T>();
657
658 const auto U_i = U_view.template read_tensor<T>(i);
659 const auto u_i = view.state(U_i);
660
661 /* Calls into the selected flux: */
662 const auto f_i = view.flux_function(u_i);
663 const auto df_i = view.flux_gradient_function(u_i);
664
665 precomputed_type prec_i;
666 for (unsigned int k = 0; k < View::n_precomputed_values / 2; ++k) {
667 prec_i[k] = f_i[k];
668 prec_i[dim + k] = df_i[k];
669 }
670 precomputed_view.template write_tensor<T>(prec_i, i);
671 };
672
673 loop<HostSpace, ScalarNumber>(
674 "hyperbolic_kernel_01b", body, 0, n_internal, n_owned);
675 }
676
677 if constexpr (transfer) {
678 /* Drop the (now stale) host mirror unless it was resident before: */
679 if (!host_resident)
680 U.template move_to_memory_space<MemorySpace>();
681 precomputed.template move_to_memory_space<MemorySpace>();
682 }
683 }
684
685
686 template <int dim, typename Number, typename MemorySpace>
687 DEAL_II_ALWAYS_INLINE inline dealii::Tensor<1, dim, Number>
689 const Number &u) const
690 {
691 const auto &flux = flux_;
692 dealii::Tensor<1, dim, Number> result;
693
694 /* This access by calling into value() repeatedly is terrible: */
695 for (unsigned int k = 0; k < dim; ++k) {
696 if constexpr (std::is_same_v<ScalarNumber, Number>) {
697 result[k] = flux->value(u, k);
698 } else {
699 for (unsigned int s = 0; s < Number::size(); ++s) {
700 result[k][s] = flux->value(u[s], k);
701 }
702 }
703 }
704
705 return result;
706 }
707
708 template <int dim, typename Number, typename MemorySpace>
709 DEAL_II_ALWAYS_INLINE inline dealii::Tensor<1, dim, Number>
711 const Number &u) const
712 {
713 const auto &flux = flux_;
714 dealii::Tensor<1, dim, Number> result;
715
716 /* This access by calling into value() repeatedly is terrible: */
717 for (unsigned int k = 0; k < dim; ++k) {
718 if constexpr (std::is_same_v<ScalarNumber, Number>) {
719 result[k] = flux->gradient(u, k);
720 } else {
721 for (unsigned int s = 0; s < Number::size(); ++s) {
722 result[k][s] = flux->gradient(u[s], k);
723 }
724 }
725 }
726
727 return result;
728 }
729
730
731 template <int dim, typename Number, typename MemorySpace>
732 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number
737
738
739 template <int dim, typename Number, typename MemorySpace>
740 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number
742 const Number &u) const
743 {
744 return ScalarNumber(0.5) * u * u;
745 }
746
747
748 template <int dim, typename Number, typename MemorySpace>
749 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number
751 const Number &u) const
752 {
753 return u;
754 }
755
756
757 template <int dim, typename Number, typename MemorySpace>
758 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number
760 const Number &k, const Number &u) const
761 {
762 return std::abs(k - u);
763 }
764
765
766 template <int dim, typename Number, typename MemorySpace>
767 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number
769 const Number &k, const Number &u) const
770 {
771 constexpr auto gte = dealii::SIMDComparison::greater_than_or_equal;
772 // return sgn(u-k):
773 return ryujin::compare_and_apply_mask<gte>(u, k, Number(1.), Number(-1.));
774 }
775
776
777 template <int dim, typename Number, typename MemorySpace>
778 template <typename Lambda>
779 DEAL_II_HOST_DEVICE_ALWAYS_INLINE auto
781 dealii::types::boundary_id id,
782 const state_type &U,
783 const dealii::Tensor<1, dim, Number> & /*normal*/,
784 const Lambda &get_dirichlet_data) const -> state_type
785 {
786 state_type result = U;
787
788 if (id == Boundary::dirichlet) {
789 result = get_dirichlet_data();
790
791 } else if (id == Boundary::dirichlet_momentum) {
792 Assert(false,
793 dealii::ExcMessage(
794 "Invalid boundary ID »Boundary::dirichlet_momentum«, "
795 "enforcing Dirichlet boundary conditions on a momentum "
796 "is not possible for scalar conservation equations."));
797
798 } else if (id == Boundary::dirichlet_velocity) {
799 Assert(false,
800 dealii::ExcMessage(
801 "Invalid boundary ID »Boundary::dirichlet_velocity«, "
802 "enforcing Dirichlet boundary conditions on a momentum "
803 "is not possible for scalar conservation equations."));
804
805 } else if (id == Boundary::slip) {
806 Assert(
807 false,
808 dealii::ExcMessage("Invalid boundary ID »Boundary::slip«, slip "
809 "boundary conditions are unavailable for scalar "
810 "conservation equations."));
811 __builtin_trap();
812
813 } else if (id == Boundary::no_slip) {
814 Assert(false,
815 dealii::ExcMessage("Invalid boundary ID »Boundary::no_slip«, "
816 "no-slip boundary conditions are unavailable "
817 "for scalar conservation equations."));
818 __builtin_trap();
819
820 } else if (id == Boundary::dynamic) {
821 Assert(false,
822 dealii::ExcMessage("Invalid boundary ID »Boundary::dynamic«, "
823 "dynamic boundary conditions are unavailable "
824 "for scalar conservation equations."));
825 __builtin_trap();
826
827 } else {
828 Assert(false, dealii::ExcNotImplemented());
829 }
830
831 return result;
832 }
833
834
835 template <int dim, typename Number, typename MemorySpace>
836 DEAL_II_HOST_DEVICE_ALWAYS_INLINE dealii::Tensor<1, dim, Number>
838 const precomputed_type &precomputed) const
839 {
840 dealii::Tensor<1, dim, Number> result;
841 for (unsigned int k = 0; k < dim; ++k)
842 result[k] = precomputed[k];
843 return result;
844 }
845
846
847 template <int dim, typename Number, typename MemorySpace>
848 DEAL_II_HOST_DEVICE_ALWAYS_INLINE dealii::Tensor<1, dim, Number>
851 const precomputed_type &precomputed) const
852 {
853 dealii::Tensor<1, dim, Number> result;
854 for (unsigned int k = 0; k < dim; ++k)
855 result[k] = precomputed[dim + k];
856 return result;
857 }
858
859
860 template <int dim, typename Number, typename MemorySpace>
861 DEAL_II_HOST_DEVICE_ALWAYS_INLINE auto
863 const PrecomputedVectorView &pv,
864 const InitialPrecomputedVectorView & /*piv*/,
865 const unsigned int i,
866 const state_type & /*U_i*/) const -> flux_contribution_type
867 {
868 /* The flux contribution is a rank 2 tensor, thus a little dance: */
870 result[0] = construct_flux_tensor(
871 pv.template read_tensor<Number, precomputed_type>(i));
872 return result;
873 }
874
875
876 template <int dim, typename Number, typename MemorySpace>
877 DEAL_II_HOST_DEVICE_ALWAYS_INLINE auto
879 const PrecomputedVectorView &pv,
880 const InitialPrecomputedVectorView & /*piv*/,
881 const unsigned int *js,
882 const state_type & /*U_j*/) const -> flux_contribution_type
883 {
884 /* The flux contribution is a rank 2 tensor, thus a little dance: */
886 result[0] = construct_flux_tensor(
887 pv.template read_tensor<Number, precomputed_type>(js));
888 return result;
889 }
890
891
892 template <int dim, typename Number, typename MemorySpace>
893 DEAL_II_HOST_DEVICE_ALWAYS_INLINE auto
895 const flux_contribution_type &flux_i,
896 const flux_contribution_type &flux_j,
897 const dealii::Tensor<1, dim, Number> &c_ij) const -> state_type
898 {
899 return -contract(add(flux_i, flux_j), c_ij);
900 }
901 } // namespace ScalarConservation
902} // namespace ryujin
dealii::Tensor< 1, problem_dimension, Number > state_type
const auto & n_locally_owned() const
const auto & sparsity_pattern_simd() const
const auto & n_locally_internal() const
DEAL_II_HOST_DEVICE dealii::Tensor< 1, dim, Number > construct_flux_gradient_tensor(const precomputed_type &precomputed_state) const
state_type nodal_source(const PrecomputedVectorView &pv, const unsigned int i, const state_type &U_i, const ScalarNumber tau) const =delete
DEAL_II_HOST_DEVICE_ALWAYS_INLINE state_type from_primitive_state(const state_type &primitive_state) const
DEAL_II_HOST_DEVICE_ALWAYS_INLINE ScalarNumber derivative_approximation_delta() const
DEAL_II_HOST_DEVICE Number square_entropy_derivative(const Number &u) const
dealii::Tensor< 1, problem_dimension, Number > state_type
DEAL_II_HOST_DEVICE_ALWAYS_INLINE bool is_admissible(const state_type &) const
std::array< Number, n_precomputed_values > precomputed_type
DEAL_II_HOST_DEVICE_ALWAYS_INLINE state_type apply_galilei_transform(const state_type &state, const Lambda &) const
DEAL_II_HOST_DEVICE flux_contribution_type flux_contribution(const PrecomputedVectorView &pv, const InitialPrecomputedVectorView &, const unsigned int i, const state_type &) const
DEAL_II_HOST_DEVICE Number square_entropy(const Number &u) const
DEAL_II_ALWAYS_INLINE dealii::Tensor< 1, dim, Number > flux_gradient_function(const Number &u) const
std::array< Number, n_initial_precomputed_values > initial_precomputed_type
state_type nodal_source(const PrecomputedVectorView &pv, const unsigned int *js, const state_type &U_j, const ScalarNumber tau) const =delete
DEAL_II_ALWAYS_INLINE const std::string & flux() const
DEAL_II_HOST_DEVICE_ALWAYS_INLINE state_type expand_state(const ST &state) const
DEAL_II_HOST_DEVICE Number kruzkov_entropy(const Number &k, const Number &u) const
dealii::Tensor< 1, problem_dimension, dealii::Tensor< 1, dim, Number > > flux_type
static DEAL_II_HOST_DEVICE Number state(const state_type &U)
DEAL_II_HOST_DEVICE dealii::Tensor< 1, dim, Number > construct_flux_tensor(const precomputed_type &precomputed_state) const
DEAL_II_HOST_DEVICE_ALWAYS_INLINE state_type to_primitive_state(const state_type &state) const
DEAL_II_HOST_DEVICE state_type flux_divergence(const flux_contribution_type &flux_i, const flux_contribution_type &flux_j, const dealii::Tensor< 1, dim, Number > &c_ij) const
DEAL_II_HOST_DEVICE_ALWAYS_INLINE state_type apply_boundary_conditions(const dealii::types::boundary_id id, const state_type &U, const dealii::Tensor< 1, dim, Number > &normal, const Lambda &get_dirichlet_data) const
DEAL_II_ALWAYS_INLINE dealii::Tensor< 1, dim, Number > flux_function(const Number &u) const
static constexpr unsigned int n_initial_precomputed_values
state_type high_order_flux_divergence(const flux_contribution_type &, const flux_contribution_type &, const dealii::Tensor< 1, dim, Number > &c_ij) const =delete
HyperbolicSystemView(const HyperbolicSystem &hyperbolic_system)
DEAL_II_HOST_DEVICE Number kruzkov_entropy_derivative(const Number &k, const Number &u) const
typename get_value_type< Number >::type ScalarNumber
void fill_precomputed_values(const OfflineData< dim, ScalarNumber > &offline_data, StateVector< ScalarNumber > &state_vector, const bool skip_constrained_dofs=true) const
HyperbolicSystem(const std::string &subsection="/HyperbolicSystem")
constexpr bool have_separate_memory_spaces
Definition gpu.h:29
@ dirichlet_momentum
@ dirichlet_velocity
DEAL_II_HOST_DEVICE_ALWAYS_INLINE dealii::Tensor< 1, problem_dim, T > contract(const FT &flux_ij, const TT &c_ij)
DEAL_II_HOST_DEVICE_ALWAYS_INLINE FT add(const FT &flux_left_ij, const FT &flux_right_ij)
void populate_flux_list(flux_list_type &flux_list, const std::string &subsection)
std::set< std::shared_ptr< Flux > > flux_list_type
MultiComponentVector< Number > U
MultiComponentVector< Number > precomputed