ExaDG
Loading...
Searching...
No Matches
grid_related_time_step_restrictions.h
1/* ______________________________________________________________________
2 *
3 * ExaDG - High-Order Discontinuous Galerkin for the Exa-Scale
4 *
5 * Copyright (C) 2021 by the ExaDG authors
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 * ______________________________________________________________________
20 */
21
22#ifndef INCLUDE_EXADG_OPERATORS_GRID_RELATED_TIME_STEP_RESTRICTIONS_H_
23#define INCLUDE_EXADG_OPERATORS_GRID_RELATED_TIME_STEP_RESTRICTIONS_H_
24
25// deal.II
26#include <deal.II/base/function.h>
27#include <deal.II/lac/la_parallel_vector.h>
28
29// ExaDG
30#include <exadg/functions_and_boundary_conditions/evaluate_functions.h>
31#include <exadg/grid/calculate_characteristic_element_length.h>
32#include <exadg/matrix_free/integrators.h>
33#include <exadg/time_integration/enum_types.h>
34
35namespace ExaDG
36{
37/*
38 * This function calculates the time step such that the spatial and temporal errors are of the same
39 * order of magnitude:
40 *
41 * spatial error: e = C_h * h^{p+1}
42 * temporal error: e = C_dt * dt^{n} (n: order of time integration method)
43 *
44 * where h is a mesh size defined globally for the whole mesh, e.g. the global minimum vertex
45 * distance.
46 */
47inline double
48calculate_time_step_max_efficiency(double const h,
49 unsigned int const fe_degree,
50 unsigned int const order_time_integration)
51{
52 double const exponent = (double)(fe_degree + 1) / order_time_integration;
53 double const time_step = std::pow(h, exponent);
54
55 return time_step;
56}
57
58/*
59 * This function calculates the time step according to the formula
60 *
61 * diffusion_number/k^{exponent_fe_degree} = diffusivity * time_step / h²
62 */
63inline double
64calculate_const_time_step_diff(double const diffusivity,
65 double const global_min_cell_diameter,
66 unsigned int const fe_degree,
67 double const exponent_fe_degree = 3.0)
68{
69 double const time_step =
70 pow(global_min_cell_diameter, 2.0) / pow(fe_degree, exponent_fe_degree) / diffusivity;
71
72 return time_step;
73}
74
75/*
76 * This function calculates the maximum velocity for a given velocity field (which is known
77 * analytically). The maximum value is defined as the maximum velocity at the cell center.
78 */
79template<int dim>
80inline double
81calculate_max_velocity(dealii::Triangulation<dim> const & triangulation,
82 std::shared_ptr<dealii::Function<dim>> velocity,
83 double const time,
84 MPI_Comm const & mpi_comm)
85{
86 double U = 0.0, max_U = std::numeric_limits<double>::min();
87
88 dealii::Tensor<1, dim, double> vel;
89 velocity->set_time(time);
90
91 for(auto const & cell : triangulation.active_cell_iterators())
92 {
93 if(cell->is_locally_owned())
94 {
95 // calculate maximum velocity
96 dealii::Point<dim> point = cell->center();
97
98 for(unsigned int d = 0; d < dim; ++d)
99 vel[d] = velocity->value(point, d);
100
101 U = vel.norm();
102 if(U > max_U)
103 max_U = U;
104 }
105 }
106 double const global_max_U = dealii::Utilities::MPI::max(max_U, mpi_comm);
107
108 return global_max_U;
109}
110
111/*
112 * Calculate time step size according to local CFL criterion where the velocity field is a
113 * prescribed analytical function. The computed time step size corresponds to CFL = 1.0.
114 *
115 * The underlying CFL condition reads
116 *
117 * cfl/k^{exponent_fe_degree} = (|| U || / h) * time_step
118 */
119template<int dim, typename value_type>
120inline double
121calculate_time_step_cfl_local(dealii::MatrixFree<dim, value_type> const & data,
122 unsigned int const dof_index,
123 unsigned int const quad_index,
124 std::shared_ptr<dealii::Function<dim>> const velocity,
125 double const time,
126 unsigned int const degree,
127 double const exponent_fe_degree,
128 CFLConditionType const cfl_condition_type,
129 MPI_Comm const & mpi_comm)
130{
131 CellIntegrator<dim, dim, value_type> fe_eval(data, dof_index, quad_index);
132
133 double new_time_step = std::numeric_limits<double>::max();
134
135 double const cfl_p = 1.0 / pow(degree, exponent_fe_degree);
136
137 // loop over cells of processor
138 for(unsigned int cell = 0; cell < data.n_cell_batches(); ++cell)
139 {
140 dealii::VectorizedArray<value_type> delta_t_cell =
141 dealii::make_vectorized_array<value_type>(std::numeric_limits<value_type>::max());
142
143 fe_eval.reinit(cell);
144
145 // loop over quadrature points
146 for(unsigned int q = 0; q < fe_eval.n_q_points; ++q)
147 {
148 dealii::Point<dim, dealii::VectorizedArray<value_type>> q_point = fe_eval.quadrature_point(q);
149
150 dealii::Tensor<1, dim, dealii::VectorizedArray<value_type>> u_x =
151 FunctionEvaluator<1, dim, value_type>::value(*velocity, q_point, time);
152 dealii::Tensor<2, dim, dealii::VectorizedArray<value_type>> invJ =
153 fe_eval.inverse_jacobian(q);
154 invJ = transpose(invJ);
155 dealii::Tensor<1, dim, dealii::VectorizedArray<value_type>> ut_xi = invJ * u_x;
156
157 if(cfl_condition_type == CFLConditionType::VelocityNorm)
158 {
159 delta_t_cell = std::min(delta_t_cell, cfl_p / ut_xi.norm());
160 }
161 else if(cfl_condition_type == CFLConditionType::VelocityComponents)
162 {
163 for(unsigned int d = 0; d < dim; ++d)
164 delta_t_cell = std::min(delta_t_cell, cfl_p / (std::abs(ut_xi[d])));
165 }
166 else
167 {
168 AssertThrow(false, dealii::ExcMessage("Not implemented."));
169 }
170 }
171
172 // loop over vectorized array
173 double dt = std::numeric_limits<double>::max();
174 for(unsigned int v = 0; v < dealii::VectorizedArray<value_type>::size(); ++v)
175 {
176 dt = std::min(dt, (double)delta_t_cell[v]);
177 }
178
179 new_time_step = std::min(new_time_step, dt);
180 }
181
182 // find minimum over all processors
183 new_time_step = dealii::Utilities::MPI::min(new_time_step, mpi_comm);
184
185 return new_time_step;
186}
187
188/*
189 * Calculate time step size according to local CFL criterion where the velocity field is a numerical
190 * solution field. The computed time step size corresponds to CFL = 1.0.
191 */
192template<int dim, typename value_type>
193inline double
194calculate_time_step_cfl_local(
195 dealii::MatrixFree<dim, value_type> const & data,
196 unsigned int const dof_index,
197 unsigned int const quad_index,
198 dealii::LinearAlgebra::distributed::Vector<value_type> const & velocity,
199 unsigned int const degree,
200 double const exponent_fe_degree,
201 CFLConditionType const cfl_condition_type,
202 MPI_Comm const & mpi_comm)
203{
204 CellIntegrator<dim, dim, value_type> fe_eval(data, dof_index, quad_index);
205
206 double new_time_step = std::numeric_limits<double>::max();
207
208 double const cfl_p = 1.0 / pow(degree, exponent_fe_degree);
209
210 // loop over cells of processor
211 for(unsigned int cell = 0; cell < data.n_cell_batches(); ++cell)
212 {
213 dealii::VectorizedArray<value_type> delta_t_cell =
214 dealii::make_vectorized_array<value_type>(std::numeric_limits<value_type>::max());
215
216 dealii::Tensor<2, dim, dealii::VectorizedArray<value_type>> invJ;
217 dealii::Tensor<1, dim, dealii::VectorizedArray<value_type>> u_x;
218 dealii::Tensor<1, dim, dealii::VectorizedArray<value_type>> ut_xi;
219
220 fe_eval.reinit(cell);
221 fe_eval.read_dof_values(velocity);
222 fe_eval.evaluate(dealii::EvaluationFlags::values);
223
224 // loop over quadrature points
225 for(unsigned int q = 0; q < fe_eval.n_q_points; ++q)
226 {
227 u_x = fe_eval.get_value(q);
228 invJ = fe_eval.inverse_jacobian(q);
229 invJ = transpose(invJ);
230 ut_xi = invJ * u_x;
231
232 if(cfl_condition_type == CFLConditionType::VelocityNorm)
233 {
234 delta_t_cell = std::min(delta_t_cell, cfl_p / ut_xi.norm());
235 }
236 else if(cfl_condition_type == CFLConditionType::VelocityComponents)
237 {
238 for(unsigned int d = 0; d < dim; ++d)
239 delta_t_cell = std::min(delta_t_cell, cfl_p / (std::abs(ut_xi[d])));
240 }
241 else
242 {
243 AssertThrow(false, dealii::ExcMessage("Not implemented."));
244 }
245 }
246
247 // loop over vectorized array
248 double dt = std::numeric_limits<double>::max();
249 for(unsigned int v = 0; v < dealii::VectorizedArray<value_type>::size(); ++v)
250 {
251 dt = std::min(dt, (double)delta_t_cell[v]);
252 }
253
254 new_time_step = std::min(new_time_step, dt);
255 }
256
257 // find minimum over all processors
258 new_time_step = dealii::Utilities::MPI::min(new_time_step, mpi_comm);
259
260 // Cut time step size after, e.g., 4 digits of accuracy in order to make sure that there is no
261 // drift in the time step size depending on the number of processors when using adaptive time
262 // stepping. This effect can occur since the velocity field and the time step size are coupled
263 // (there is some form of feedback loop in case of adaptive time stepping, i.e., a minor change
264 // in the time step size due to round-off errors implies that the velocity field is evaluated
265 // at a slightly different time in the next time step and so on). This way, it can be ensured
266 // that the sequence of time step sizes is exactly reproducible with the results being
267 // independent of the number of processors, which is important for code verification in a
268 // parallel setting.
269 new_time_step = dealii::Utilities::truncate_to_n_digits(new_time_step, 4);
270
271 return new_time_step;
272}
273
274/*
275 * this function computes the actual CFL number in each cell given a global time step size
276 * (that holds for all cells)
277 */
278
279template<int dim, typename value_type>
280void
281calculate_cfl(dealii::LinearAlgebra::distributed::Vector<value_type> & cfl,
282 dealii::Triangulation<dim> const & triangulation,
283 dealii::MatrixFree<dim, value_type> const & data,
284 unsigned int const dof_index,
285 unsigned int const quad_index,
286 dealii::LinearAlgebra::distributed::Vector<value_type> const & velocity,
287 double const time_step,
288 unsigned int const degree,
289 double const exponent_fe_degree)
290{
291 CellIntegrator<dim, dim, value_type> fe_eval(data, dof_index, quad_index);
292
293 unsigned int const n_active_cells = triangulation.n_active_cells();
294
295 cfl.reinit(n_active_cells);
296
297 for(unsigned int cell = 0; cell < data.n_cell_batches(); ++cell)
298 {
299 fe_eval.reinit(cell);
300 fe_eval.read_dof_values(velocity);
301 fe_eval.evaluate(dealii::EvaluationFlags::values);
302
303 dealii::VectorizedArray<value_type> u_va =
304 dealii::make_vectorized_array<value_type>(std::numeric_limits<value_type>::min());
305
306 dealii::Tensor<2, dim, dealii::VectorizedArray<value_type>> invJ;
307 dealii::Tensor<1, dim, dealii::VectorizedArray<value_type>> u_x;
308 dealii::Tensor<1, dim, dealii::VectorizedArray<value_type>> ut_xi;
309
310 // loop over quadrature points
311 for(unsigned int q = 0; q < fe_eval.n_q_points; ++q)
312 {
313 u_x = fe_eval.get_value(q);
314 invJ = fe_eval.inverse_jacobian(q);
315 invJ = transpose(invJ);
316 ut_xi = invJ * u_x;
317
318 u_va = std::max(u_va, ut_xi.norm());
319 }
320
321 // loop over vectorized array
322 for(unsigned int v = 0; v < data.n_active_entries_per_cell_batch(cell); ++v)
323 {
324 cfl[data.get_cell_iterator(cell, v)->active_cell_index()] =
325 u_va[v] * time_step * pow(degree, exponent_fe_degree);
326 }
327 }
328}
329
330} // namespace ExaDG
331
332
333#endif /* INCLUDE_EXADG_OPERATORS_GRID_RELATED_TIME_STEP_RESTRICTIONS_H_ */
Definition driver.cpp:33