[P]arallel [Hi]gh-order [Li]brary for [P]DEs  Latest
Parallel High-Order Library for PDEs through hp-adaptive Discontinuous Galerkin methods
periodic_entropy_tests.cpp
1 #include "periodic_entropy_tests.h"
2 #include "physics/physics_factory.h"
3 
4 namespace PHiLiP {
5 
6 namespace FlowSolver {
7 
8 //=========================================================
9 // TEST FOR ENTROPY CONSERVATION/STABILITY ON PERIODIC DOMAINS (EULER/NS)
10 //=========================================================
11 
12 template <int dim, int nstate>
14  : PeriodicCubeFlow<dim, nstate>(parameters_input)
15  , unsteady_data_table_filename_with_extension(this->all_param.flow_solver_param.unsteady_data_table_filename+".txt")
16 {
17  this->euler_physics = std::dynamic_pointer_cast<Physics::Euler<dim,dim+2,double>>(
19 }
20 
21 template <int dim, int nstate>
23 {
24 
25  using FlowCaseEnum = Parameters::FlowSolverParam::FlowCaseType;
26  const FlowCaseEnum flow_case = this->all_param.flow_solver_param.flow_case_type;
28  // For Euler simulations, use CFL
29  const unsigned int number_of_degrees_of_freedom_per_state = dg->dof_handler.n_dofs()/nstate;
30  const double approximate_grid_spacing = (this->domain_right-this->domain_left)/pow(number_of_degrees_of_freedom_per_state,(1.0/dim));
31 
32  double constant_time_step=0;
33  if (flow_case == FlowCaseEnum::isentropic_vortex){
34  // Using dt = CFL * delta_x/U_infinity, consistent with Ranocha's choice (Relaxation Runge Kutta methods... 2020)
35  // U_infinity is initialized as M_infinity
36  constant_time_step = CFL * approximate_grid_spacing / this->all_param.euler_param.mach_inf;
37  } else if (flow_case == FlowCaseEnum::kelvin_helmholtz_instability){
38  /*
39  const double max_wave_speed = this->compute_integrated_quantities(*dg, IntegratedQuantityEnum::max_wave_speed);
40  constant_time_step = CFL * approximate_grid_spacing / max_wave_speed;
41  */
42  // TEMP using same as is defined in periodic turbulence for consistency with some existing results
43  const double constant_time_step = this->all_param.flow_solver_param.courant_friedrichs_lewy_number * approximate_grid_spacing;
44  return constant_time_step;
45  } else{
46  this->pcout << "Timestep size has not been defined in periodic_entropy_tests for this flow_case_type. Aborting..." << std::endl;
47  std::abort();
48  }
49 
50  return constant_time_step;
51 }
52 
53 template<int dim, int nstate>
55 {
56  // Check that poly_degree is uniform everywhere
57  if (dg.get_max_fe_degree() != dg.get_min_fe_degree()) {
58  // Note: This function may have issues with nonuniform p. Should test in debug mode if developing in the future.
59  this->pcout << "ERROR: compute_integrated_quantities() is untested for nonuniform p. Aborting..." << std::endl;
60  std::abort();
61  }
62 
63  double integrated_quantity = 0.0;
64 
65  const unsigned int grid_degree = dg.high_order_grid->fe_system.tensor_degree();
66  const unsigned int poly_degree = dg.max_degree;
67 
68  // Set the quadrature of size dim and 1D for sum-factorization.
69  dealii::Quadrature<1> quad_1D;
70  std::vector<double> quad_weights_;
71  unsigned int n_quad_pts_;
72  if (overintegrate > 0) {
73  dealii::QGauss<dim> quad_extra(dg.max_degree+1+overintegrate);
74  dealii::QGauss<1> quad_extra_1D(dg.max_degree+1+overintegrate);
75  quad_1D = quad_extra_1D;
76  quad_weights_ = quad_extra.get_weights();
77  n_quad_pts_ = quad_extra.size();
78  } else {
79  quad_1D = dg.oneD_quadrature_collection[poly_degree];
80  quad_weights_ = dg.volume_quadrature_collection[poly_degree].get_weights();
81  n_quad_pts_ = dg.volume_quadrature_collection[poly_degree].size();
82  }
83  const std::vector<double> &quad_weights = quad_weights_;
84  const unsigned int n_quad_pts = n_quad_pts_;
85 
86  // Construct the basis functions and mapping shape functions.
87  OPERATOR::basis_functions<dim,2*dim,double> soln_basis(1, poly_degree, grid_degree);
88  OPERATOR::mapping_shape_functions<dim,2*dim,double> mapping_basis(1, poly_degree, grid_degree);
89  // Build basis function volume operator and gradient operator from 1D finite element for 1 state.
90  soln_basis.build_1D_volume_operator(dg.oneD_fe_collection_1state[poly_degree], quad_1D);
91  soln_basis.build_1D_gradient_operator(dg.oneD_fe_collection_1state[poly_degree], quad_1D);
92  // Build mapping shape functions operators using the oneD high_ordeR_grid finite element
93  mapping_basis.build_1D_shape_functions_at_grid_nodes(dg.high_order_grid->oneD_fe_system, dg.high_order_grid->oneD_grid_nodes);
94  mapping_basis.build_1D_shape_functions_at_flux_nodes(dg.high_order_grid->oneD_fe_system, quad_1D, dg.oneD_face_quadrature);
95  // If in the future we need the physical quadrature node location, turn these flags to true and the constructor will
96  // automatically compute it for you. Currently set to false as to not compute extra unused terms.
97  const bool store_vol_flux_nodes = false;//currently doesn't need the volume physical nodal position
98  const bool store_surf_flux_nodes = false;//currently doesn't need the surface physical nodal position
99 
100  const unsigned int n_dofs = dg.fe_collection[poly_degree].n_dofs_per_cell();
101  const unsigned int n_shape_fns = n_dofs / nstate;
102  std::vector<dealii::types::global_dof_index> dofs_indices (n_dofs);
103  auto metric_cell = dg.high_order_grid->dof_handler_grid.begin_active();
104  // Changed for loop to update metric_cell.
105  for (auto cell = dg.dof_handler.begin_active(); cell!= dg.dof_handler.end(); ++cell, ++metric_cell) {
106  if (!cell->is_locally_owned()) continue;
107  cell->get_dof_indices (dofs_indices);
108 
109  // We first need to extract the mapping support points (grid nodes) from high_order_grid.
110  const dealii::FESystem<dim> &fe_metric = dg.high_order_grid->fe_system;
111  const unsigned int n_metric_dofs = fe_metric.dofs_per_cell;
112  const unsigned int n_grid_nodes = n_metric_dofs / dim;
113  std::vector<dealii::types::global_dof_index> metric_dof_indices(n_metric_dofs);
114  metric_cell->get_dof_indices (metric_dof_indices);
115  std::array<std::vector<double>,dim> mapping_support_points;
116  for(int idim=0; idim<dim; idim++){
117  mapping_support_points[idim].resize(n_grid_nodes);
118  }
119  // Get the mapping support points (physical grid nodes) from high_order_grid.
120  // Store it in such a way we can use sum-factorization on it with the mapping basis functions.
121  const std::vector<unsigned int > &index_renumbering = dealii::FETools::hierarchic_to_lexicographic_numbering<dim>(grid_degree);
122  for (unsigned int idof = 0; idof< n_metric_dofs; ++idof) {
123  const double val = (dg.high_order_grid->volume_nodes[metric_dof_indices[idof]]);
124  const unsigned int istate = fe_metric.system_to_component_index(idof).first;
125  const unsigned int ishape = fe_metric.system_to_component_index(idof).second;
126  const unsigned int igrid_node = index_renumbering[ishape];
127  mapping_support_points[istate][igrid_node] = val;
128  }
129  // Construct the metric operators.
130  OPERATOR::metric_operators<double, dim, 2*dim> metric_oper(nstate, poly_degree, grid_degree, store_vol_flux_nodes, store_surf_flux_nodes);
131  // Build the metric terms to compute the gradient and volume node positions.
132  // This functions will compute the determinant of the metric Jacobian and metric cofactor matrix.
133  // If flags store_vol_flux_nodes and store_surf_flux_nodes set as true it will also compute the physical quadrature positions.
134  metric_oper.build_volume_metric_operators(
135  n_quad_pts, n_grid_nodes,
136  mapping_support_points,
137  mapping_basis,
138  dg.all_parameters->use_invariant_curl_form);
139 
140  // Fetch the modal soln coefficients
141  // We immediately separate them by state as to be able to use sum-factorization
142  // in the interpolation operator. If we left it by n_dofs_cell, then the matrix-vector
143  // mult would sum the states at the quadrature point.
144  // That is why the basis functions are based off the 1state oneD fe_collection.
145  std::array<std::vector<double>,nstate> soln_coeff;
146  for (unsigned int idof = 0; idof < n_dofs; ++idof) {
147  const unsigned int istate = dg.fe_collection[poly_degree].system_to_component_index(idof).first;
148  const unsigned int ishape = dg.fe_collection[poly_degree].system_to_component_index(idof).second;
149  if(ishape == 0){
150  soln_coeff[istate].resize(n_shape_fns);
151  }
152 
153  soln_coeff[istate][ishape] = dg.solution(dofs_indices[idof]);
154  }
155  // Interpolate each state to the quadrature points using sum-factorization
156  // with the basis functions in each reference direction.
157  std::array<std::vector<double>,nstate> soln_at_q_vect;
158  std::array<dealii::Tensor<1,dim,std::vector<double>>,nstate> soln_grad_at_q_vect;
159  for(int istate=0; istate<nstate; istate++){
160  soln_at_q_vect[istate].resize(n_quad_pts);
161  // Interpolate soln coeff to volume cubature nodes.
162  soln_basis.matrix_vector_mult_1D(soln_coeff[istate], soln_at_q_vect[istate],
163  soln_basis.oneD_vol_operator);
164  // We need to first compute the reference gradient of the solution, then transform that to a physical gradient.
165  dealii::Tensor<1,dim,std::vector<double>> ref_gradient_basis_fns_times_soln;
166  for(int idim=0; idim<dim; idim++){
167  ref_gradient_basis_fns_times_soln[idim].resize(n_quad_pts);
168  soln_grad_at_q_vect[istate][idim].resize(n_quad_pts);
169  }
170  // Apply gradient of reference basis functions on the solution at volume cubature nodes.
171  soln_basis.gradient_matrix_vector_mult_1D(soln_coeff[istate], ref_gradient_basis_fns_times_soln,
172  soln_basis.oneD_vol_operator,
173  soln_basis.oneD_grad_operator);
174  // Transform the reference gradient into a physical gradient operator.
175  for(int idim=0; idim<dim; idim++){
176  for(unsigned int iquad=0; iquad<n_quad_pts; iquad++){
177  for(int jdim=0; jdim<dim; jdim++){
178  //transform into the physical gradient
179  soln_grad_at_q_vect[istate][idim][iquad] += metric_oper.metric_cofactor_vol[idim][jdim][iquad]
180  * ref_gradient_basis_fns_times_soln[jdim][iquad]
181  / metric_oper.det_Jac_vol[iquad];
182  }
183  }
184  }
185  }
186 
187  // Loop over quadrature nodes, compute quantities to be integrated, and integrate them.
188  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
189 
190  std::array<double,nstate> soln_at_q;
191  std::array<dealii::Tensor<1,dim,double>,nstate> soln_grad_at_q;
192  // Extract solution and gradient in a way that the physics ca n use them.
193  for(int istate=0; istate<nstate; istate++){
194  soln_at_q[istate] = soln_at_q_vect[istate][iquad];
195  for(int idim=0; idim<dim; idim++){
196  soln_grad_at_q[istate][idim] = soln_grad_at_q_vect[istate][idim][iquad];
197  }
198  }
199 
200  //#####################################################################
201  // Compute integrated quantities here
202  //#####################################################################
203  if (quantity == IntegratedQuantityEnum::kinetic_energy) {
204  const double KE_integrand = this->euler_physics->compute_kinetic_energy_from_conservative_solution(soln_at_q);
205  integrated_quantity += KE_integrand * quad_weights[iquad] * metric_oper.det_Jac_vol[iquad];
206  } else if (quantity == IntegratedQuantityEnum::numerical_entropy) {
207  const double quadrature_entropy = this->euler_physics->compute_numerical_entropy_function(soln_at_q);
208  //Using std::cout because of cell->is_locally_owned check
209  if (isnan(quadrature_entropy)) std::cout << "WARNING: NaN entropy detected at a node!" << std::endl;
210  integrated_quantity += quadrature_entropy * quad_weights[iquad] * metric_oper.det_Jac_vol[iquad];
211  } else if (quantity == IntegratedQuantityEnum::max_wave_speed) {
212  const double local_wave_speed = this->euler_physics->max_convective_eigenvalue(soln_at_q);
213  if(local_wave_speed > integrated_quantity) integrated_quantity = local_wave_speed;
214  } else {
215  std::cout << "Integrated quantity is not correctly defined." << std::endl;
216  }
217  //#####################################################################
218  }
219  }
220 
221  //MPI
222  if (quantity == IntegratedQuantityEnum::max_wave_speed) {
223  integrated_quantity = dealii::Utilities::MPI::max(integrated_quantity, this->mpi_communicator);
224  } else {
225  integrated_quantity = dealii::Utilities::MPI::sum(integrated_quantity, this->mpi_communicator);
226  }
227 
228  return integrated_quantity;
229 }
230 
231 
232 template <int dim, int nstate>
234  const std::shared_ptr <DGBase<dim, double>> dg
235  ) const
236 {
237  return compute_integrated_quantities(*dg, IntegratedQuantityEnum::numerical_entropy, 0);
238 }
239 
240 template <int dim, int nstate>
242  const std::shared_ptr<ODE::ODESolverBase<dim, double>> ode_solver,
243  const std::shared_ptr <DGBase<dim, double>> dg,
244  const std::shared_ptr<dealii::TableHandler> unsteady_data_table)
245 {
246  //unpack current iteration and current time from ode solver
247  const unsigned int current_iteration = ode_solver->current_iteration;
248  const double current_time = ode_solver->current_time;
249 
250  const double dt = this->get_constant_time_step(dg);
251 
253  const bool is_rrk = (this->all_param.ode_solver_param.ode_solver_type == ODEEnum::rrk_explicit_solver);
254 
255  // All discrete proofs use solution nodes, therefore it is best to report
256  // entropy on the solution nodes rather than by overintegrating.
257  const double current_numerical_entropy = this->compute_integrated_quantities(*dg, IntegratedQuantityEnum::numerical_entropy, 0); //do not overintegrate
258  if (current_iteration==0) this->previous_numerical_entropy = current_numerical_entropy;
259  const double entropy = current_numerical_entropy - previous_numerical_entropy + ode_solver->FR_entropy_contribution_RRK_solver;
260  this->previous_numerical_entropy = current_numerical_entropy;
261 
262  if (std::isnan(entropy)){
263  // Note that this throws an exception rather than using abort()
264  // such that the test khi_robustness can start another test after
265  // an expected crash.
266  this->pcout << "Entropy is nan. Ending flow simulation by throwing an exception..." << std::endl << std::flush;
267  throw current_time;
268  }
269  if (current_iteration == 0) initial_entropy = current_numerical_entropy;
270 
271  double relaxation_parameter = ode_solver->relaxation_parameter_RRK_solver;
272 
273  const double kinetic_energy = this->compute_integrated_quantities(*dg, IntegratedQuantityEnum::kinetic_energy);
274  if (std::isnan(kinetic_energy)){
275  this->pcout << "Kinetic energy is nan. Ending flow simulation by throwing an exception..." << std::endl << std::flush;
276  throw current_time;
277  }
278 
279  // Output solution to console according to output_solution_every_n_iterations
280  int output_solution_every_n_iterations = round(this->all_param.ode_solver_param.output_solution_every_dt_time_intervals/dt);
281  if (this->all_param.ode_solver_param.output_solution_every_x_steps > output_solution_every_n_iterations)
282  output_solution_every_n_iterations = this->all_param.ode_solver_param.output_solution_every_x_steps;
283  if (output_solution_every_n_iterations > 0){
284  //Need to check that output_solution_every_n_iterations is nonzero to avoid
285  //floating point exception
286  if ((current_iteration % output_solution_every_n_iterations) == 0){
287 
288  this->pcout << " Iter: " << current_iteration
289  << " Time: " << std::setprecision(16) << current_time
290  << " Entropy: " << entropy
291  << " (U-Uo)/Uo: " << entropy/initial_entropy
292  << " Kinetic energy: " << kinetic_energy;
293  if (is_rrk)
294  this->pcout << " Relaxation Parameter: " << relaxation_parameter;
295  this->pcout << std::endl;
296  }
297  }
298 
299  // Write to file at every iteration
300  unsteady_data_table->add_value("iteration", current_iteration);
301  unsteady_data_table->set_scientific("iteration", false);
302  this->add_value_to_data_table(current_time,"time",unsteady_data_table);
303  unsteady_data_table->set_scientific("time", false);
304  this->add_value_to_data_table(entropy,"entropy",unsteady_data_table);
305  unsteady_data_table->set_scientific("entropy", false);
306  this->add_value_to_data_table(entropy/initial_entropy,"U/Uo",unsteady_data_table);
307  unsteady_data_table->set_scientific("U/Uo", false);
308  this->add_value_to_data_table(kinetic_energy,"kinetic_energy",unsteady_data_table);
309  unsteady_data_table->set_scientific("kinetic_energy", false);
310  if (is_rrk) {
311  this->add_value_to_data_table(relaxation_parameter,"gamma",unsteady_data_table);
312  unsteady_data_table->set_scientific("gamma", false);
313  }
314  std::ofstream unsteady_data_table_file(this->unsteady_data_table_filename_with_extension);
315  unsteady_data_table->write_text(unsteady_data_table_file);
316 
317  //for next iteration
318  previous_time = current_time;
319 
320 }
321 
322 #if PHILIP_DIM>1
324 #endif
325 
326 } // FlowSolver namespace
327 } // PHiLiP namespace
328 
const Parameters::AllParameters all_param
All parameters.
const MPI_Comm mpi_communicator
MPI communicator.
FlowCaseType
Selects the flow case to be simulated.
dealii::Tensor< 2, dim, std::vector< real > > metric_cofactor_vol
The volume metric cofactor matrix.
Definition: operators.h:1165
void build_1D_volume_operator(const dealii::FESystem< 1, 1 > &finite_element, const dealii::Quadrature< 1 > &quadrature)
Assembles the one dimensional operator.
Definition: operators.cpp:1082
FlowCaseType flow_case_type
Selected FlowCaseType from the input file.
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)
const Parameters::AllParameters *const all_parameters
Pointer to all parameters.
Definition: dg_base.hpp:91
double compute_integrated_quantities(DGBase< dim, double > &dg, IntegratedQuantityEnum quantity, const int overintegrate=10) const
Compute and update integrated quantities.
dealii::FullMatrix< double > oneD_grad_operator
Stores the one dimensional gradient operator.
Definition: operators.h:363
double mach_inf
Mach number at infinity.
int output_solution_every_x_steps
Outputs the solution every x steps to .vtk file.
PeriodicEntropyTests(const Parameters::AllParameters *const parameters_input)
Constructor.
double previous_time
Last time (for calculating relaxation factor)
std::string unsteady_data_table_filename_with_extension
Filename for unsteady data.
dealii::QGauss< 0 > oneD_face_quadrature
1D surface quadrature is always one single point for all poly degrees.
Definition: dg_base.hpp:634
Files for the baseline physics.
Definition: ADTypes.hpp:10
Base class ODE solver.
const double domain_left
Domain left-boundary value for generating the grid.
double previous_numerical_entropy
Store previous entropy.
EulerParam euler_param
Contains parameters for the Euler equations non-dimensionalization.
void build_1D_shape_functions_at_grid_nodes(const dealii::FESystem< 1, 1 > &finite_element, const dealii::Quadrature< 1 > &quadrature)
Constructs the volume operator and gradient operator.
Definition: operators.cpp:2155
double output_solution_every_dt_time_intervals
Outputs the solution every dt time intervals to .vtk file.
Main parameter class that contains the various other sub-parameter classes.
const dealii::hp::FECollection< 1 > oneD_fe_collection_1state
1D Finite Element Collection for p-finite-element to represent the solution for a single state...
Definition: dg_base.hpp:627
dealii::LinearAlgebra::distributed::Vector< double > solution
Current modal coefficients of the solution.
Definition: dg_base.hpp:409
std::vector< real > det_Jac_vol
The determinant of the metric Jacobian at volume cubature nodes.
Definition: operators.h:1171
void add_value_to_data_table(const double value, const std::string value_string, const std::shared_ptr< dealii::TableHandler > data_table) const
Add a value to a given data table with scientific format.
ODESolverParam ode_solver_param
Contains parameters for ODE solver.
IntegratedQuantityEnum
Enum of integrated quantities to calculate.
void build_1D_gradient_operator(const dealii::FESystem< 1, 1 > &finite_element, const dealii::Quadrature< 1 > &quadrature)
Assembles the one dimensional operator.
Definition: operators.cpp:1102
double initial_entropy
Storing entropy at first step.
const unsigned int max_degree
Maximum degree used for p-refi1nement.
Definition: dg_base.hpp:104
Base metric operators class that stores functions used in both the volume and on surface.
Definition: operators.h:1086
unsigned int get_max_fe_degree()
Gets the maximum value of currently active FE degree.
Definition: dg_base.cpp:305
The mapping shape functions evaluated at the desired nodes (facet set included in volume grid nodes f...
Definition: operators.h:1026
double get_constant_time_step(std::shared_ptr< DGBase< dim, double >> dg) const override
Function to compute the constant time step.
Euler equations. Derived from PhysicsBase.
Definition: euler.h:78
std::shared_ptr< HighOrderGrid< dim, real, MeshType > > high_order_grid
High order grid that will provide the MappingFEField.
Definition: dg_base.hpp:655
void gradient_matrix_vector_mult_1D(const std::vector< real > &input_vect, dealii::Tensor< 1, dim, std::vector< real >> &output_vect, const dealii::FullMatrix< double > &basis, const dealii::FullMatrix< double > &gradient_basis)
Computes the gradient of a scalar using sum-factorization where the basis are the same in each direct...
Definition: operators.cpp:414
dealii::FullMatrix< double > oneD_vol_operator
Stores the one dimensional volume operator.
Definition: operators.h:355
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.
unsigned int get_min_fe_degree()
Gets the minimum value of currently active FE degree.
Definition: dg_base.cpp:317
ODESolverEnum ode_solver_type
ODE solver type.
double compute_entropy(const std::shared_ptr< DGBase< dim, double >> dg) const
dealii::DoFHandler< dim > dof_handler
Finite Element Collection to represent the high-order grid.
Definition: dg_base.hpp:652
void build_volume_metric_operators(const unsigned int n_quad_pts, const unsigned int n_metric_dofs, const std::array< std::vector< real >, dim > &mapping_support_points, mapping_shape_functions< dim, n_faces, real > &mapping_basis, const bool use_invariant_curl_form=false)
Builds the volume metric operators.
Definition: operators.cpp:2294
const double domain_right
Domain right-boundary value for generating the grid.
void compute_unsteady_data_and_write_to_table(const std::shared_ptr< ODE::ODESolverBase< dim, double >> ode_solver, const std::shared_ptr< DGBase< dim, double >> dg, const std::shared_ptr< dealii::TableHandler > unsteady_data_table) override
Compute the desired unsteady data and write it to a table.
dealii::hp::QCollection< 1 > oneD_quadrature_collection
1D quadrature to generate Lagrange polynomials for the sake of flux interpolation.
Definition: dg_base.hpp:632
DGBase is independent of the number of state variables.
Definition: dg_base.hpp:82
dealii::ConditionalOStream pcout
ConditionalOStream.
dealii::hp::QCollection< dim > volume_quadrature_collection
Finite Element Collection to represent the high-order grid.
Definition: dg_base.hpp:608
void build_1D_shape_functions_at_flux_nodes(const dealii::FESystem< 1, 1 > &finite_element, const dealii::Quadrature< 1 > &quadrature, const dealii::Quadrature< 0 > &face_quadrature)
Constructs the volume, gradient, surface, and surface gradient operator.
Definition: operators.cpp:2164
void matrix_vector_mult_1D(const std::vector< real > &input_vect, std::vector< real > &output_vect, const dealii::FullMatrix< double > &basis_x, const bool adding=false, const double factor=1.0)
Apply the matrix vector operation using the 1D operator in each direction.
Definition: operators.cpp:308
const dealii::hp::FECollection< dim > fe_collection
Finite Element Collection for p-finite-element to represent the solution.
Definition: dg_base.hpp:597