ryujin 2.1.1 revision a15074459a388761bd8df6bd4ef7e6abe9d8077b
initial_values.template.h
Go to the documentation of this file.
1//
2// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3// Copyright (C) 2020 - 2023 by the ryujin authors
4//
5
6#pragma once
7
8#include "initial_values.h"
9#include "simd.h"
10
11#include <deal.II/numerics/vector_tools.h>
12#include <deal.II/numerics/vector_tools.templates.h>
13
14#include <random>
15
16namespace ryujin
17{
18 using namespace dealii;
19
20 template <typename Description, int dim, typename Number>
22 const HyperbolicSystem &hyperbolic_system,
23 const OfflineData<dim, Number> &offline_data,
24 const std::string &subsection)
25 : ParameterAcceptor(subsection)
26 , hyperbolic_system_(&hyperbolic_system)
27 , offline_data_(&offline_data)
28 {
29 ParameterAcceptor::parse_parameters_call_back.connect(std::bind(
31 this));
32
33 configuration_ = "uniform";
34 add_parameter("configuration",
35 configuration_,
36 "The initial state configuration. Valid names are given by "
37 "any of the subsections defined below.");
38
39 initial_direction_[0] = 1.;
40 add_parameter(
41 "direction",
42 initial_direction_,
43 "Initial direction of initial configuration (Galilei transform)");
44
45 initial_position_[0] = 1.;
46 add_parameter(
47 "position",
48 initial_position_,
49 "Initial position of initial configuration (Galilei transform)");
50
51 perturbation_ = 0.;
52 add_parameter("perturbation",
53 perturbation_,
54 "Add a random perturbation of the specified magnitude to the "
55 "initial state.");
56
57 /*
58 * And finally populate the initial state list with all initial state
59 * configurations defined in the InitialStateLibrary namespace:
60 */
62 initial_state_list_, *hyperbolic_system_, subsection);
63 }
64
65 namespace
66 {
70 template <int dim>
71 inline DEAL_II_ALWAYS_INLINE dealii::Point<dim>
72 affine_transform(const dealii::Tensor<1, dim> initial_direction,
73 const dealii::Point<dim> initial_position,
74 const dealii::Point<dim> x)
75 {
76 auto direction = x - initial_position;
77
78 /* Roll third component of initial_direction onto xy-plane: */
79 if constexpr (dim == 3) {
80 auto n_x = initial_direction[0];
81 auto n_z = initial_direction[2];
82 const auto norm = std::sqrt(n_x * n_x + n_z * n_z);
83 n_x /= norm;
84 n_z /= norm;
85 auto new_direction = direction;
86 if (norm > 1.0e-14) {
87 new_direction[0] = n_x * direction[0] + n_z * direction[2];
88 new_direction[2] = -n_z * direction[0] + n_x * direction[2];
89 }
90 direction = new_direction;
91 }
92
93 /* Roll second component of initial_direction onto x-axis: */
94 if constexpr (dim >= 2) {
95 auto n_x = initial_direction[0];
96 auto n_y = initial_direction[1];
97 const auto norm = std::sqrt(n_x * n_x + n_y * n_y);
98 n_x /= norm;
99 n_y /= norm;
100 auto new_direction = direction;
101 if (norm > 1.0e-14) {
102 new_direction[0] = n_x * direction[0] + n_y * direction[1];
103 new_direction[1] = -n_y * direction[0] + n_x * direction[1];
104 }
105 direction = new_direction;
106 }
107
108 return Point<dim>() + direction;
109 }
110
111
115 template <int dim, typename Number>
116 inline DEAL_II_ALWAYS_INLINE dealii::Tensor<1, dim, Number>
117 affine_transform_vector(const dealii::Tensor<1, dim> initial_direction,
118 dealii::Tensor<1, dim, Number> direction)
119 {
120 if constexpr (dim >= 2) {
121 auto n_x = initial_direction[0];
122 auto n_y = initial_direction[1];
123 const auto norm = std::sqrt(n_x * n_x + n_y * n_y);
124 n_x /= norm;
125 n_y /= norm;
126 auto new_direction = direction;
127 if (norm > 1.0e-14) {
128 new_direction[0] = n_x * direction[0] - n_y * direction[1];
129 new_direction[1] = n_y * direction[0] + n_x * direction[1];
130 }
131 direction = new_direction;
132 }
133
134 if constexpr (dim == 3) {
135 auto n_x = initial_direction[0];
136 auto n_z = initial_direction[2];
137 const auto norm = std::sqrt(n_x * n_x + n_z * n_z);
138 n_x /= norm;
139 n_z /= norm;
140 auto new_direction = direction;
141 if (norm > 1.0e-14) {
142 new_direction[0] = n_x * direction[0] - n_z * direction[2];
143 new_direction[2] = n_z * direction[0] + n_x * direction[2];
144 }
145 direction = new_direction;
146 }
147
148 return direction;
149 }
150 } /* namespace */
151
152
153 template <typename Description, int dim, typename Number>
155 {
156 /* First, let's normalize the direction: */
157
158 AssertThrow(initial_direction_.norm() != 0.,
159 ExcMessage("Initial direction is set to the zero vector."));
160 initial_direction_ /= initial_direction_.norm();
161
162 /* Populate std::function object: */
163
164 {
165 bool initialized = false;
166 for (auto &it : initial_state_list_)
167 if (it->name() == configuration_) {
168 initial_state_ = [this, &it](const dealii::Point<dim> &point,
169 Number t) {
170 const auto transformed_point =
171 affine_transform(initial_direction_, initial_position_, point);
172 auto state = it->compute(transformed_point, t);
173 const auto view = hyperbolic_system_->template view<dim, Number>();
174 state =
175 view.apply_galilei_transform(state, [&](const auto &momentum) {
176 return affine_transform_vector(initial_direction_, momentum);
177 });
178 return state;
179 };
180
181 flux_contributions_ = [this, &it](const dealii::Point<dim> &point) {
182 const auto transformed_point =
183 affine_transform(initial_direction_, initial_position_, point);
184 return it->initial_precomputations(transformed_point);
185 };
186 initialized = true;
187 break;
188 }
189
190 AssertThrow(
191 initialized,
192 ExcMessage(
193 "Could not find an initial state description with name \"" +
194 configuration_ + "\""));
195 }
196
197 /* Add a random perturbation to the original function object: */
198
199 if (perturbation_ != 0.) {
200 initial_state_ = [old_state = this->initial_state_,
201 perturbation = this->perturbation_](
202 const dealii::Point<dim> &point, Number t) {
203 auto state = old_state(point, t);
204
205 if (t > 0.)
206 return state;
207
208 static std::default_random_engine generator =
209 std::default_random_engine(std::random_device()());
210 static std::uniform_real_distribution<Number> distribution(-1., 1.);
211 static auto draw = std::bind(distribution, generator);
212 for (unsigned int i = 0; i < problem_dimension; ++i)
213 state[i] *= (Number(1.) + perturbation * draw());
214
215 return state;
216 };
217 }
218 }
219
220
221 template <typename Description, int dim, typename Number>
223 -> vector_type
224 {
225#ifdef DEBUG_OUTPUT
226 std::cout << "InitialValues<dim, Number>::interpolate(t = " << t << ")"
227 << std::endl;
228#endif
229
230 vector_type U;
231 U.reinit(offline_data_->vector_partitioner());
232
233 using scalar_type = typename OfflineData<dim, Number>::scalar_type;
234
235 const auto callable = [&](const auto &p) { return initial_state(p, t); };
236
237 scalar_type temp;
238 const auto scalar_partitioner = offline_data_->scalar_partitioner();
239 temp.reinit(scalar_partitioner);
240
241 for (unsigned int d = 0; d < problem_dimension; ++d) {
242 VectorTools::interpolate(offline_data_->dof_handler(),
243 to_function<dim, Number>(callable, d),
244 temp);
245 U.insert_component(temp, d);
246 }
247
248 /*
249 * Cosmetic fix up: Ensure that the initial state is compatible with
250 * slip and no_slip boundary conditions. This ensures that nothing is
251 * ever transported out of slip and no slip boundaries - even if
252 * initial conditions happen to be set incorrectly.
253 */
254
255 const auto &boundary_map = offline_data_->boundary_map();
256 const unsigned int n_owned = offline_data_->n_locally_owned();
257
258 for (auto entry : boundary_map) {
259 const auto i = entry.first;
260 if (i >= n_owned)
261 continue;
262
263 const auto normal = std::get<0>(entry.second);
264 const auto id = std::get<3>(entry.second);
265
266 if (id == Boundary::slip || id == Boundary::no_slip) {
267 auto U_i = U.get_tensor(i);
268 const auto view = hyperbolic_system_->template view<dim, Number>();
269 U_i = view.apply_boundary_conditions(
270 id, U_i, normal, [&]() { return U_i; });
271 U.write_tensor(U_i, i);
272 }
273 }
274
275 U.update_ghost_values();
276 return U;
277 }
278
279
280 template <typename Description, int dim, typename Number>
283 -> MultiComponentVector<Number, n_precomputed_values>
284 {
285 const auto scalar_partitioner = offline_data_->scalar_partitioner();
286
288 precomputed.reinit_with_scalar_partitioner(scalar_partitioner);
289
290 if constexpr (n_precomputed_values == 0)
291 return precomputed;
292
293 using scalar_type = typename OfflineData<dim, Number>::scalar_type;
294
295 const auto callable = [&](const auto &p) { return flux_contributions(p); };
296
297 scalar_type temp;
298 temp.reinit(scalar_partitioner);
299
300 for (unsigned int d = 0; d < n_precomputed_values; ++d) {
301 VectorTools::interpolate(offline_data_->dof_handler(),
302 to_function<dim, Number>(callable, d),
303 temp);
304 precomputed.insert_component(temp, d);
305 }
306
307 precomputed.update_ghost_values();
308 return precomputed;
309 }
310
311} /* namespace ryujin */
static void populate_initial_state_list(initial_state_list_type &initial_state_list, const HyperbolicSystem &h, const std::string &s)
InitialValues(const HyperbolicSystem &hyperbolic_system, const OfflineData< dim, Number > &offline_data, const std::string &subsection="/InitialValues")
typename Description::HyperbolicSystem HyperbolicSystem
vector_type interpolate(Number t=0) const
MultiComponentVector< Number, n_precomputed_values > interpolate_precomputed_initial_values() const
void reinit_with_scalar_partitioner(const std::shared_ptr< const dealii::Utilities::MPI::Partitioner > &scalar_partitioner)
void insert_component(const scalar_type &scalar_vector, unsigned int component)
Tensor get_tensor(const unsigned int i) const
void write_tensor(const Tensor &tensor, const unsigned int i)