[P]arallel [Hi]gh-order [Li]brary for [P]DEs  Latest
Parallel High-Order Library for PDEs through hp-adaptive Discontinuous Galerkin methods
bound_preserving_limiter_tests.cpp
1 #include <stdlib.h> /* srand, rand */
2 #include <iostream>
3 
4 #include <deal.II/base/convergence_table.h>
5 #include <deal.II/fe/fe_values.h>
6 
7 #include "bound_preserving_limiter_tests.h"
8 
9 #include "physics/initial_conditions/initial_condition_function.h"
10 #include "flow_solver/flow_solver_factory.h"
11 
12 namespace PHiLiP {
13 namespace Tests {
14 
15 template <int dim, int nstate>
17  const PHiLiP::Parameters::AllParameters* const parameters_input,
18  const dealii::ParameterHandler& parameter_handler_input)
19  :
20  TestsBase::TestsBase(parameters_input)
21  , parameter_handler(parameter_handler_input)
22 {}
23 
24 template <int dim, int nstate>
26 {
28  using flow_case_enum = Parameters::FlowSolverParam::FlowCaseType;
29  flow_case_enum flow_case = all_parameters_new.flow_solver_param.flow_case_type;
30 
31  double left = all_parameters_new.flow_solver_param.grid_left_bound;
32  double right = all_parameters_new.flow_solver_param.grid_right_bound;
33  const unsigned int number_of_degrees_of_freedom_per_state = dg->dof_handler.n_dofs()/nstate;
34 
35  const unsigned int n_global_active_cells = dg->triangulation->n_global_active_cells();
36  const unsigned int n_dofs_cfl = dg->dof_handler.n_dofs() / nstate;
37  double delta_x = (PHILIP_DIM == 2) ? (right - left) / pow(n_global_active_cells, (1.0 / dim)) : (right - left) / pow(n_dofs_cfl, (1.0 / dim));
38  double time_step = 1e-5;
39 
40  /**********************************
41  * These values for the time step are chosen to show dominant spatial accuracy in the OOA results for P2
42  * For >=P3 timestep values refer to:
43  * Zhang, Xiangxiong, and Chi-Wang Shu.
44  * "On maximum-principle-satisfying high order schemes for scalar conservation laws."
45  * Journal of Computational Physics 229.9 (2010): 3091-3120.
46  **********************************/
47 
48  if(flow_case == flow_case_enum::advection_limiter)
49  time_step = (PHILIP_DIM == 2) ? (1.0 / 14.0) * delta_x : (1.0 / 3.0) * pow(delta_x, 2.0);
50 
51  if(flow_case == flow_case_enum::burgers_limiter)
52  time_step = (PHILIP_DIM == 2) ? (1.0 / 14.0) * delta_x : (1.0 / 24.0) * delta_x;
53 
54  if (flow_case == flow_case_enum::low_density){
55  // Initialize the maximum local wave speed to zero
56  double maximum_local_wave_speed = 0.0;
57 
58  // Overintegrate the error to make sure there is not integration error in the error estimate
59  int overintegrate = 10;
60  dealii::QGauss<dim> quad_extra(dg->max_degree+1+overintegrate);
61  dealii::FEValues<dim,dim> fe_values_extra(*(dg->high_order_grid->mapping_fe_field), dg->fe_collection[dg->max_degree], quad_extra,
62  dealii::update_values | dealii::update_gradients | dealii::update_JxW_values | dealii::update_quadrature_points);
63 
64  const unsigned int n_quad_pts = fe_values_extra.n_quadrature_points;
65  std::array<double,nstate> soln_at_q;
66 
67  std::vector<dealii::types::global_dof_index> dofs_indices (fe_values_extra.dofs_per_cell);
68  for (auto cell = dg->dof_handler.begin_active(); cell!=dg->dof_handler.end(); ++cell) {
69  if (!cell->is_locally_owned()) continue;
70  fe_values_extra.reinit (cell);
71  cell->get_dof_indices (dofs_indices);
72 
73  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
74 
75  std::fill(soln_at_q.begin(), soln_at_q.end(), 0.0);
76  for (unsigned int idof=0; idof<fe_values_extra.dofs_per_cell; ++idof) {
77  const unsigned int istate = fe_values_extra.get_fe().system_to_component_index(idof).first;
78  soln_at_q[istate] += dg->solution[dofs_indices[idof]] * fe_values_extra.shape_value_component(idof, iquad, istate);
79  }
80  double local_wave_speed = 0.0;
81  if(nstate == dim + 2) {
82  // Update the maximum local wave speed (i.e. convective eigenvalue)
83  Physics::Euler<dim,nstate,double> euler_physics_double
86  all_parameters_new.euler_param.ref_length,
87  all_parameters_new.euler_param.gamma_gas,
88  all_parameters_new.euler_param.mach_inf,
89  all_parameters_new.euler_param.angle_of_attack,
90  all_parameters_new.euler_param.side_slip_angle);
91 
92  local_wave_speed = euler_physics_double.max_convective_eigenvalue(soln_at_q);
93  }
94  if(local_wave_speed > maximum_local_wave_speed) maximum_local_wave_speed = local_wave_speed;
95  }
96  }
97  maximum_local_wave_speed = dealii::Utilities::MPI::max(maximum_local_wave_speed, this->mpi_communicator);
98 
99  const double approximate_grid_spacing = (all_parameters_new.flow_solver_param.grid_right_bound-all_parameters_new.flow_solver_param.grid_left_bound)/pow(number_of_degrees_of_freedom_per_state,(1.0/dim));
100  const double cfl_number = all_parameters_new.flow_solver_param.courant_friedrichs_lewy_number;
101  time_step = cfl_number * approximate_grid_spacing / maximum_local_wave_speed;
102  }
103 
104  return time_step;
105 }
106 
107 template <int dim, int nstate>
108 double BoundPreservingLimiterTests<dim, nstate>::calculate_uexact(const dealii::Point<dim> qpoint, const dealii::Tensor<1, 3, double> adv_speeds, double final_time) const
109 {
111  using flow_case_enum = Parameters::FlowSolverParam::FlowCaseType;
112  flow_case_enum flow_case = all_parameters_new.flow_solver_param.flow_case_type;
113  const double pi = atan(1) * 4.0;
114 
115  double uexact = 1.0;
116  if (flow_case == Parameters::FlowSolverParam::FlowCaseType::low_density && dim == 2) {
117  uexact = 0.01 + exp(-500.0*(pow(qpoint[0], 2.0)+pow(qpoint[1], 2.0)));
118  }
119  if (flow_case == Parameters::FlowSolverParam::FlowCaseType::low_density && dim == 1) {
120  uexact = 0.01 + exp(-500.0*pow(qpoint[0], 2.0));
121  }
122  else {
123  for (int idim = 0; idim < dim; idim++) {
124  if (flow_case == Parameters::FlowSolverParam::FlowCaseType::burgers_limiter)
125  uexact *= cos(pi * (qpoint[idim] - final_time));//for grid 1-3
126  if (flow_case == Parameters::FlowSolverParam::FlowCaseType::advection_limiter)
127  uexact *= sin(2.0 * pi * (qpoint[idim] - adv_speeds[idim] * final_time));//for grid 1-3
128  }
129  }
130 
131  return uexact;
132 }
133 
134 template <int dim, int nstate>
136  std::shared_ptr<DGBase<dim, double>> dg,
137  const int poly_degree,
138  const double final_time) const
139 {
140  // Overintegrate the error to make sure there is not integration error in the error estimate
141  int overintegrate = 10;
142  dealii::QGauss<dim> quad_extra(poly_degree + 1 + overintegrate);
143  dealii::FEValues<dim, dim> fe_values_extra(*(dg->high_order_grid->mapping_fe_field), dg->fe_collection[poly_degree], quad_extra,
144  dealii::update_values | dealii::update_JxW_values | dealii::update_quadrature_points);
145  const unsigned int n_quad_pts = fe_values_extra.n_quadrature_points;
146  std::array<double, nstate> soln_at_q;
147 
148  double l1error = 0.0;
149  double l2error = 0.0;
150  double linferror = 0.0;
151 
152  // Integrate every cell and compute L2
153  std::vector<dealii::types::global_dof_index> dofs_indices(fe_values_extra.dofs_per_cell);
154  const dealii::Tensor<1, 3, double> adv_speeds = Parameters::ManufacturedSolutionParam::get_default_advection_vector();
155  for (auto cell = dg->dof_handler.begin_active(); cell != dg->dof_handler.end(); ++cell) {
156  if (!cell->is_locally_owned()) continue;
157 
158  fe_values_extra.reinit(cell);
159  cell->get_dof_indices(dofs_indices);
160 
161  for (unsigned int iquad = 0; iquad < n_quad_pts; ++iquad) {
162 
163  std::fill(soln_at_q.begin(), soln_at_q.end(), 0.0);
164  for (unsigned int idof = 0; idof < fe_values_extra.dofs_per_cell; ++idof) {
165  const unsigned int istate = fe_values_extra.get_fe().system_to_component_index(idof).first;
166  soln_at_q[istate] += dg->solution[dofs_indices[idof]] * fe_values_extra.shape_value_component(idof, iquad, istate);
167  }
168 
169  const dealii::Point<dim> qpoint = (fe_values_extra.quadrature_point(iquad));
170  double uexact = calculate_uexact(qpoint, adv_speeds, final_time);
171 
172  //std::cout << "u: " << soln_at_q[0] << " uexact: " << uexact << std::endl;
173  l1error += pow(abs(soln_at_q[0] - uexact), 1.0) * fe_values_extra.JxW(iquad);
174  l2error += pow(abs(soln_at_q[0] - uexact), 2.0) * fe_values_extra.JxW(iquad);
175  //L-infinity norm
176  linferror = std::max(abs(soln_at_q[0]-uexact), linferror);
177  }
178  }
179  //MPI sum
180  double l1error_mpi = dealii::Utilities::MPI::sum(l1error, this->mpi_communicator);
181 
182  double l2error_mpi = dealii::Utilities::MPI::sum(l2error, this->mpi_communicator);
183  l2error_mpi = pow(l2error_mpi, 1.0/2.0);
184 
185  double linferror_mpi = dealii::Utilities::MPI::max(linferror, this->mpi_communicator);
186 
187  std::array<double,3> lerror_mpi;
188  lerror_mpi[0] = l1error_mpi;
189  lerror_mpi[1] = l2error_mpi;
190  lerror_mpi[2] = linferror_mpi;
191  return lerror_mpi;
192 }
193 
194 template <int dim, int nstate>
196 {
197  pcout << " Running Bound Preserving Limiter test. " << std::endl;
198  pcout << dim << " " << nstate << std::endl;
200 
201  int test_result = 1;
202 
203  if (!all_parameters_new.limiter_param.use_OOA) {
204  test_result = run_full_limiter_test();
205  }
206  else {
207  test_result = run_convergence_test();
208  }
209  return test_result; //if got to here means passed the test, otherwise would've failed earlier
210 }
211 
212 template <int dim, int nstate>
214 {
215  pcout << "\n" << "Creating FlowSolver" << std::endl;
216 
219 
220  using flow_case_enum = Parameters::FlowSolverParam::FlowCaseType;
221  flow_case_enum flow_case = all_parameters_new.flow_solver_param.flow_case_type;
222 
223  if (flow_case == Parameters::FlowSolverParam::FlowCaseType::low_density) {
225  if(dim == 2)
227  }
228 
229  // Create flow solver to access DG object which is needed to calculate time step
230  std::unique_ptr<FlowSolver::FlowSolver<dim, nstate>> flow_solver = FlowSolver::FlowSolverFactory<dim, nstate>::select_flow_case(&param, parameter_handler);
231  double time_step = get_time_step(flow_solver->dg);
232  param.flow_solver_param.constant_time_step = time_step;
233  // Delete flow solver object (the time_step param cannot be changed directly because flow_solver_param is protected in flow_solver)
234  flow_solver.reset();
235  // Reinitialize flow solver with new time step parameter
237  flow_solver->run();
238 
239  return 0;
240 }
241 
242 template <int dim, int nstate>
244 {
247 
248  const unsigned int n_grids = manu_grid_conv_param.number_of_grids;
249  dealii::ConvergenceTable convergence_table;
250  std::vector<double> grid_size(n_grids);
251  std::vector<double> soln_error_l2(n_grids);
252  double final_order = 0.0;
253  const double expected_order = all_parameters_new.flow_solver_param.expected_order_at_final_time;
254 
255  for (unsigned int igrid = 3; igrid < n_grids; igrid++) {
256 
257  pcout << "\n" << "Creating FlowSolver" << std::endl;
258 
262 
263  using flow_case_enum = Parameters::FlowSolverParam::FlowCaseType;
264  flow_case_enum flow_case = all_parameters_new.flow_solver_param.flow_case_type;
265  //const double pi = atan(1) * 4.0;
266 
267  if (flow_case == Parameters::FlowSolverParam::FlowCaseType::low_density) {
269 
270  if (dim == 2) {
272  }
273  }
274 
275  // Create flow solver to access DG object which is needed to calculate time step
276  std::unique_ptr<FlowSolver::FlowSolver<dim, nstate>> flow_solver = FlowSolver::FlowSolverFactory<dim, nstate>::select_flow_case(&param, parameter_handler);
277  const unsigned int n_global_active_cells = flow_solver->dg->triangulation->n_global_active_cells();
278  const int poly_degree = all_parameters_new.flow_solver_param.poly_degree;
279  double time_step = get_time_step(flow_solver->dg);
280  param.flow_solver_param.constant_time_step = time_step;
281  // Delete flow solver object (the time_step param cannot be changed directly because flow_solver_param is protected in flow_solver)
282  flow_solver.reset();
283  // Reinitialize flow solver with new time step parameter
285  flow_solver->run();
286  const double final_time_actual = flow_solver->ode_solver->current_time;
287 
288  // output results
289  const unsigned int n_dofs = flow_solver->dg->dof_handler.n_dofs();
290  this->pcout << "Dimension: " << dim
291  << "\t Polynomial degree p: " << poly_degree
292  << std::endl
293  << "Grid number: " << igrid + 1 << "/" << n_grids
294  << ". Number of active cells: " << n_global_active_cells
295  << ". Number of degrees of freedom: " << n_dofs
296  << std::endl;
297 
298  const std::array<double,3> lerror_mpi_sum = calculate_l_n_error(flow_solver->dg, poly_degree, final_time_actual);
299 
300  // Convergence table
301  const double dx = 1.0 / pow(n_dofs, (1.0 / dim));
302  grid_size[igrid] = dx;
303  soln_error_l2[igrid] = lerror_mpi_sum[1];
304 
305  convergence_table.add_value("p", poly_degree);
306  convergence_table.add_value("cells", n_global_active_cells);
307  convergence_table.add_value("DoFs", n_dofs);
308  convergence_table.add_value("dx", dx);
309  convergence_table.add_value("soln_L1_error", lerror_mpi_sum[0]);
310  convergence_table.add_value("soln_L2_error", lerror_mpi_sum[1]);
311  convergence_table.add_value("soln_Linf_error", lerror_mpi_sum[2]);
312 
313  this->pcout << " Grid size h: " << dx
314  << " L1-soln_error: " << lerror_mpi_sum[0]
315  << " L2-soln_error: " << lerror_mpi_sum[1]
316  << " Linf-soln_error: " << lerror_mpi_sum[2]
317  << " Residual: " << flow_solver->ode_solver->residual_norm
318  << std::endl;
319 
320  if (igrid > 0) {
321  const double slope_soln_err = log(soln_error_l2[igrid] / soln_error_l2[igrid - 1])
322  / log(grid_size[igrid] / grid_size[igrid - 1]);
323 
324  if (igrid == n_grids - 1)
325  final_order = slope_soln_err;
326 
327  this->pcout << "From grid " << igrid - 1
328  << " to grid " << igrid
329  << " dimension: " << dim
330  << " polynomial degree p: " << poly_degree
331  << std::endl
332  << " solution_error1 " << soln_error_l2[igrid - 1]
333  << " solution_error2 " << soln_error_l2[igrid]
334  << " slope " << slope_soln_err
335  << std::endl;
336  }
337 
338  this->pcout << " ********************************************"
339  << std::endl
340  << " Convergence rates for p = " << poly_degree
341  << std::endl
342  << " ********************************************"
343  << std::endl;
344  convergence_table.evaluate_convergence_rates("soln_L1_error", "cells", dealii::ConvergenceTable::reduction_rate_log2, dim);
345  convergence_table.evaluate_convergence_rates("soln_L2_error", "cells", dealii::ConvergenceTable::reduction_rate_log2, dim);
346  convergence_table.evaluate_convergence_rates("soln_Linf_error", "cells", dealii::ConvergenceTable::reduction_rate_log2, dim);
347  convergence_table.set_scientific("dx", true);
348  convergence_table.set_scientific("soln_L1_error", true);
349  convergence_table.set_scientific("soln_L2_error", true);
350  convergence_table.set_scientific("soln_Linf_error", true);
351  if (this->pcout.is_active()) convergence_table.write_text(this->pcout.get_stream());
352 
353  std::ofstream table_file("convergence_rates.txt");
354  convergence_table.write_text(table_file);
355 
356 
357  }//end of grid loop
358 
359  if(abs(final_order - expected_order) < 1e-4)
360  return 0;
361  else
362  return 1;
363 }
364 
365 #if PHILIP_DIM==1
368 #elif PHILIP_DIM==2
372 #endif
373 
374 } // Tests namespace
375 } // PHiLiP namespace
FlowCaseType
Selects the flow case to be simulated.
LimiterParam limiter_param
Contains parameters for limiter.
FlowCaseType flow_case_type
Selected FlowCaseType from the input file.
unsigned int number_of_grid_elements_per_dimension
Number of grid elements per dimension for hyper_cube mesh based cases.
static dealii::Tensor< 1, 3, double > get_default_advection_vector()
gets the default advection vector
double courant_friedrichs_lewy_number
Courant-Friedrich-Lewy (CFL) number for constant time step.
FlowSolverParam flow_solver_param
Contains the parameters for simulation cases (flow solver test)
Parameters related to the manufactured convergence study.
Class used to run tests that verify implementation of bound preserving limiters.
const MPI_Comm mpi_communicator
MPI communicator.
Definition: tests.h:39
double constant_time_step
Constant time step.
std::array< double, 3 > calculate_l_n_error(std::shared_ptr< DGBase< dim, double >> flow_solver_dg, const int poly_degree, const double final_time) const
Calculate and return the L2 Error.
double mach_inf
Mach number at infinity.
const dealii::ParameterHandler & parameter_handler
Parameter handler for storing the .prm file being ran.
Files for the baseline physics.
Definition: ADTypes.hpp:10
bool use_OOA
Flag to perform convergence analysis for Limiter Tests (ie. burgers_limiter, advection_limiter, low_density)
int run_convergence_test() const
Runs convergence test and prints out results in console.
EulerParam euler_param
Contains parameters for the Euler equations non-dimensionalization.
unsigned int poly_degree
Polynomial order (P) of the basis functions for DG.
Main parameter class that contains the various other sub-parameter classes.
BoundPreservingLimiterTests(const Parameters::AllParameters *const parameters_input, const dealii::ParameterHandler &parameter_handler_input)
Constructor.
double grid_left_bound
Left bound of domain for hyper_cube mesh based cases.
ManufacturedConvergenceStudyParam manufactured_convergence_study_param
Contains parameters for manufactured convergence study.
double ref_length
Reference length.
static std::unique_ptr< FlowSolver< dim, nstate > > select_flow_case(const Parameters::AllParameters *const parameters_input, const dealii::ParameterHandler &parameter_handler_input)
Factory to return the correct flow solver given input file.
const Parameters::AllParameters *const all_parameters
Pointer to all parameters.
Definition: tests.h:20
double expected_order_at_final_time
For limiter convergence tests, specify expected order at final time.
double side_slip_angle
Input file provides in degrees, but the value stored here is in radians.
unsigned int number_of_grid_elements_x
Number of subdivisions in x direction for a rectangle grid.
int run_full_limiter_test() const
Runs full test and outputs VTK files.
int number_of_mesh_refinements
Number of refinements for naca0012 and Gaussian bump based cases.
real max_convective_eigenvalue(const std::array< real, nstate > &soln) const override
Maximum convective eigenvalue.
Definition: euler.cpp:906
double angle_of_attack
Input file provides in degrees, but the value stored here is in radians.
unsigned int number_of_grid_elements_y
Number of subdivisions in y direction for a rectangle grid.
double get_time_step(std::shared_ptr< DGBase< dim, double >> dg) const
Function to compute the initial adaptive time step.
dealii::ConditionalOStream pcout
ConditionalOStream.
Definition: tests.h:45
double grid_right_bound
Right bound of domain for hyper_cube mesh based cases.
double calculate_uexact(const dealii::Point< dim > qpoint, const dealii::Tensor< 1, 3, double > adv_speeds, double final_time) const
Calculate and return the exact value at the point depending on the case being run.
DGBase is independent of the number of state variables.
Definition: dg_base.hpp:82
Base class of all the tests.
Definition: tests.h:17