include/meltpooldg/utilities/limiters.templates.hpp Source File

Developer Documentation: include/meltpooldg/utilities/limiters.templates.hpp Source File
Developer Documentation
limiters.templates.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <deal.II/base/parameter_handler.h>
4#include <deal.II/base/tensor.h>
5#include <deal.II/base/utilities.h>
6#include <deal.II/base/vectorization.h>
7
8#include <deal.II/grid/grid_tools.h>
9#include <deal.II/grid/tria.h>
10
11#include <deal.II/matrix_free/matrix_free.h>
12
14#include <meltpooldg/utilities/better_enum.hpp>
19
20#include <boost/container/small_vector.hpp>
21
22#include <array>
23#include <functional>
24#include <vector>
25
26template <typename number>
27void
29{
30 prm.enter_subsection("limiter");
31 {
32 prm.add_parameter("apply", apply_limiter, "Whether to apply a limiter.");
33 prm.add_parameter(
34 "tvb constant",
35 tvb_constant,
36 "The TVB constant used in the TVB minmod limiter. The constant has the dimension of a second derivative.");
37 prm.add_parameter("type", type, "The type of limiter to apply.");
38 }
39 prm.leave_subsection();
40}
41
42template <int n_components, typename TensorType, typename Container>
43TensorType
44MeltPoolDG::Utilities::tvd_minmod(const Container &values)
45{
46 Assert(values.begin() != values.end(), dealii::ExcMessage("Container must not be empty."));
47
48 TensorType result;
49 for (unsigned int i = 0; i < n_components; ++i)
50 {
51 // Check sign consistency
52 const bool all_positive =
53 std::all_of(values.begin(), values.end(), [i](TensorType v) { return v[i] > 0; });
54 const bool all_negative =
55 std::all_of(values.begin(), values.end(), [i](TensorType v) { return v[i] < 0; });
56
57 if (not(all_positive or all_negative))
58 {
59 result[i] = typename TensorType::value_type(0.);
60 }
61 else
62 {
63 // Find element with smallest absolute value
64 auto it = std::min_element(values.begin(),
65 values.end(),
66 [i](const TensorType &a, const TensorType &b) {
67 return std::abs(a[i]) < std::abs(b[i]);
68 });
69
70 result[i] = (*it)[i];
71 }
72 }
73
74 return result;
75}
76
77template <typename number, int n_components, typename TensorType, typename Container>
78TensorType
79MeltPoolDG::Utilities::tvb_minmod(const Container &values,
80 const std::vector<number> &tvb_constant,
81 const number cell_size)
82{
83 Assert(values.begin() != values.end(), dealii::ExcMessage("Container must not be empty."));
84 Assert(tvb_constant.size() == n_components,
85 dealii::ExcMessage(
86 "The size of the TVB constant vector must match the number of components."));
87
88 TensorType result;
89 std::array<bool, n_components> below_limit_mask;
90 for (unsigned int i = 0; i < n_components; ++i)
91 {
92 if (std::abs(values[0][i]) <= tvb_constant[i] * cell_size * cell_size)
93 {
94 below_limit_mask[i] = true;
95 }
96 else
97 {
98 below_limit_mask[i] = false;
99 }
100 }
101
102 if (std::all_of(below_limit_mask.begin(), below_limit_mask.end(), [](bool v) { return v; }))
103 {
104 for (unsigned int i = 0; i < n_components; ++i)
105 result[i] = values[0][i];
106 }
107 else
108 {
109 result = tvd_minmod<n_components, TensorType, Container>(values);
110
111 // TODO: Does this make sense to only apply the TVB modification to the components that are
112 // below the slope limit? Or should we rather apply the TVB modification to all components if
113 // at least one component is above the slope limit?
114 for (unsigned int i = 0; i < n_components; ++i)
115 {
116 if (below_limit_mask[i])
117 result[i] = values[0][i];
118 }
119 }
120
121 return result;
122}
123
124template <int dim, int n_components, typename number>
125std::array<dealii::Tensor<1, n_components, number>, dim>
127 const std::vector<std::pair<dealii::Tensor<1, n_components, number>,
128 dealii::Tensor<1, n_components, dealii::Tensor<1, dim, number>>>>
129 &cell_average_values,
130 const typename dealii::Triangulation<dim>::active_cell_iterator &cell,
132{
133 std::array<dealii::Tensor<1, n_components, number>, dim> limited_slopes;
134
135 for (unsigned int d = 0; d < dim; ++d)
136 {
137 boost::container::small_vector<dealii::Tensor<1, n_components, number>, 3>
138 minmod_input_values;
139 minmod_input_values.emplace_back();
140 for (unsigned int i = 0; i < n_components; ++i)
141 minmod_input_values[0][i] = cell_average_values[cell->active_cell_index()].second[i][d];
142 for (unsigned int face_no = 0; face_no < 2; ++face_no)
143 {
144 unsigned int face_index = 2 * d + face_no;
145 const auto neighbor = cell->neighbor(face_index);
146
147 if (!cell->at_boundary(face_index))
148 {
149 number distance_to_neighbor;
150 dealii::Tensor<1, n_components, number> neighbor_cell_average_value;
151 if ((cell->level() == neighbor->level() and neighbor->is_active()) or dim == 1)
152 {
153 // Case: Neighbor is active and on the same level as the current cell
154 distance_to_neighbor = cell->center().distance(neighbor->center());
155 neighbor_cell_average_value =
156 cell_average_values[neighbor->active_cell_index()].first;
157 }
158 else if (cell->level() == neighbor->level() and !neighbor->is_active())
159 {
160 // Case: Neighbor has active children which due to two-to-one refinement must be
161 // one level finer than the current cell
162 Assert(
163 cell->reference_cell().is_hyper_cube() and
164 neighbor->reference_cell().is_hyper_cube(),
165 dealii::ExcMessage(
166 "The minmod limiter currently only supports 3D hexahedral meshes with isotropic refinement."));
167
168 // Only use active cells that share a face with the current cell. If the face lies
169 // on a partition boundary, we only have access to ghost cell averages for direct
170 // face-neighbors. Average values for other child cells of the neighbor cell
171 // (those not sharing a face) might be unavailable across the partition.
172 distance_to_neighbor =
173 0.5 * cell->extent_in_direction(d) +
174 0.25 * cell->neighbor_child_on_subface(face_index, 0)->extent_in_direction(d);
175
176 // Assuming isotropic refinement
177 constexpr unsigned int n_subfaces = dealii::Utilities::fixed_power<dim - 1>(2);
178 for (unsigned int subface = 0; subface < n_subfaces; ++subface)
179 {
180 neighbor_cell_average_value +=
181 cell_average_values[cell->neighbor_child_on_subface(face_index, subface)
182 ->active_cell_index()]
183 .first;
184 }
185 neighbor_cell_average_value /= n_subfaces;
186 }
187 else
188 {
189 // Case: Neighbor is active but one level coarser than the current cell.
190 Assert(
191 cell->reference_cell().is_hyper_cube() and
192 neighbor->reference_cell().is_hyper_cube(),
193 dealii::ExcMessage(
194 "The minmod limiter currently only supports 3D hexahedral meshes with isotropic refinement."));
195
196
197 auto vec_neighbor_to_cell = cell->center() - neighbor->center();
198 distance_to_neighbor =
199 cell->extent_in_direction(d) * 0.5 + 0.25 * neighbor->extent_in_direction(d);
200 for (unsigned int c = 0; c < n_components; ++c)
201 neighbor_cell_average_value[c] =
202 cell_average_values[neighbor->active_cell_index()].first[c] +
203 (vec_neighbor_to_cell[d] - 0.5 * cell->extent_in_direction(d) -
204 0.25 * neighbor->extent_in_direction(d)) *
205 cell_average_values[neighbor->active_cell_index()].second[c][d];
206 }
207
208 if (face_no == 0)
209 {
210 minmod_input_values.push_back(
211 (cell_average_values[cell->active_cell_index()].first -
212 neighbor_cell_average_value) /
213 distance_to_neighbor);
214 }
215 else
216 {
217 minmod_input_values.push_back(
218 (neighbor_cell_average_value -
219 cell_average_values[cell->active_cell_index()].first) /
220 distance_to_neighbor);
221 }
222 }
223 }
224
225 switch (limiter_data.type)
226 {
227 case LimiterType::tvd_minmod:
228 limited_slopes[d] = tvd_minmod<
229 n_components,
230 dealii::Tensor<1, n_components, number>,
231 boost::container::small_vector<dealii::Tensor<1, n_components, number>, 3>>(
232 minmod_input_values);
233 break;
234 case LimiterType::tvb_minmod:
235 limited_slopes[d] = tvb_minmod<
236 number,
237 n_components,
238 dealii::Tensor<1, n_components, number>,
239 boost::container::small_vector<dealii::Tensor<1, n_components, number>, 3>>(
240 minmod_input_values, limiter_data.tvb_constant, cell->extent_in_direction(d));
241 break;
242 default:
243 AssertThrow(false, dealii::ExcMessage("Unknown limiter type."));
244 }
245 }
246 return limited_slopes;
247}
248
249template <int dim,
250 int n_components,
251 typename number,
252 typename VectorizedArrayType,
253 typename VectorType>
254void
256 const MatrixFreeContext<dim, number> &mf_context,
257 VectorType &dst,
258 const VectorType &src,
260{
261 if (not limiter_data.apply_limiter)
262 return;
263
264 AssertThrow(mf_context.mf.get_dof_handler(mf_context.dof_idx).get_fe().degree == 1,
265 dealii::ExcMessage(
266 "The minmod type limiter currently only supports linear elements (degree 1)."));
267
268 const auto cell_average_values =
269 compute_cell_average_quantities<dim, number, n_components>(mf_context, src);
270
271 // The cell loop lambda that will be passed to the matrix-free object to apply the limiter in
272 // a cell-wise manner. For each cell, the lambda computes the limited slopes at quadrature
273 // points and applies them to compute the limited solution values at quadrature points, which
274 // are then transformed back to the nodal basis and stored in the destination vector.
275 const std::function<void(const dealii::MatrixFree<dim, number, VectorizedArrayType> &,
276 VectorType &,
277 const VectorType &,
278 const std::pair<unsigned int, unsigned int> &)>
279 limit_loop = [&](const dealii::MatrixFree<dim, number, VectorizedArrayType> &matrix_free,
280 VectorType &dst,
281 const VectorType &src,
282 const std::pair<unsigned int, unsigned int> &cell_range) {
283 FECellIntegrator<dim, n_components, number> fe_cell_integrator(matrix_free,
284 mf_context.dof_idx,
285 mf_context.quad_idx);
286
287 dealii::MatrixFreeOperators::CellwiseInverseMassMatrix<dim, -1, n_components, number> inverse(
288 fe_cell_integrator);
289
290 std::vector<VectorizedArrayType> limited_values(
291 n_components * fe_cell_integrator.quadrature_point_indices().size());
292
293 for (unsigned int cell = cell_range.first; cell < cell_range.second; ++cell)
294 {
295 fe_cell_integrator.reinit(cell);
296 fe_cell_integrator.gather_evaluate(src, dealii::EvaluationFlags::values);
297
298 const auto &cells = cells_in_cell_batch(mf_context.mf, cell);
299
300 dealii::Tensor<1, n_components, dealii::Tensor<1, dim, VectorizedArrayType>>
301 limited_slopes;
302
303 dealii::Point<dim, VectorizedArrayType> cell_centers;
304 for (unsigned int i = 0; i < mf_context.mf.n_active_entries_per_cell_batch(cell); ++i)
305 for (unsigned int d = 0; d < dim; ++d)
306 cell_centers[d][i] = cells[i]->center()[d];
307
308 dealii::Tensor<1, n_components, VectorizedArrayType> cell_average_values_cell;
309 for (unsigned int i = 0; i < mf_context.mf.n_active_entries_per_cell_batch(cell); ++i)
310 {
311 for (unsigned int c = 0; c < n_components; ++c)
312 cell_average_values_cell[c][i] =
313 cell_average_values[cells[i]->active_cell_index()].first[c];
314 }
315
316 for (const unsigned int q : fe_cell_integrator.quadrature_point_indices())
317 {
318 dealii::Tensor<1, dim, VectorizedArrayType> vector_to_dof =
319 fe_cell_integrator.quadrature_point(q) - cell_centers;
320
321 // The cell loop lambda that will be passed to the matrix-free object to apply the
322 // limiter in a cell-wise manner. For each cell, the lambda computes the limited
323 // slopes at quadrature points and applies them to compute the limited solution
324 // values at quadrature points, which are then transformed back to the nodal basis
325 // and stored in the destination vector.
326 if (q == 0)
327 {
328 for (unsigned int lane = 0;
329 lane < mf_context.mf.n_active_entries_per_cell_batch(cell);
330 ++lane)
331 {
332 std::array<dealii::Tensor<1, n_components, number>, dim> limited_slope_lane =
333 compute_minmod_type_limited_slopes<dim, n_components, number>(
334 cell_average_values, cells[lane], limiter_data);
335
336 for (unsigned int c = 0; c < n_components; ++c)
337 for (unsigned int d = 0; d < dim; ++d)
338 limited_slopes[c][d][lane] = limited_slope_lane[d][c];
339 }
340 }
341
342 for (unsigned int c = 0; c < n_components; ++c)
343 limited_values[c * fe_cell_integrator.quadrature_point_indices().size() + q] =
344 cell_average_values_cell[c] + limited_slopes[c] * vector_to_dof;
345 }
346
347 inverse.transform_from_q_points_to_basis(n_components,
348 limited_values.data(),
349 fe_cell_integrator.begin_dof_values());
350 fe_cell_integrator.set_dof_values(dst);
351 }
352 };
353
354 dst.zero_out_ghost_values();
355 mf_context.mf.cell_loop(limit_loop, dst, src, true);
356 dst.update_ghost_values();
357}
std::array< dealii::Tensor< 1, n_components, number >, dim > compute_minmod_type_limited_slopes(const std::vector< std::pair< dealii::Tensor< 1, n_components, number >, dealii::Tensor< 1, n_components, dealii::Tensor< 1, dim, number > > > > &cell_average_values, const typename dealii::Triangulation< dim >::active_cell_iterator &cell, const LimiterData< number > &limiter_data)
Definition limiters.templates.hpp:126
TensorType tvb_minmod(const Container &values, const std::vector< number > &tvb_constant, const number cell_size)
Definition limiters.templates.hpp:79
void apply_minmod_type_limiter(const MatrixFreeContext< dim, number > &mf_context, VectorType &dst, const VectorType &src, const LimiterData< number > &limiter_data)
Definition limiters.templates.hpp:255
TensorType tvd_minmod(const Container &values)
Definition limiters.templates.hpp:44
std::vector< dealii::TriaIterator< dealii::CellAccessor< dim > > > cells_in_cell_batch(const dealii::MatrixFree< dim, number > &mf, const unsigned int cell_batch_id)
Definition matrix_free_util.hpp:69
dealii::FEEvaluation< dim, -1, 0, n_components, number, VectorizedArrayType > FECellIntegrator
Definition fe_integrator.hpp:14
Definition matrix_free_util.hpp:21
unsigned int quad_idx
Definition matrix_free_util.hpp:24
const dealii::MatrixFree< dim, Number > & mf
Definition matrix_free_util.hpp:22
unsigned int dof_idx
Definition matrix_free_util.hpp:23
Definition limiters.hpp:32
bool apply_limiter
Boolean flag indicating whether to apply the limiter or not.
Definition limiters.hpp:34
std::vector< number > tvb_constant
The TVB constant used in the TVB minmod limiter.
Definition limiters.hpp:37
LimiterType type
The type of limiter to apply.
Definition limiters.hpp:40
void add_parameters(dealii::ParameterHandler &prm)
Definition limiters.templates.hpp:28