[P]arallel [Hi]gh-order [Li]brary for [P]DEs  Latest
Parallel High-Order Library for PDEs through hp-adaptive Discontinuous Galerkin methods
euler_entropy_waves.cpp
1 #include <iostream>
2 
3 #include <deal.II/base/convergence_table.h>
4 
5 #include <deal.II/dofs/dof_tools.h>
6 
7 #include <deal.II/distributed/tria.h>
8 
9 #include <deal.II/grid/grid_generator.h>
10 #include <deal.II/grid/grid_refinement.h>
11 #include <deal.II/grid/grid_tools.h>
12 #include <deal.II/grid/grid_out.h>
13 #include <deal.II/grid/grid_in.h>
14 
15 #include <deal.II/grid/grid_tools.h>
16 
17 
18 #include <deal.II/numerics/vector_tools.h>
19 
20 #include <deal.II/fe/fe_values.h>
21 
22 #include <Sacado.hpp>
23 
24 #include "tests.h"
25 #include "euler_entropy_waves.h"
26 #include <deal.II/fe/mapping_q.h>
27 
28 #include "physics/physics_factory.h"
29 #include "dg/dg_factory.hpp"
30 #include "ode_solver/ode_solver_factory.h"
31 
32 
33 namespace PHiLiP {
34 namespace Tests {
35 
36 template <int dim, typename real>
38 ::EulerEntropyWavesFunction (const Physics::Euler<dim, dim+2, real> euler_physics, const real dimensional_density_inf)
39  : dealii::Function<dim,real>(dim+2, 0.0)
40  , euler_physics(euler_physics)
41  , dimensional_density_inf(dimensional_density_inf)
42 {
43  Q_inf = 0.0;
44  for(int d=0;d<dim;d++) {
45  Q_inf += euler_physics.velocities_inf[d];
46  }
47 }
48 
49 template <int dim, typename real>
51 ::value (const dealii::Point<dim> &point, const unsigned int istate) const
52 {
53 
54  const real char_length = euler_physics.ref_length;
55  real sum_location = 0.0;;
56  for(int d=0;d<dim;d++) {
57  sum_location += point[d];
58  }
59 
60  const double time = this->get_time();
61  const real density = 1.0 + 1.0/dimensional_density_inf * sin ( 2.0*dealii::numbers::PI*char_length* (sum_location - Q_inf * time) );
62  const dealii::Tensor<1,dim,real> velocities = euler_physics.velocities_inf;
63  const real pressure = euler_physics.pressure_inf;
64 
65  std::array<real, dim+2> primitive_values;
66  primitive_values[0] = density;
67  for(int d=0;d<dim;d++) {
68  primitive_values[1+d] = velocities[d];
69  }
70  primitive_values[dim+1] = pressure;
71  const std::array<real, dim+2> conservative_values = euler_physics.convert_primitive_to_conservative(primitive_values);
72 
73  return conservative_values[istate];
74 }
75 
76 template <int dim, int nstate>
78  :
79  TestsBase::TestsBase(parameters_input)
80 {}
81 
82 template<int dim, int nstate>
84 ::run_test () const
85 {
87  using GridEnum = ManParam::GridEnum;
89 
90  Assert(dim == param.dimension, dealii::ExcDimensionMismatch(dim, param.dimension));
91  Assert(dim == 2, dealii::ExcDimensionMismatch(dim, 2));
92 
93  ManParam manu_grid_conv_param = param.manufactured_convergence_study_param;
94 
95  const unsigned int p_start = manu_grid_conv_param.degree_start;
96  const unsigned int p_end = manu_grid_conv_param.degree_end;
97 
98  const unsigned int n_grids_input = manu_grid_conv_param.number_of_grids;
99 
100  std::vector<int> fail_conv_poly;
101  std::vector<double> fail_conv_slop;
102  std::vector<dealii::ConvergenceTable> convergence_table_vector;
103 
104  std::shared_ptr <Physics::PhysicsBase<dim,nstate,double>> physics = Physics::PhysicsFactory<dim, nstate, double>::create_Physics(&param);
105  std::shared_ptr <Physics::Euler<dim,nstate,double>> euler = std::dynamic_pointer_cast<Physics::Euler<dim,nstate,double>>(physics);
106 
107  const double dimensional_density_inf = 1.0;
108  EulerEntropyWavesFunction<dim,double> initial_vortex_function(*euler, dimensional_density_inf);
109  initial_vortex_function.set_time(0.0);
110 
111  EulerEntropyWavesFunction<dim,double> final_vortex_function(*euler, dimensional_density_inf);
112  final_vortex_function.set_time(1.0);
113 
114  for (unsigned int poly_degree = p_start; poly_degree <= p_end; ++poly_degree) {
115 
116  // p0 tends to require a finer grid to reach asymptotic region
117  unsigned int n_grids = n_grids_input;
118  if (poly_degree <= 1) n_grids = n_grids_input + 2;
119 
120  std::vector<double> soln_error(n_grids);
121  std::vector<double> grid_size(n_grids);
122 
123  const std::vector<int> n_1d_cells = get_number_1d_cells(n_grids);
124 
125  dealii::ConvergenceTable convergence_table;
126 
127  // Create periodicity vector to store the periodic info.
128 #if PHILIP_DIM==1
129  using Triangulation = dealii::Triangulation<dim>;
130 #else
131  using Triangulation = dealii::parallel::distributed::Triangulation<dim>;
132 #endif
133 
134  std::vector<dealii::GridTools::PeriodicFacePair<typename Triangulation::cell_iterator > > periodicity_vector;
135  for (unsigned int igrid=0; igrid<n_grids; ++igrid) {
136  // Note that Triangulation must be declared before DG
137  // DG will be destructed before Triangulation
138  // thus removing any dependence of Triangulation and allowing Triangulation to be destructed
139  // Otherwise, a Subscriptor error will occur
140  std::shared_ptr <Triangulation> grid = std::make_shared<Triangulation> (
141 #if PHILIP_DIM!=1
142  this->mpi_communicator,
143 #endif
144  typename dealii::Triangulation<dim>::MeshSmoothing(
145  dealii::Triangulation<dim>::smoothing_on_refinement |
146  dealii::Triangulation<dim>::smoothing_on_coarsening));
147 
148  // Generate hypercube
149  if ( manu_grid_conv_param.grid_type == GridEnum::hypercube || manu_grid_conv_param.grid_type == GridEnum::sinehypercube ) {
150 
151  std::vector<unsigned int> n_subdivisions(2);
152  n_subdivisions[0] = n_1d_cells[igrid];
153  n_subdivisions[1] = n_1d_cells[igrid];
154  const bool colorize = true;
155  dealii::Point<dim> p1(0.0,0.0), p2(1.0,1.0);
156  for (int d=0;d<dim;d++) {
157  p1[d] = 0.0;
158  p2[d] = 1.0;
159  }
160  dealii::GridGenerator::subdivided_hyper_rectangle (*grid, n_subdivisions, p1, p2, colorize);
161 
162  dealii::FullMatrix<double> rotation_matrix(dim);
163  rotation_matrix[1][0] = 1.;
164  rotation_matrix[0][1] = 1.;
165  for (int d=0;d<dim;d++) {
166  dealii::GridTools::collect_periodic_faces< Triangulation > (*grid, 2*d, 2*d+1, d, periodicity_vector, dealii::Tensor<1, dim>(),
167  rotation_matrix);
168  }
169  grid->add_periodicity(periodicity_vector);
170 
171  //for (typename dealii::Triangulation<dim>::active_cell_iterator cell = grid->begin_active(); cell != grid->end(); ++cell) {
172  // // Set a dummy boundary ID
173  // for (unsigned int face=0; face<dealii::GeometryInfo<dim>::faces_per_cell; ++face) {
174  // if (cell->face(face)->at_boundary()) {
175  // unsigned int current_id = cell->face(face)->boundary_id();
176  // if (current_id == 0) {
177  // cell->face(face)->set_boundary_id (1004); // x_left, Farfield
178  // } else if (current_id == 1) {
179  // cell->face(face)->set_boundary_id (1004); // x_right, Symmetry/Wall
180  // } else if (current_id == 2) {
181  // cell->face(face)->set_boundary_id (1004); // y_bottom, Symmetry/Wall
182  // } else if (current_id == 3) {
183  // cell->face(face)->set_boundary_id (1004); // y_top, Wall
184  // } else {
185  // std::cout << "current_face_id " << current_id << std::endl;
186  // std::abort();
187  // }
188  // }
189  // }
190  //}
191  //const double max_ratio = 1.5;
192  }
193 
194  // Distort grid by random amount if requested
195  const double random_factor = manu_grid_conv_param.random_distortion;
196  const bool keep_boundary = true;
197  if (random_factor > 0.0) dealii::GridTools::distort_random (random_factor, *grid, keep_boundary);
198 
199  // Create DG object using the factory
200  std::shared_ptr < DGBase<dim, double> > dg = DGFactory<dim,double>::create_discontinuous_galerkin(&param, poly_degree, grid);
201  dg->allocate_system ();
202 
203  // Initialize solution with vortex function at time t=0
204  dealii::VectorTools::interpolate(dg->dof_handler, initial_vortex_function, dg->solution);
205 
206  // Create ODE solver using the factory and providing the DG object
207  std::shared_ptr<ODE::ODESolverBase<dim, double>> ode_solver = ODE::ODESolverFactory<dim, double>::create_ODESolver(dg);
208 
209  unsigned int n_active_cells = grid->n_active_cells();
210  std::cout
211  << "Dimension: " << dim
212  << "\t Polynomial degree p: " << poly_degree
213  << std::endl
214  << "Grid number: " << igrid+1 << "/" << n_grids
215  << ". Number of active cells: " << n_active_cells
216  << ". Number of degrees of freedom: " << dg->dof_handler.n_dofs()
217  << std::endl;
218 
219  // Solve the steady state problem
220  ode_solver->steady_state();
221 
222  // Overintegrate the error to make sure there is not integration error in the error estimate
223  int overintegrate = 10;
224  dealii::QGauss<dim> quad_extra(dg->max_degree+1+overintegrate);
225  dealii::FEValues<dim,dim> fe_values_extra(dealii::MappingQ<dim>(dg->max_degree+overintegrate), dg->fe_collection[poly_degree], quad_extra,
226  dealii::update_values | dealii::update_JxW_values | dealii::update_quadrature_points);
227  //int overintegrate = 10;
228  //dealii::QGauss<dim> quad_extra(dg->fe_system.tensor_degree()+overintegrate);
229  //dealii::FEValues<dim,dim> fe_values_extra(dg->mapping, dg->fe_system, quad_extra,
230  // dealii::update_values | dealii::update_JxW_values | dealii::update_quadrature_points);
231  const unsigned int n_quad_pts = fe_values_extra.n_quadrature_points;
232  std::array<double,nstate> soln_at_q;
233 
234  double l2error = 0;
235 
236  // Integrate solution error
237  typename dealii::DoFHandler<dim>::active_cell_iterator
238  cell = dg->dof_handler.begin_active(),
239  endc = dg->dof_handler.end();
240 
241  std::vector<dealii::types::global_dof_index> dofs_indices (fe_values_extra.dofs_per_cell);
242  for (; cell!=endc; ++cell) {
243 
244  fe_values_extra.reinit (cell);
245  cell->get_dof_indices (dofs_indices);
246 
247  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
248 
249  std::fill(soln_at_q.begin(), soln_at_q.end(), 0);
250  for (unsigned int idof=0; idof<fe_values_extra.dofs_per_cell; ++idof) {
251  const unsigned int istate = fe_values_extra.get_fe().system_to_component_index(idof).first;
252  soln_at_q[istate] += dg->solution[dofs_indices[idof]] * fe_values_extra.shape_value_component(idof, iquad, istate);
253  }
254 
255  const dealii::Point<dim> qpoint = (fe_values_extra.quadrature_point(iquad));
256  //std::array<double,nstate> uexact;
257  //std::cout << "cos(0.59*x+1 " << cos(0.59*qpoint[0]+1) << std::endl;
258  //std::cout << "uexact[1] " << uexact[1] << std::endl;
259 
260  for (int istate=0; istate<nstate; ++istate) {
261  const double uexact = physics->manufactured_solution_function->value(qpoint, istate);
262  l2error += pow(soln_at_q[istate] - uexact, 2) * fe_values_extra.JxW(iquad);
263  }
264  }
265 
266  }
267  l2error = sqrt(l2error);
268 
269  // Convergence table
270  double dx = 1.0/pow(n_active_cells,(1.0/dim));
271  dx = dealii::GridTools::maximal_cell_diameter(*grid);
272  grid_size[igrid] = dx;
273  soln_error[igrid] = l2error;
274 
275  convergence_table.add_value("p", poly_degree);
276  convergence_table.add_value("cells", grid->n_active_cells());
277  convergence_table.add_value("dx", dx);
278  convergence_table.add_value("soln_L2_error", l2error);
279 
280 
281  std::cout << " Grid size h: " << dx
282  << " L2-soln_error: " << l2error
283  << " Residual: " << ode_solver->residual_norm
284  << std::endl;
285 
286  if (igrid > 0) {
287  const double slope_soln_err = log(soln_error[igrid]/soln_error[igrid-1])
288  / log(grid_size[igrid]/grid_size[igrid-1]);
289  std::cout << "From grid " << igrid-1
290  << " to grid " << igrid
291  << " dimension: " << dim
292  << " polynomial degree p: " << poly_degree
293  << std::endl
294  << " solution_error1 " << soln_error[igrid-1]
295  << " solution_error2 " << soln_error[igrid]
296  << " slope " << slope_soln_err
297  << std::endl;
298  }
299  }
300  std::cout
301  << " ********************************************"
302  << std::endl
303  << " Convergence rates for p = " << poly_degree
304  << std::endl
305  << " ********************************************"
306  << std::endl;
307  convergence_table.evaluate_convergence_rates("soln_L2_error", "cells", dealii::ConvergenceTable::reduction_rate_log2, dim);
308  convergence_table.set_scientific("dx", true);
309  convergence_table.set_scientific("soln_L2_error", true);
310  convergence_table.write_text(std::cout);
311 
312  convergence_table_vector.push_back(convergence_table);
313 
314  const double expected_slope = poly_degree+1;
315 
316  const double last_slope = log(soln_error[n_grids-1]/soln_error[n_grids-2])
317  / log(grid_size[n_grids-1]/grid_size[n_grids-2]);
318  double before_last_slope = last_slope;
319  if ( n_grids > 2 ) {
320  before_last_slope = log(soln_error[n_grids-2]/soln_error[n_grids-3])
321  / log(grid_size[n_grids-2]/grid_size[n_grids-3]);
322  }
323  const double slope_avg = 0.5*(before_last_slope+last_slope);
324  const double slope_diff = slope_avg-expected_slope;
325 
326  double slope_deficit_tolerance = -0.1;
327  if(poly_degree == 0) slope_deficit_tolerance = -0.2; // Otherwise, grid sizes need to be much bigger for p=0
328 
329  if (slope_diff < slope_deficit_tolerance) {
330  std::cout << std::endl
331  << "Convergence order not achieved. Average last 2 slopes of "
332  << slope_avg << " instead of expected "
333  << expected_slope << " within a tolerance of "
334  << slope_deficit_tolerance
335  << std::endl;
336  // p=0 just requires too many meshes to get into the asymptotic region.
337  if(poly_degree!=0) fail_conv_poly.push_back(poly_degree);
338  if(poly_degree!=0) fail_conv_slop.push_back(slope_avg);
339  }
340 
341  }
342  std::cout << std::endl
343  << std::endl
344  << std::endl
345  << std::endl;
346  std::cout << " ********************************************"
347  << std::endl;
348  std::cout << " Convergence summary"
349  << std::endl;
350  std::cout << " ********************************************"
351  << std::endl;
352  for (auto conv = convergence_table_vector.begin(); conv!=convergence_table_vector.end(); conv++) {
353  conv->write_text(std::cout);
354  std::cout << " ********************************************"
355  << std::endl;
356  }
357  int n_fail_poly = fail_conv_poly.size();
358  if (n_fail_poly > 0) {
359  for (int ifail=0; ifail < n_fail_poly; ++ifail) {
360  const double expected_slope = fail_conv_poly[ifail]+1;
361  const double slope_deficit_tolerance = -0.1;
362  std::cout << std::endl
363  << "Convergence order not achieved for polynomial p = "
364  << fail_conv_poly[ifail]
365  << ". Slope of "
366  << fail_conv_slop[ifail] << " instead of expected "
367  << expected_slope << " within a tolerance of "
368  << slope_deficit_tolerance
369  << std::endl;
370  }
371  }
372  return n_fail_poly;
373 }
374 
376 
377 
378 } // Tests namespace
379 } // PHiLiP namespace
380 
381 
unsigned int degree_start
First polynomial degree to start the loop. If diffusion, must be at least 1.
Parameters related to the manufactured convergence study.
const MPI_Comm mpi_communicator
MPI communicator.
Definition: tests.h:39
Files for the baseline physics.
Definition: ADTypes.hpp:10
int run_test() const
Manufactured grid convergence.
real value(const dealii::Point< dim > &point, const unsigned int istate=0) const
Manufactured solution exact value.
unsigned int dimension
Number of dimensions. Note that it has to match the executable PHiLiP_xD.
Main parameter class that contains the various other sub-parameter classes.
ManufacturedConvergenceStudyParam manufactured_convergence_study_param
Contains parameters for manufactured convergence study.
const Parameters::AllParameters *const all_parameters
Pointer to all parameters.
Definition: tests.h:20
static std::shared_ptr< DGBase< dim, real, MeshType > > create_discontinuous_galerkin(const Parameters::AllParameters *const parameters_input, const unsigned int degree, const unsigned int max_degree_input, const unsigned int grid_degree_input, const std::shared_ptr< Triangulation > triangulation_input)
Creates a derived object DG, but returns it as DGBase.
Definition: dg_factory.cpp:10
EulerEntropyWaves()=delete
Constructor. Deleted the default constructor since it should not be used.
Performs grid convergence for various polynomial degrees.
EulerEntropyWavesFunction(const Physics::Euler< dim, dim+2, real > euler_physics, const real dimensional_density_inf)
Constructor that initializes base_values, amplitudes, frequencies.
static std::shared_ptr< PhysicsBase< dim, nstate, real > > create_Physics(const Parameters::AllParameters *const parameters_input, std::shared_ptr< ModelBase< dim, nstate, real > > model_input=nullptr)
Factory to return the correct physics given input file.
std::vector< int > get_number_1d_cells(const int ngrids) const
Evaluates the number of cells to generate the grids for 1D grid based on input file.
Definition: tests.cpp:71
static std::shared_ptr< ODESolverBase< dim, real, MeshType > > create_ODESolver(std::shared_ptr< DGBase< dim, real, MeshType > > dg_input)
Creates either implicit or explicit ODE solver based on parameter value(no POD basis given) ...
dealii::Tensor< 1, dim, double > velocities_inf
Non-dimensionalized Velocity vector at farfield.
Definition: euler.h:136
Base class of all the tests.
Definition: tests.h:17