ryujin 2.1.1 revision 336b16a72e829721302c626ec7071b92032b8248
riemann_solver.template.h
Go to the documentation of this file.
1//
2// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3// Copyright (C) 2023 - 2024 by the ryujin authors
4//
5
6#pragma once
7
8#include <compile_time_options.h>
9
10#include "riemann_solver.h"
11
12#include <simd.h>
13
14#include <random>
15
16// #define DEBUG_RIEMANN_SOLVER
17
18namespace ryujin
19{
20 namespace ScalarConservation
21 {
22 template <int dim, typename Number>
24 const Number &u_i,
25 const Number &u_j,
26 const precomputed_type &prec_i,
27 const precomputed_type &prec_j,
28 const dealii::Tensor<1, dim, Number> &n_ij) const
29 {
30 const auto &view = hyperbolic_system.view<dim, Number>();
31
32 /* Project all fluxes to 1D: */
33 const Number f_i = view.construct_flux_tensor(prec_i) * n_ij;
34 const Number f_j = view.construct_flux_tensor(prec_j) * n_ij;
35 const Number df_i = view.construct_flux_gradient_tensor(prec_i) * n_ij;
36 const Number df_j = view.construct_flux_gradient_tensor(prec_j) * n_ij;
37
38 const auto h2 = Number(2. * view.derivative_approximation_delta());
39
40#ifdef DEBUG_RIEMANN_SOLVER
41 std::cout << "\nu_i = " << u_i << std::endl;
42 std::cout << "u_j = " << u_j << std::endl;
43 std::cout << "f_i = " << f_i << std::endl;
44 std::cout << "f_j = " << f_j << std::endl;
45 std::cout << "df_i = " << df_i << std::endl;
46 std::cout << "df_j = " << df_j << std::endl;
47#endif
48
49 /*
50 * The Roe average with a regularization based on $h$ which is the
51 * step size used for the central difference approximation of f'(u).
52 *
53 * The regularization max(|u_i - u_j|, 2 * h) ensures that the
54 * quotient approximates the derivative f'( (u_i + u_j)/2 ) to the
55 * same precision that we use to compute f'(u_i) and f'(u_j) in the
56 * FunctionParser (via a central difference approximation).
57 *
58 * This implies that in contrast to the actual limit of the
59 * difference quotient we will approach 0 as |u_j - u_i| goes to
60 * zero. We fix this by taking the maximum with our approximation of
61 * f'(u_i) and f'(u_j) further down below.
62 */
63
64 auto lambda_max = std::abs(f_i - f_j) / std::max(std::abs(u_i - u_j), h2);
65#ifdef DEBUG_RIEMANN_SOLVER
66 std::cout << " Roe average = " << lambda_max << std::endl;
67#endif
68
69 constexpr auto gte = dealii::SIMDComparison::greater_than_or_equal;
70
71 if (parameters.use_greedy_wavespeed()) {
72 /*
73 * In case of a greedy estimate we make sure that we always use the
74 * Roe average and only fall back to the derivative approximation
75 * when u_i and u_j are close to each other within 2h:
76 */
77 lambda_max = dealii::compare_and_apply_mask<gte>(
78 std::abs(u_i - u_j),
79 h2,
80 lambda_max,
81 /* Approximate derivative in centerpoint: */
82 std::abs(ScalarNumber(0.5) * (df_i + df_j)));
83#ifdef DEBUG_RIEMANN_SOLVER
84 std::cout << " interpolated = "
85 << std::abs(ScalarNumber(0.5) * (df_i + df_j)) << std::endl;
86#endif
87
88 } else {
89 /*
90 * Always take the maximum with |f'(u_i)| and |f'(u_j)|.
91 *
92 * For convex fluxes this implies that lambda_max is indeed the
93 * maximal wavespeed of the system. See Example 79.17 in reference
94 * @cite ErnGuermond2021.
95 */
96 lambda_max = std::max(lambda_max, std::abs(df_i));
97 lambda_max = std::max(lambda_max, std::abs(df_j));
98#ifdef DEBUG_RIEMANN_SOLVER
99 std::cout << " left derivative = " << std::abs(df_i) << std::endl;
100 std::cout << " right derivative = " << std::abs(df_j) << std::endl;
101#endif
102 }
103
104 /*
105 * Thread-local helper lambda to generate a random number in [0,1]:
106 */
107
108 thread_local static const auto draw = []() {
109 static std::random_device random_device;
110 static auto generator = std::default_random_engine(random_device());
111 static std::uniform_real_distribution<ScalarNumber> dist(0., 1.);
112
113 if constexpr (std::is_same_v<ScalarNumber, Number>) {
114 /*
115 * Scalar quantity:
116 */
117 return dist(generator);
118
119 } else {
120 /*
121 * Populate a vectorized array:
122 */
123 Number result;
124 for (unsigned int s = 0; s < Number::size(); ++s)
125 result[s] = dist(generator);
126 return result;
127 }
128 };
129
130 /*
131 * Helper functions for enforcing entropy inequalities:
132 */
133
134 const auto enforce_entropy = [&](const Number &k) {
135 const Number f_k = view.flux_function(k) * n_ij;
136
137#ifdef DEBUG_RIEMANN_SOLVER
138 std::cout << "k = " << k << std::endl;
139 std::cout << "f_k = " << f_k << std::endl;
140#endif
141
142 const Number eta_i = view.kruzkov_entropy(k, u_i);
143 const Number q_i =
144 view.kruzkov_entropy_derivative(k, u_i) * (f_i - f_k);
145
146 const Number eta_j = view.kruzkov_entropy(k, u_j);
147 const Number q_j =
148 view.kruzkov_entropy_derivative(k, u_j) * (f_j - f_k);
149
150 const Number a = u_i + u_j - ScalarNumber(2.) * k;
151 const Number b = f_j - f_i;
152 const Number c = eta_i + eta_j;
153 const Number d = q_j - q_i;
154
155 /*
156 * FIXME: Ordinarily, lambda_left and lambda_right would be
157 * computed without taking the absolute value of the numerator.
158 * (The denominator is - in the absence of rounding errors - always
159 * nonnegative. The numerator has a sign.)
160 * But empirically it turns out that taking the absolute value and
161 * letting both estimates participate in the maximal wavespeed
162 * estimate helps a lot.
163 */
164 const Number lambda_left = std::abs(d + b) / (std::abs(c + a) + h2);
165 const Number lambda_right = std::abs(d - b) / (std::abs(c - a) + h2);
166
167#ifdef DEBUG_RIEMANN_SOLVER
168 std::cout << " left wavespeed = " << lambda_left << std::endl;
169 std::cout << " right wavespeed = " << lambda_right << std::endl;
170#endif
171 lambda_max = std::max(lambda_max, lambda_left);
172 lambda_max = std::max(lambda_max, lambda_right);
173 };
174
175
176 if (parameters.use_averaged_entropy()) {
177 const Number k = ScalarNumber(0.5) * (u_i + u_j);
178 enforce_entropy(k);
179 }
180
181 const unsigned int n_entropies = parameters.random_entropies();
182 for (unsigned int i = 0; i < n_entropies; ++i) {
183 const Number factor = draw();
184 const Number k = factor * u_i + (Number(1.) - factor) * u_j;
185 enforce_entropy(k);
186 }
187
188#ifdef DEBUG_RIEMANN_SOLVER
189 std::cout << "-> lambda_max = " << lambda_max << std::endl;
190#endif
191 return lambda_max;
192 }
193
194
195 template <int dim, typename Number>
196 DEAL_II_ALWAYS_INLINE inline Number RiemannSolver<dim, Number>::compute(
197 const state_type &U_i,
198 const state_type &U_j,
199 const unsigned int i,
200 const unsigned int *js,
201 const dealii::Tensor<1, dim, Number> &n_ij) const
202 {
203 const auto view = hyperbolic_system.view<dim, Number>();
204
205 using pst = typename View::precomputed_type;
206
207 const auto u_i = view.state(U_i);
208 const auto u_j = view.state(U_j);
209
210 const auto &pv = precomputed_values;
211 const auto prec_i = pv.template get_tensor<Number, pst>(i);
212 const auto prec_j = pv.template get_tensor<Number, pst>(js);
213
214 return compute(u_i, u_j, prec_i, prec_j, n_ij);
215 }
216
217 } // namespace ScalarConservation
218} // namespace ryujin
std::array< Number, n_precomputed_values > precomputed_type
typename View::state_type state_type
typename View::ScalarNumber ScalarNumber
Number compute(const Number &u_i, const Number &u_j, const precomputed_type &prec_i, const precomputed_type &prec_j, const dealii::Tensor< 1, dim, Number > &n_ij) const
typename View::precomputed_type precomputed_type