[P]arallel [Hi]gh-order [Li]brary for [P]DEs  Latest
Parallel High-Order Library for PDEs through hp-adaptive Discontinuous Galerkin methods
euler_vortex.cpp
1 #include <stdlib.h> /* srand, rand */
2 #include <iostream>
3 
4 #include <deal.II/base/convergence_table.h>
5 
6 #include <deal.II/dofs/dof_tools.h>
7 
8 #include <deal.II/grid/grid_generator.h>
9 #include <deal.II/grid/grid_refinement.h>
10 #include <deal.II/grid/grid_tools.h>
11 #include <deal.II/grid/grid_out.h>
12 #include <deal.II/grid/grid_in.h>
13 
14 #include <deal.II/numerics/vector_tools.h>
15 
16 #include <deal.II/lac/affine_constraints.h>
17 
18 
19 #include <deal.II/fe/fe_values.h>
20 
21 #include <Sacado.hpp>
22 
23 #include <deal.II/fe/mapping_q.h>
24 
25 #include "tests.h"
26 #include "euler_vortex.h"
27 
28 #include "physics/physics_factory.h"
29 #include "physics/manufactured_solution.h"
30 #include "dg/dg_factory.hpp"
31 #include "ode_solver/ode_solver_factory.h"
32 
33 
34 namespace PHiLiP {
35 namespace Tests {
36 
37 template <int dim, typename real>
40  const Physics::Euler<dim, dim+2, real> euler_physics,
41  const dealii::Point<dim> initial_vortex_center,
42  const real vortex_strength,
43  const real vortex_stddev_decay)
44  :
45  dealii::Function<dim,real>(dim+2, 0.0)
46  , euler_physics(euler_physics)
47  , vortex_characteristic_length(1.0*euler_physics.ref_length)
48  , initial_vortex_center(initial_vortex_center)
49  , vortex_strength(vortex_strength)
50  , vortex_stddev_decay(vortex_stddev_decay)
51 {
52  static_assert(dim==2);
53 }
54 
55 template <int dim, typename real>
56 inline dealii::Point<dim> EulerVortexFunction<dim,real>::advected_location(dealii::Point<dim> grid_location) const
57 {
58  dealii::Point<dim> new_location;
59  const double time = this->get_time();
60  for(int d=0; d<dim; d++) {
61  new_location[d] = (grid_location[d] - initial_vortex_center[d]) - euler_physics.velocities_inf[d] * time;
62  }
63  return new_location;
64 }
65 
66 template <int dim, typename real>
68 ::value (const dealii::Point<dim> &point, const unsigned int istate) const
69 {
70  const double variance = vortex_stddev_decay*vortex_stddev_decay;
71 
72  dealii::Point<dim> new_loc = advected_location(point);
73  const double x = new_loc[0];
74  const double y = new_loc[1];
75 
76  //const double local_radius_sqr = new_loc.square();
77  //const double pi = dealii::numbers::PI;
78  //const double perturbation_strength = vortex_strength * exp(0.5*variance*(1.0-local_radius_sqr)) * 0.5 / pi;
79  //const double delta_vel_x = -y * perturbation_strength;
80  //const double delta_vel_y = x * perturbation_strength;
81  //const double delta_temp = euler_physics.temperature_inf / pow(euler_physics.sound_inf, 2) * 0.5*euler_physics.gamm1 * perturbation_strength*perturbation_strength;
82 
83  //const double vel_x = euler_physics.velocities_inf[0] + delta_vel_x;
84  //const double vel_y = euler_physics.velocities_inf[1] + delta_vel_y;
85  //const double temperature = (euler_physics.temperature_inf + delta_temp);
86 
88  //const double density = euler_physics.density_inf*pow(temperature/euler_physics.temperature_inf, 1.0/euler_physics.gamm1);
90  //const double pressure = euler_physics.pressure_inf * pow(temperature/euler_physics.temperature_inf, euler_physics.gam/euler_physics.gamm1);
91 
92  // Spiegel2015 isentropic vortex
93  //const double local_radius_sqr = new_loc.square();
94  //const double perturbation_strength = vortex_strength * exp(-0.5/(variance*std::pow(vortex_characteristic_length,2))*(local_radius_sqr));
95  //const double delta_vel_x = -y * perturbation_strength;
96  //const double delta_vel_y = x * perturbation_strength;
97  //const double delta_temp = -0.5*euler_physics.gamm1 * perturbation_strength*perturbation_strength;
98 
99  //const double vel_x = euler_physics.velocities_inf[0] + delta_vel_x;
100  //const double vel_y = euler_physics.velocities_inf[1] + delta_vel_y;
101  //const double temperature = euler_physics.temperature_inf * (1.0+delta_temp);
102  //const double pressure = euler_physics.pressure_inf * std::pow(1.0+delta_temp,euler_physics.gam/euler_physics.gamm1);
104  //const double density = euler_physics.compute_density_from_pressure_temperature (pressure, temperature);
105 
106 
107  // Philip's isentropic vortex
108  const double r2 = (std::pow(x,2.0)+std::pow(y,2.0))/(variance);
109  const double density = euler_physics.density_inf;
110  const double vel_x = euler_physics.velocities_inf[0] - y*vortex_strength/(variance)*exp(-0.5*r2);
111  const double vel_y = euler_physics.velocities_inf[1] + x*vortex_strength/(variance)*exp(-0.5*r2);
112  const double pressure = euler_physics.pressure_inf - euler_physics.density_inf*(vortex_strength*vortex_strength)/(2*variance)*exp(-r2);
113 
114  const std::array<double, 4> primitive_values = {{density, vel_x, vel_y, pressure}};
115  const std::array<double, 4> conservative_values = euler_physics.convert_primitive_to_conservative(primitive_values);
116 
117  //std::cout << density << " " << pressure << std::endl;
118  //std::cout << primitive_values[0] << " " << primitive_values[1] << " " << primitive_values[2]<< " " << primitive_values[3]<< std::endl;
119  //std::cout << conservative_values[0] << " " << conservative_values[1] << " " << conservative_values[2]<< " " << conservative_values[3]<< std::endl;
120  // if(std::abs(x) > 19.9) {
121  // std::cout<< "density " << conservative_values[0] << std::endl;
122  // std::cout<< "momx " << conservative_values[1] << std::endl;
123  // std::cout<< "momy " << conservative_values[2] << std::endl;
124  // std::cout<< "energy " << conservative_values[3] << std::endl;
125  // std::cout<< std::endl;
126  // }
127 
128  return conservative_values[istate];
129 }
130 
131 template <int dim, int nstate>
133  :
134  TestsBase::TestsBase(parameters_input)
135 {}
136 
137 template<int dim, int nstate>
139 ::run_test () const
140 {
142  using GridEnum = ManParam::GridEnum;
144 
145  Assert(dim == param.dimension, dealii::ExcDimensionMismatch(dim, param.dimension));
146  Assert(dim == 2, dealii::ExcDimensionMismatch(dim, 2));
147 
148  ManParam manu_grid_conv_param = param.manufactured_convergence_study_param;
149 
150  const unsigned int p_start = manu_grid_conv_param.degree_start;
151  const unsigned int p_end = manu_grid_conv_param.degree_end;
152 
153  const unsigned int n_grids_input = manu_grid_conv_param.number_of_grids;
154 
155  std::vector<int> fail_conv_poly;
156  std::vector<double> fail_conv_slop;
157  std::vector<dealii::ConvergenceTable> convergence_table_vector;
158 
159  std::shared_ptr <Physics::PhysicsBase<dim,nstate,double>> physics = Physics::PhysicsFactory<dim, nstate, double>::create_Physics(&param);
160  std::shared_ptr <Physics::Euler<dim,nstate,double>> euler = std::dynamic_pointer_cast<Physics::Euler<dim,nstate,double>>(physics);
161 
162  const dealii::Point<dim> initial_vortex_center(-0.0,-0.0);
163  const double vortex_strength = euler->mach_inf*4.0;
164  const double vortex_stddev_decay = 1.0;
165  const double half_length = 5*euler->ref_length;
166  //const double half_length = 5*euler->ref_length;
167  EulerVortexFunction<dim,double> initial_vortex_function(*euler, initial_vortex_center, vortex_strength, vortex_stddev_decay);
168  initial_vortex_function.set_time(0.0);
169 
170  for (unsigned int poly_degree = p_start; poly_degree <= p_end; ++poly_degree) {
171 
172  // p0 tends to require a finer grid to reach asymptotic region
173  unsigned int n_grids = n_grids_input;
174  //if (poly_degree <= 2) n_grids = n_grids_input + 1;
175 
176  std::vector<double> soln_error(n_grids);
177  std::vector<double> grid_size(n_grids);
178 
179  const std::vector<int> n_1d_cells = get_number_1d_cells(n_grids);
180 
181  dealii::ConvergenceTable convergence_table;
182 
183  for (unsigned int igrid=0; igrid<n_grids; ++igrid) {
184  // Note that Triangulation must be declared before DG
185  // DG will be destructed before Triangulation
186  // thus removing any dependence of Triangulation and allowing Triangulation to be destructed
187  // Otherwise, a Subscriptor error will occur
188  using Triangulation = dealii::parallel::distributed::Triangulation<dim>;
189  std::shared_ptr <Triangulation> grid = std::make_shared<Triangulation> (this->mpi_communicator,
190  typename dealii::Triangulation<dim>::MeshSmoothing(
191  dealii::Triangulation<dim>::smoothing_on_refinement |
192  dealii::Triangulation<dim>::smoothing_on_coarsening));
193 
194  // Generate hypercube
195  if ( manu_grid_conv_param.grid_type == GridEnum::hypercube || manu_grid_conv_param.grid_type == GridEnum::sinehypercube ) {
196 
197  std::vector<unsigned int> n_subdivisions(2);
198  n_subdivisions[0] = n_1d_cells[igrid];
199  n_subdivisions[1] = n_1d_cells[igrid];
200  const bool colorize = true;
201  dealii::Point<dim> p1(-half_length,-half_length), p2(half_length,half_length);
202  dealii::GridGenerator::subdivided_hyper_rectangle (*grid, n_subdivisions, p1, p2, colorize);
203  for (typename Triangulation::active_cell_iterator cell = grid->begin_active(); cell != grid->end(); ++cell) {
204  // Set a dummy boundary ID
205  for (unsigned int face=0; face<dealii::GeometryInfo<dim>::faces_per_cell; ++face) {
206  if (cell->face(face)->at_boundary()) {
207  unsigned int current_id = cell->face(face)->boundary_id();
208  if (current_id == 0) {
209  cell->face(face)->set_boundary_id (1004); // x_left, Farfield
210  } else if (current_id == 1) {
211  cell->face(face)->set_boundary_id (1004); // x_right, Symmetry/Wall
212  } else if (current_id == 2) {
213  cell->face(face)->set_boundary_id (1004); // y_bottom, Symmetry/Wall
214  } else if (current_id == 3) {
215  cell->face(face)->set_boundary_id (1004); // y_top, Wall
216  } else {
217  std::cout << "current_face_id " << current_id << std::endl;
218  std::abort();
219  }
220  }
221  }
222  }
223  //const double max_ratio = 1.5;
224  }
225 
226  // Distort grid by random amount if requested
227  const double random_factor = manu_grid_conv_param.random_distortion;
228  const bool keep_boundary = true;
229  if (random_factor > 0.0) dealii::GridTools::distort_random (random_factor, *grid, keep_boundary);
230 
231  // Create DG object using the factory
232  std::shared_ptr < DGBase<dim, double> > dg = DGFactory<dim,double>::create_discontinuous_galerkin(&param, poly_degree, grid);
233  dg->allocate_system ();
234 
235  // Initialize solution with vortex function at time t=0
236  dealii::VectorTools::interpolate(dg->dof_handler, initial_vortex_function, dg->solution);
237  // dealii::AffineConstraints<double> constraints;
238  // constraints.close();
239  // dealii::VectorTools::project (dg->dof_handler,
240  // constraints,
241  // dg->volume_quadrature_collection,
242  // initial_vortex_function,
243  // dg->solution);
244 
245  // Create ODE solver using the factory and providing the DG object
246  std::shared_ptr<ODE::ODESolverBase<dim, double>> ode_solver = ODE::ODESolverFactory<dim, double>::create_ODESolver(dg);
247 
248  const unsigned int n_active_cells = grid->n_active_cells();
249  const unsigned int n_dofs = dg->dof_handler.n_dofs();
250  std::cout
251  << "Dimension: " << dim << "\t Polynomial degree p: " << poly_degree << std::endl
252  << "Grid number: " << igrid+1 << "/" << n_grids
253  << ". Number of active cells: " << n_active_cells
254  << ". Number of degrees of freedom: " << n_dofs
255  << std::endl;
256 
257  // Not really steady state
258  // Will have a low number of time steps in the control file (around 10-20)
259  // We can then compare the exact solution to whatever time it reached
260  ode_solver->steady_state();
261 
262  EulerVortexFunction<dim,double> final_vortex_function(*euler, initial_vortex_center, vortex_strength, vortex_stddev_decay);
263  const double final_time = ode_solver->current_time;
264  final_vortex_function.set_time(final_time);
265 
266  // Overintegrate the error to make sure there is not integration error in the error estimate
267  int overintegrate = 10;
268  dealii::QGauss<dim> quad_extra(dg->max_degree+1+overintegrate);
269  dealii::MappingQ<dim,dim> mappingq(dg->max_degree+overintegrate);
270  dealii::FEValues<dim,dim> fe_values_extra(mappingq, dg->fe_collection[poly_degree], quad_extra,
271  dealii::update_values | dealii::update_JxW_values | dealii::update_quadrature_points);
272  const unsigned int n_quad_pts = fe_values_extra.n_quadrature_points;
273  std::array<double,nstate> soln_at_q;
274 
275  double l2error = 0;
276 
277  std::vector<dealii::types::global_dof_index> dofs_indices (fe_values_extra.dofs_per_cell);
278  // Integrate solution error
279  for (auto cell = dg->dof_handler.begin_active(); cell!=dg->dof_handler.end(); ++cell) {
280 
281  fe_values_extra.reinit (cell);
282  cell->get_dof_indices (dofs_indices);
283 
284  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
285 
286  std::fill(soln_at_q.begin(), soln_at_q.end(), 0);
287  for (unsigned int idof=0; idof<fe_values_extra.dofs_per_cell; ++idof) {
288  const unsigned int istate = fe_values_extra.get_fe().system_to_component_index(idof).first;
289  soln_at_q[istate] += dg->solution[dofs_indices[idof]] * fe_values_extra.shape_value_component(idof, iquad, istate);
290  }
291 
292  const dealii::Point<dim> qpoint = (fe_values_extra.quadrature_point(iquad));
293 
294  // Check only density
295  // Check only energy
296  for (int istate=3; istate<4; ++istate) {
297  const double uexact = final_vortex_function.value(qpoint, istate);
298  l2error += pow(soln_at_q[istate] - uexact, 2) * fe_values_extra.JxW(iquad);
299  }
300  }
301 
302  }
303  l2error = sqrt(l2error);
304 
305  // Convergence table
306  double dx = 1.0/pow(n_dofs,(1.0/dim));
307  //dx = dealii::GridTools::maximal_cell_diameter(*grid);
308  grid_size[igrid] = dx;
309  soln_error[igrid] = l2error;
310 
311  convergence_table.add_value("p", poly_degree);
312  convergence_table.add_value("cells", n_active_cells);
313  convergence_table.add_value("DoFs", n_dofs);
314  convergence_table.add_value("dx", dx);
315  convergence_table.add_value("soln_L2_error", l2error);
316 
317 
318  std::cout << " Grid size h: " << dx
319  << " L2-soln_error: " << l2error
320  << " Residual: " << ode_solver->residual_norm
321  << std::endl;
322 
323  if (igrid > 0) {
324  const double slope_soln_err = log(soln_error[igrid]/soln_error[igrid-1])
325  / log(grid_size[igrid]/grid_size[igrid-1]);
326  std::cout << "From grid " << igrid-1
327  << " to grid " << igrid
328  << " dimension: " << dim
329  << " polynomial degree p: " << poly_degree
330  << std::endl
331  << " solution_error1 " << soln_error[igrid-1]
332  << " solution_error2 " << soln_error[igrid]
333  << " slope " << slope_soln_err
334  << std::endl;
335  }
336  }
337  std::cout
338  << " ********************************************"
339  << std::endl
340  << " Convergence rates for p = " << poly_degree
341  << std::endl
342  << " ********************************************"
343  << std::endl;
344  convergence_table.evaluate_convergence_rates("soln_L2_error", "cells", dealii::ConvergenceTable::reduction_rate_log2, dim);
345  convergence_table.set_scientific("dx", true);
346  convergence_table.set_scientific("soln_L2_error", true);
347  convergence_table.write_text(std::cout);
348 
349  convergence_table_vector.push_back(convergence_table);
350 
351  const double expected_slope = poly_degree+1;
352 
353  const double last_slope = log(soln_error[n_grids-1]/soln_error[n_grids-2])
354  / log(grid_size[n_grids-1]/grid_size[n_grids-2]);
355  double before_last_slope = last_slope;
356  if ( n_grids > 2 ) {
357  before_last_slope = log(soln_error[n_grids-2]/soln_error[n_grids-3])
358  / log(grid_size[n_grids-2]/grid_size[n_grids-3]);
359  }
360  // Don't actually take the average for this case
361  const double slope_avg = 0.5*before_last_slope+ 0.5*last_slope;
362  const double slope_diff = slope_avg-expected_slope;
363 
364  double slope_deficit_tolerance = -std::abs(manu_grid_conv_param.slope_deficit_tolerance);
365  if(poly_degree == 0) slope_deficit_tolerance *= 2; // Otherwise, grid sizes need to be much bigger for p=0
366 
367  if (slope_diff < slope_deficit_tolerance) {
368  std::cout << std::endl
369  << "Convergence order not achieved. Average last 2 slopes of "
370  << slope_avg << " instead of expected "
371  << expected_slope << " within a tolerance of "
372  << slope_deficit_tolerance
373  << std::endl;
374  // p=0 just requires too many meshes to get into the asymptotic region.
375  if(poly_degree!=0) fail_conv_poly.push_back(poly_degree);
376  if(poly_degree!=0) fail_conv_slop.push_back(slope_avg);
377  }
378 
379  }
380  std::cout << std::endl
381  << std::endl
382  << std::endl
383  << std::endl;
384  std::cout << " ********************************************"
385  << std::endl;
386  std::cout << " Convergence summary"
387  << std::endl;
388  std::cout << " ********************************************"
389  << std::endl;
390  for (auto conv = convergence_table_vector.begin(); conv!=convergence_table_vector.end(); conv++) {
391  conv->write_text(std::cout);
392  std::cout << " ********************************************"
393  << std::endl;
394  }
395  int n_fail_poly = fail_conv_poly.size();
396  if (n_fail_poly > 0) {
397  for (int ifail=0; ifail < n_fail_poly; ++ifail) {
398  const double expected_slope = fail_conv_poly[ifail]+1;
399  const double slope_deficit_tolerance = -0.1;
400  std::cout << std::endl
401  << "Convergence order not achieved for polynomial p = "
402  << fail_conv_poly[ifail]
403  << ". Slope of "
404  << fail_conv_slop[ifail] << " instead of expected "
405  << expected_slope << " within a tolerance of "
406  << slope_deficit_tolerance
407  << std::endl;
408  }
409  }
410  return n_fail_poly;
411 }
412 
413 #if PHILIP_DIM==2
415 #endif
416 
417 
418 } // Tests namespace
419 } // PHiLiP namespace
420 
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
Performs grid convergence for various polynomial degrees.
Definition: euler_vortex.h:53
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
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.
real value(const dealii::Point< dim > &point, const unsigned int istate=0) const
Manufactured solution exact value.
dealii::Point< dim > advected_location(const dealii::Point< dim > old_location) const
Exact solution using the current time provided by the dealii::Function class.
int run_test() const
Manufactured grid convergence.
EulerVortexFunction(const Physics::Euler< dim, dim+2, real > euler_physics, const dealii::Point< dim > initial_vortex_center, const real vortex_strength, const real vortex_stddev_decay)
Constructor that initializes base_values, amplitudes, frequencies.
EulerVortex()=delete
Constructor. Deleted the default constructor since it should not be used.
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) ...
Base class of all the tests.
Definition: tests.h:17