6 #include <deal.II/dofs/dof_tools.h> 8 #include <deal.II/distributed/tria.h> 9 #include <deal.II/grid/tria.h> 11 #include <deal.II/grid/grid_generator.h> 12 #include <deal.II/grid/grid_refinement.h> 13 #include <deal.II/grid/grid_tools.h> 14 #include <deal.II/grid/grid_out.h> 15 #include <deal.II/grid/grid_in.h> 17 #include <deal.II/numerics/vector_tools.h> 19 #include <deal.II/fe/fe_values.h> 24 #include "mesh/grids/curved_periodic_grid.hpp" 26 #include "grid_study.h" 28 #include "physics/physics_factory.h" 29 #include "physics/model_factory.h" 30 #include "physics/manufactured_solution.h" 31 #include "dg/dg_factory.hpp" 32 #include "ode_solver/ode_solver_factory.h" 34 #include "grid_refinement/grid_refinement.h" 35 #include "grid_refinement/gmsh_out.h" 36 #include "grid_refinement/size_field.h" 41 template <
int dim,
int nstate>
47 template <
int dim,
int nstate>
51 dealii::LinearAlgebra::distributed::Vector<double> solution_no_ghost;
63 template <
int dim,
int nstate>
67 pcout <<
"Evaluating solution integral..." << std::endl;
68 double solution_integral = 0.0;
75 int overintegrate = 10;
76 dealii::QGauss<dim> quad_extra(dg.
max_degree+1+overintegrate);
79 dealii::update_values | dealii::update_JxW_values | dealii::update_quadrature_points);
80 const unsigned int n_quad_pts = fe_values_extra.n_quadrature_points;
81 std::array<double,nstate> soln_at_q;
83 const bool linear_output =
true;
85 if (linear_output) exponent = 1;
89 std::vector<dealii::types::global_dof_index> dofs_indices (fe_values_extra.dofs_per_cell);
90 for (
auto cell : dg.
dof_handler.active_cell_iterators()) {
92 if (!cell->is_locally_owned())
continue;
94 fe_values_extra.reinit (cell);
95 cell->get_dof_indices (dofs_indices);
97 for (
unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
100 std::fill(soln_at_q.begin(), soln_at_q.end(), 0.0);
101 for (
unsigned int idof=0; idof<fe_values_extra.dofs_per_cell; ++idof) {
102 const unsigned int istate = fe_values_extra.get_fe().system_to_component_index(idof).first;
103 soln_at_q[istate] += dg.
solution[dofs_indices[idof]] * fe_values_extra.shape_value_component(idof, iquad, istate);
106 for (
int s=0; s<nstate; s++) {
107 solution_integral += pow(soln_at_q[s], exponent) * fe_values_extra.JxW(iquad);
112 const double solution_integral_mpi_sum = dealii::Utilities::MPI::sum(solution_integral,
mpi_communicator);
113 return solution_integral_mpi_sum;
116 template<
int dim,
int nstate>
121 std::string error_filename_baseline =
"convergence_table";
128 error_filename_baseline += std::string(
"_") + std::to_string(dim) + std::string(
"d");
129 error_filename_baseline += std::string(
"_") + pde_string;
130 error_filename_baseline += std::string(
"_") + conv_num_flux_string;
131 error_filename_baseline += std::string(
"_") + diss_num_flux_string;
132 error_filename_baseline += std::string(
"_") + manufactured_solution_string;
133 return error_filename_baseline;
136 template<
int dim,
int nstate>
139 const std::string error_filename_baseline,
140 const dealii::ConvergenceTable convergence_table,
141 const unsigned int poly_degree)
const 143 std::string error_filename = error_filename_baseline;
144 std::string error_fileType = std::string(
"txt");
145 error_filename += std::string(
"_") + std::string(
"p") + std::to_string(poly_degree);
146 std::ofstream error_table_file(error_filename + std::string(
".") + error_fileType);
147 convergence_table.write_text(error_table_file);
151 template<
int dim,
int nstate>
157 using GridEnum = ManParam::GridEnum;
166 const unsigned int p_start = manu_grid_conv_param.
degree_start;
167 const unsigned int p_end = manu_grid_conv_param.degree_end;
169 const unsigned int n_grids_input = manu_grid_conv_param.number_of_grids;
175 double exact_solution_integral;
176 pcout <<
"Evaluating EXACT solution integral..." << std::endl;
179 using Triangulation = dealii::Triangulation<dim>;
181 using Triangulation = dealii::parallel::distributed::Triangulation<dim>;
185 std::shared_ptr<Triangulation> grid_super_fine = std::make_shared<Triangulation>(
189 typename dealii::Triangulation<dim>::MeshSmoothing(
190 dealii::Triangulation<dim>::smoothing_on_refinement |
191 dealii::Triangulation<dim>::smoothing_on_coarsening));
193 dealii::GridGenerator::subdivided_hyper_cube(*grid_super_fine, n_1d_cells[n_grids_input-1]);
206 for (
auto cell = grid_super_fine->begin_active(); cell != grid_super_fine->end(); ++cell) {
207 for (
unsigned int face=0; face<dealii::GeometryInfo<dim>::faces_per_cell; ++face) {
208 if (cell->face(face)->at_boundary()) cell->face(face)->set_boundary_id (1000);
213 dg_super_fine->allocate_system ();
216 if (manu_grid_conv_param.output_solution) {
217 dg_super_fine->output_results_vtk(9999);
220 pcout <<
"Exact solution integral is " << exact_solution_integral << std::endl;
223 int n_flow_convergence_error = 0;
224 std::vector<int> fail_conv_poly;
225 std::vector<double> fail_conv_slop;
226 std::vector<dealii::ConvergenceTable> convergence_table_vector;
228 std::string error_filename_baseline;
229 if (manu_grid_conv_param.output_convergence_tables) {
233 for (
unsigned int poly_degree = p_start; poly_degree <= p_end; ++poly_degree) {
236 unsigned int n_grids = n_grids_input;
237 if (poly_degree <= 1) n_grids = n_grids_input + 1;
239 std::vector<double> soln_error(n_grids);
240 std::vector<double> output_error(n_grids);
241 std::vector<double> grid_size(n_grids);
245 dealii::ConvergenceTable convergence_table;
251 std::shared_ptr<Triangulation> grid = std::make_shared<Triangulation>(
255 typename dealii::Triangulation<dim>::MeshSmoothing(
256 dealii::Triangulation<dim>::smoothing_on_refinement |
257 dealii::Triangulation<dim>::smoothing_on_coarsening));
259 dealii::Vector<float> estimated_error_per_cell;
260 dealii::Vector<double> estimated_error_per_cell_double;
262 for (
unsigned int igrid=0; igrid<n_grids; ++igrid) {
264 dealii::GridGenerator::subdivided_hyper_cube(*grid, n_1d_cells[igrid]);
274 for (
auto cell = grid->begin_active(); cell != grid->end(); ++cell) {
276 cell->set_material_id(9002);
277 for (
unsigned int face=0; face<dealii::GeometryInfo<dim>::faces_per_cell; ++face) {
278 if (cell->face(face)->at_boundary()) cell->face(face)->set_boundary_id (1000);
283 if (manu_grid_conv_param.grid_type == GridEnum::sinehypercube) dealii::GridTools::transform (&
warp, *grid);
335 const double random_factor = manu_grid_conv_param.random_distortion;
336 const bool keep_boundary =
true;
337 if (random_factor > 0.0) dealii::GridTools::distort_random (random_factor, *grid, keep_boundary);
340 if (manu_grid_conv_param.grid_type == GridEnum::read_grid) {
342 std::string read_mshname = manu_grid_conv_param.input_grids+std::to_string(igrid)+
".msh";
343 pcout<<
"Reading grid: " << read_mshname << std::endl;
344 std::ifstream inmesh(read_mshname);
345 dealii::GridIn<dim,dim> grid_in;
347 grid_in.attach_triangulation(*grid);
348 grid_in.read_msh(inmesh);
351 if (manu_grid_conv_param.output_meshes) {
352 std::string write_mshname =
"grid-"+std::to_string(igrid)+
".msh";
353 std::ofstream outmesh(write_mshname);
354 dealii::GridOutFlags::Msh msh_flags(
true,
true);
355 dealii::GridOut grid_out;
356 grid_out.set_flags(msh_flags);
357 grid_out.write_msh(*grid, outmesh);
364 using FadType = Sacado::Fad::DFad<double>;
368 dg->allocate_system ();
378 const unsigned int n_global_active_cells = grid->n_global_active_cells();
379 const unsigned int n_dofs = dg->dof_handler.n_dofs();
380 pcout <<
"Dimension: " << dim
381 <<
"\t Polynomial degree p: " << poly_degree
383 <<
"Grid number: " << igrid+1 <<
"/" << n_grids
384 <<
". Number of active cells: " << n_global_active_cells
385 <<
". Number of degrees of freedom: " << n_dofs
390 const int flow_convergence_error = ode_solver->steady_state();
391 if (flow_convergence_error) n_flow_convergence_error += 1;
394 int overintegrate = 10;
395 dealii::QGauss<dim> quad_extra(dg->max_degree+overintegrate);
397 dealii::FEValues<dim,dim> fe_values_extra(*(dg->high_order_grid->mapping_fe_field), dg->fe_collection[poly_degree], quad_extra,
398 dealii::update_values | dealii::update_JxW_values | dealii::update_quadrature_points);
399 const unsigned int n_quad_pts = fe_values_extra.n_quadrature_points;
400 std::array<double,nstate> soln_at_q;
403 std::array<double,nstate> cell_l2error_state;
404 std::array<double,nstate> l2error_state;
405 for (
int istate=0; istate<nstate; ++istate) {
406 l2error_state[istate] = 0.0;
413 std::vector<dealii::types::global_dof_index> dofs_indices (fe_values_extra.dofs_per_cell);
414 estimated_error_per_cell.reinit(grid->n_active_cells());
415 estimated_error_per_cell_double.reinit(grid->n_active_cells());
416 for (
auto cell = dg->dof_handler.begin_active(); cell!=dg->dof_handler.end(); ++cell) {
418 if (!cell->is_locally_owned())
continue;
420 fe_values_extra.reinit (cell);
421 cell->get_dof_indices (dofs_indices);
423 double cell_l2error = 0;
425 for (
int istate=0; istate<nstate; ++istate) {
426 cell_l2error_state[istate] = 0.0;
429 for (
unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
431 std::fill(soln_at_q.begin(), soln_at_q.end(), 0);
432 for (
unsigned int idof=0; idof<fe_values_extra.dofs_per_cell; ++idof) {
433 const unsigned int istate = fe_values_extra.get_fe().system_to_component_index(idof).first;
434 soln_at_q[istate] += dg->solution[dofs_indices[idof]] * fe_values_extra.shape_value_component(idof, iquad, istate);
437 const dealii::Point<dim> qpoint = (fe_values_extra.quadrature_point(iquad));
439 for (
int istate=0; istate<nstate; ++istate) {
440 const double uexact = physics_double->manufactured_solution_function->value(qpoint, istate);
443 cell_l2error += pow(soln_at_q[istate] - uexact, 2) * fe_values_extra.JxW(iquad);
445 cell_l2error_state[istate] += pow(soln_at_q[istate] - uexact, 2) * fe_values_extra.JxW(iquad);
448 estimated_error_per_cell[cell->active_cell_index()] = cell_l2error;
449 estimated_error_per_cell_double[cell->active_cell_index()] = cell_l2error;
450 l2error += cell_l2error;
452 for (
int istate=0; istate<nstate; ++istate) {
453 l2error_state[istate] += cell_l2error_state[istate];
456 const double l2error_mpi_sum = std::sqrt(dealii::Utilities::MPI::sum(l2error,
mpi_communicator));
458 std::array<double,nstate> l2error_mpi_sum_state;
459 for (
int istate=0; istate<nstate; ++istate) {
460 l2error_mpi_sum_state[istate] = std::sqrt(dealii::Utilities::MPI::sum(l2error_state[istate],
mpi_communicator));
465 if (manu_grid_conv_param.output_solution) {
482 std::shared_ptr< GridRefinement::GridRefinementBase<dim,nstate,double> > gr
484 gr->output_results_vtk(igrid);
489 const double dx = 1.0/pow(n_dofs,(1.0/dim));
490 grid_size[igrid] = dx;
491 soln_error[igrid] = l2error_mpi_sum;
492 output_error[igrid] = std::abs(solution_integral - exact_solution_integral);
494 convergence_table.add_value(
"p", poly_degree);
495 convergence_table.add_value(
"cells", n_global_active_cells);
496 convergence_table.add_value(
"DoFs", n_dofs);
497 convergence_table.add_value(
"dx", dx);
498 convergence_table.add_value(
"residual", dg->get_residual_l2norm ());
499 convergence_table.add_value(
"soln_L2_error", l2error_mpi_sum);
500 convergence_table.add_value(
"output_error", output_error[igrid]);
503 if (manu_grid_conv_param.add_statewise_solution_error_to_convergence_tables) {
504 std::array<std::string,nstate> soln_L2_error_state_str;
505 for (
int istate=0; istate<nstate; ++istate) {
506 soln_L2_error_state_str[istate] = std::string(
"soln_L2_error_state") + std::string(
"_") + std::to_string(istate);
507 convergence_table.add_value(soln_L2_error_state_str[istate], l2error_mpi_sum_state[istate]);
511 pcout <<
" Grid size h: " << dx
512 <<
" L2-soln_error: " << l2error_mpi_sum
513 <<
" Residual: " << ode_solver->residual_norm
516 pcout <<
" output_exact: " << exact_solution_integral
517 <<
" output_discrete: " << solution_integral
518 <<
" output_error: " << output_error[igrid]
522 const double slope_soln_err = log(soln_error[igrid]/soln_error[igrid-1])
523 / log(grid_size[igrid]/grid_size[igrid-1]);
524 const double slope_output_err = log(output_error[igrid]/output_error[igrid-1])
525 / log(grid_size[igrid]/grid_size[igrid-1]);
526 pcout <<
"From grid " << igrid-1
527 <<
" to grid " << igrid
528 <<
" dimension: " << dim
529 <<
" polynomial degree p: " << poly_degree
531 <<
" solution_error1 " << soln_error[igrid-1]
532 <<
" solution_error2 " << soln_error[igrid]
533 <<
" slope " << slope_soln_err
535 <<
" solution_integral_error1 " << output_error[igrid-1]
536 <<
" solution_integral_error2 " << output_error[igrid]
537 <<
" slope " << slope_output_err
542 convergence_table.evaluate_convergence_rates(
"soln_L2_error",
"cells", dealii::ConvergenceTable::reduction_rate_log2, dim);
543 convergence_table.evaluate_convergence_rates(
"output_error",
"cells", dealii::ConvergenceTable::reduction_rate_log2, dim);
544 convergence_table.set_scientific(
"dx",
true);
545 convergence_table.set_scientific(
"residual",
true);
546 convergence_table.set_scientific(
"soln_L2_error",
true);
547 convergence_table.set_scientific(
"output_error",
true);
548 if (manu_grid_conv_param.add_statewise_solution_error_to_convergence_tables) {
549 std::string test_str;
550 for (
int istate=0; istate<nstate; ++istate) {
551 test_str = std::string(
"soln_L2_error_state") + std::string(
"_") + std::to_string(istate);
552 convergence_table.set_scientific(test_str,
true);
556 if (manu_grid_conv_param.output_convergence_tables) {
558 error_filename_baseline,
563 pcout <<
" ********************************************" 565 <<
" Convergence rates for p = " << poly_degree
567 <<
" ********************************************" 570 if (
pcout.is_active()) convergence_table.write_text(
pcout.get_stream());
572 convergence_table_vector.push_back(convergence_table);
574 const double expected_slope = poly_degree+1;
576 double last_slope = 0.0;
578 last_slope = log(soln_error[n_grids-1]/soln_error[n_grids-2])
579 / log(grid_size[n_grids-1]/grid_size[n_grids-2]);
581 double before_last_slope = last_slope;
583 before_last_slope = log(soln_error[n_grids-2]/soln_error[n_grids-3])
584 / log(grid_size[n_grids-2]/grid_size[n_grids-3]);
586 const double slope_avg = 0.5*(before_last_slope+last_slope);
587 const double slope_diff = slope_avg-expected_slope;
589 double slope_deficit_tolerance = -std::abs(manu_grid_conv_param.slope_deficit_tolerance);
590 if(poly_degree == 0) slope_deficit_tolerance *= 2;
592 if (slope_diff < slope_deficit_tolerance) {
594 <<
"Convergence order not achieved. Average last 2 slopes of " 595 << slope_avg <<
" instead of expected " 596 << expected_slope <<
" within a tolerance of " 597 << slope_deficit_tolerance
600 if(poly_degree!=0) fail_conv_poly.push_back(poly_degree);
601 if(poly_degree!=0) fail_conv_slop.push_back(slope_avg);
605 pcout << std::endl << std::endl << std::endl << std::endl;
606 pcout <<
" ********************************************" << std::endl;
607 pcout <<
" Convergence summary" << std::endl;
608 pcout <<
" ********************************************" << std::endl;
609 for (
auto conv = convergence_table_vector.begin(); conv!=convergence_table_vector.end(); conv++) {
610 if (
pcout.is_active()) conv->write_text(
pcout.get_stream());
611 pcout <<
" ********************************************" << std::endl;
613 int n_fail_poly = fail_conv_poly.size();
614 if (n_fail_poly > 0) {
615 for (
int ifail=0; ifail < n_fail_poly; ++ifail) {
616 const double expected_slope = fail_conv_poly[ifail]+1;
617 const double slope_deficit_tolerance = -std::abs(manu_grid_conv_param.slope_deficit_tolerance);
619 <<
"Convergence order not achieved for polynomial p = " 620 << fail_conv_poly[ifail]
622 << fail_conv_slop[ifail] <<
" instead of expected " 623 << expected_slope <<
" within a tolerance of " 624 << slope_deficit_tolerance
628 if (n_fail_poly) test_fail += 1;
629 test_fail += n_flow_convergence_error;
630 if (n_flow_convergence_error) {
632 <<
"Flow did not converge some some cases. Please check the residuals achieved versus the residual tolerance." 638 template <
int dim,
int nstate>
642 dealii::Point<dim> q = p;
644 if (dim >= 2) q[0] += 1*std::sin(q[dim-1]);
645 if (dim >= 3) q[1] += 1*std::cos(q[dim-1]);
649 template <
int dim,
int nstate>
651 ::print_mesh_info(
const dealii::Triangulation<dim> &triangulation,
const std::string &filename)
const 653 pcout <<
"Mesh info:" << std::endl
654 <<
" dimension: " << dim << std::endl
655 <<
" no. of cells: " << triangulation.n_global_active_cells() << std::endl;
656 std::map<dealii::types::boundary_id, unsigned int> boundary_count;
657 for (
auto cell : triangulation.active_cell_iterators()) {
658 for (
unsigned int face=0; face < dealii::GeometryInfo<dim>::faces_per_cell; ++face) {
659 if (cell->face(face)->at_boundary()) boundary_count[cell->face(face)->boundary_id()]++;
662 pcout <<
" boundary indicators: ";
663 for (
const std::pair<const dealii::types::boundary_id, unsigned int> &pair : boundary_count) {
664 pcout << pair.first <<
"(" << pair.second <<
" times) ";
668 std::ofstream out (filename);
669 dealii::GridOut grid_out;
670 grid_out.write_eps (triangulation, out);
671 pcout <<
" written to " << filename << std::endl << std::endl;
unsigned int degree_start
First polynomial degree to start the loop. If diffusion, must be at least 1.
std::string get_convergence_tables_baseline_filename(const Parameters::AllParameters *const parameters_input) const
Returns the baseline filename for outputted convergence tables.
double integrate_solution_over_domain(DGBase< dim, double > &dg) const
L2-Integral of the solution over the entire domain.
Performs grid convergence for various polynomial degrees.
Sacado::Fad::DFad< double > FadType
Sacado AD type for first derivatives.
int run_test() const
Manufactured grid convergence.
Parameters related to the manufactured convergence study.
const MPI_Comm mpi_communicator
MPI communicator.
Files for the baseline physics.
GridRefinementStudyParam grid_refinement_study_param
Contains the parameters for grid refinement study.
std::array< GridRefinementParam, MAX_REFINEMENTS > grid_refinement_param_vector
Array of grid refinement parameters to be run as part of grid refinement study.
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.
dealii::LinearAlgebra::distributed::Vector< double > solution
Current modal coefficients of the solution.
ManufacturedConvergenceStudyParam manufactured_convergence_study_param
Contains parameters for manufactured convergence study.
dealii::IndexSet locally_owned_dofs
Locally own degrees of freedom.
const Parameters::AllParameters *const all_parameters
Pointer to all parameters.
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.
const unsigned int max_degree
Maximum degree used for p-refi1nement.
std::string get_conv_num_flux_string(const Parameters::AllParameters *const param) const
Returns a string describing which convective numerical flux is being used.
static std::shared_ptr< ModelBase< dim, nstate, real > > create_Model(const Parameters::AllParameters *const parameters_input)
Factory to return the correct model given input parameters.
std::shared_ptr< HighOrderGrid< dim, real, MeshType > > high_order_grid
High order grid that will provide the MappingFEField.
void write_convergence_table_to_output_file(const std::string error_filename_baseline, const dealii::ConvergenceTable convergence_table, const unsigned int poly_degree) const
Writes the convergence table output file.
GridStudy()=delete
Constructor. Deleted the default constructor since it should not be used.
std::string get_pde_string(const Parameters::AllParameters *const param) const
Returns a string describing which PDE is being used.
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::string get_diss_num_flux_string(const Parameters::AllParameters *const param) const
Returns a string describing which dissipative numerical flux is being used.
dealii::DoFHandler< dim > dof_handler
Finite Element Collection to represent the high-order grid.
static std::shared_ptr< GridRefinementBase< dim, nstate, real, MeshType > > create_GridRefinement(PHiLiP::Parameters::GridRefinementParam gr_param, std::shared_ptr< PHiLiP::Adjoint< dim, nstate, real, MeshType > > adj, std::shared_ptr< PHiLiP::Physics::PhysicsBase< dim, nstate, real > > physics_input)
Construct grid refinement class based on adjoint and physics.
void print_mesh_info(const dealii::Triangulation< dim > &triangulation, const std::string &filename) const
Prints our mesh info and generates eps file if 2D grid.
std::string get_manufactured_solution_string(const Parameters::AllParameters *const param) const
Returns a string describing which manufactured solution is being used.
dealii::ConditionalOStream pcout
ConditionalOStream.
void initialize_perturbed_solution(DGBase< dim, double > &dg, const Physics::PhysicsBase< dim, nstate, double > &physics) const
Initialize the solution with the exact solution.
DGBase is independent of the number of state variables.
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.
std::shared_ptr< ManufacturedSolutionFunction< dim, real > > manufactured_solution_function
Manufactured solution function.
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) ...
const dealii::hp::FECollection< dim > fe_collection
Finite Element Collection for p-finite-element to represent the solution.
Base class of all the tests.
static dealii::Point< dim > warp(const dealii::Point< dim > &p)
Warps mesh into sinusoidal.