[P]arallel [Hi]gh-order [Li]brary for [P]DEs  Latest
Parallel High-Order Library for PDEs through hp-adaptive Discontinuous Galerkin methods
root_finding_rrk_ode_solver.cpp
1 #include "root_finding_rrk_ode_solver.h"
2 #include "physics/euler.h"
3 #include "physics/physics_factory.h"
4 
5 namespace PHiLiP {
6 namespace ODE {
7 
8 template <int dim, typename real, typename MeshType>
10  std::shared_ptr<RKTableauBase<dim,real,MeshType>> rk_tableau_input)
11  : RRKODESolverBase<dim,real,MeshType>(rk_tableau_input)
12 {
13 }
14 
15 template <int dim, typename real, typename MeshType>
17  std::shared_ptr<DGBase<dim,real,MeshType>> dg,
18  const std::vector<dealii::LinearAlgebra::distributed::Vector<double>> &rk_stage,
19  const dealii::LinearAlgebra::distributed::Vector<double> &solution_update
20  )
21 {
22  // Console output is based on linearsolverparam
23  const bool do_output = (dg->all_parameters->ode_solver_param.rrk_root_solver_output == Parameters::OutputEnum::verbose);
24 
25  // Note: there is some overlap in computations here and in runge_kutta_ode_solver.
26  // In future optimization, this could be improved.
27  dealii::LinearAlgebra::distributed::Vector<double> step_direction;
28  step_direction.reinit(rk_stage[0]);
29  for (int i = 0; i < this->n_rk_stages; ++i){
30  step_direction.add(this->butcher_tableau->get_b(i), rk_stage[i]);
31  }
32  step_direction *= dt;
33 
34  // Compute entropy change estimate in M norm , [ v^T (M) du/dt ]
35  const double entropy_change_est = compute_entropy_change_estimate(dt, dg, rk_stage);
36  if (do_output) this->pcout <<"Entropy change estimate: " << std::setprecision(16) << entropy_change_est << std::endl;
37 
38  // n and np1 denote timestep indices
39  const dealii::LinearAlgebra::distributed::Vector<double> u_n = solution_update;
40  const double num_entropy_n = compute_numerical_entropy(u_n,dg);
41 
42  // Allow user to manually select bisection or secant solver.
43  // As secant method is nearly always preferable, this is hard-coded.
44  const bool use_secant = true;
45  bool secant_failed = false;
46 
47  // k, kp1, km1 denote iteration indices of secant or bisection solvers
48  double gamma_kp1 = 0;
49  const double conv_tol = dg->all_parameters->ode_solver_param.relaxation_runge_kutta_root_tolerance;
50  int iter_counter = 0;
51  const int iter_limit = 100;
52  if (use_secant){
53 
54  const double initial_guess_0 = this->relaxation_parameter - 1E-5;
55  const double initial_guess_1 = this->relaxation_parameter + 1E-5;
56  double residual = 1.0;
57  double gamma_k = initial_guess_1;
58  double gamma_km1 = initial_guess_0;
59  double r_gamma_k = compute_root_function(gamma_k, u_n, step_direction, num_entropy_n, entropy_change_est,dg);
60  double r_gamma_km1 = compute_root_function(gamma_km1, u_n, step_direction, num_entropy_n,entropy_change_est,dg);
61 
62  while ((residual > conv_tol) && (iter_counter < iter_limit)){
63  if (r_gamma_km1 == r_gamma_k){
64  if (do_output) this->pcout << " Roots are identical. Multiplying gamma_k by 1.001 and recomputing..." << std::endl;
65  gamma_k *= 1.001;
66  r_gamma_km1 = compute_root_function(gamma_km1, u_n, step_direction, num_entropy_n, entropy_change_est,dg);
67  r_gamma_k = compute_root_function(gamma_k, u_n, step_direction, num_entropy_n, entropy_change_est,dg);
68  }
69  if ((gamma_k < 0.5) || (gamma_k > 1.5)) {
70  if (do_output) this->pcout << " Gamma is far from 1. Setting gamma_k = 1 and contining iterations." << std::endl;
71  gamma_k = 1.0;
72  r_gamma_k = compute_root_function(gamma_k, u_n, step_direction, num_entropy_n, entropy_change_est,dg);
73 
74  }
75  // Secant method, as recommended by Rogowski et al. 2022
76  gamma_kp1 = gamma_k - r_gamma_k * (gamma_k - gamma_km1)/(r_gamma_k-r_gamma_km1);
77  residual = abs(gamma_kp1 - gamma_k);
78  iter_counter ++;
79 
80  //update values
81  gamma_km1 = gamma_k;
82  gamma_k = gamma_kp1;
83  r_gamma_km1 = r_gamma_k;
84  r_gamma_k = compute_root_function(gamma_k, u_n, step_direction, num_entropy_n, entropy_change_est,dg);
85 
86  //output
87  if (do_output) {
88  this->pcout << "Iter: " << iter_counter
89  << " gamma_k: " << gamma_k
90  << " residual: " << residual << std::endl;
91  }
92 
93  if (isnan(gamma_k) || isnan(gamma_km1)) {
94  if (do_output) this->pcout << " NaN detected. Restarting iterations from 1.0." << std::endl;
95  gamma_k = 1.0 - 1E-5;
96  r_gamma_k = compute_root_function(gamma_k, u_n, step_direction, num_entropy_n, entropy_change_est,dg);
97  gamma_km1 = 1.0 + 1E-5;
98  r_gamma_km1 = compute_root_function(gamma_km1, u_n, step_direction, num_entropy_n, entropy_change_est,dg);
99  residual = 1.0;
100  }
101  }
102 
103  // If secant method fails to find a root within the specified number of iterations, fall back on bisection method.
104  if (iter_limit == iter_counter) {
105  this->pcout << "Secant method failed to find a root within the iteration limit. Restarting with bisection method." << std::endl;
106  secant_failed = true;
107  }
108  }
109  if (!use_secant || secant_failed) {
110  //Bisection method
111 
112  iter_counter = 0;
113 
114  double l_limit = this->relaxation_parameter - 0.1;
115  double u_limit = this->relaxation_parameter + 0.1;
116  double root_l_limit = compute_root_function(l_limit, u_n, step_direction, num_entropy_n, entropy_change_est,dg);
117  double root_u_limit = compute_root_function(u_limit, u_n, step_direction, num_entropy_n, entropy_change_est,dg);
118 
119  double residual = 1.0;
120 
121  while ((residual > conv_tol) && (iter_counter < iter_limit)){
122  if (root_l_limit * root_u_limit > 0){
123  this->pcout << "Bisection solver: No root in the interval. Increasing interval size..." << std::endl;
124  l_limit -= 0.1;
125  u_limit += 0.1;
126  }
127 
128  gamma_kp1 = 0.5 * (l_limit + u_limit);
129  if (do_output) this->pcout << "Iter: " << iter_counter;
130  if (do_output) this->pcout << " Gamma by bisection is " << gamma_kp1;
131  double root_at_gamma = compute_root_function(gamma_kp1, u_n, step_direction, num_entropy_n, entropy_change_est,dg);
132  if (root_at_gamma < 0) {
133  l_limit = gamma_kp1;
134  root_l_limit = root_at_gamma;
135  } else {
136  u_limit = gamma_kp1;
137  root_u_limit = root_at_gamma;
138  }
139  residual = u_limit-l_limit;
140  if (do_output) this->pcout << " With residual " << residual << std::endl;
141  iter_counter++;
142  }
143  }
144 
145  if (iter_limit == iter_counter) {
146  this->pcout << "Error: Iteration limit reached and root finding was not successful." << std::endl;
147  secant_failed = true;
148  std::abort();
149  return -1;
150  } else { // Root-finding was successful
151 
152  if (do_output) {
153  // Use [ gamma * dt * (v^T (M+K) du/dt - v^T (M) du/dt ) ] as a workaround to calculate [ gamma * (v^T (K) du/dt) ]
154  const double FR_entropy_contribution = gamma_kp1 *(
155  compute_entropy_change_estimate(dt, dg, rk_stage, false)
156  - compute_entropy_change_estimate(dt, dg, rk_stage, true)
157  );
158 
159  // TEMP store in dg so that flow solver case can access it
160  dealii::LinearAlgebra::distributed::Vector<double> temp = u_n;
161  temp.add(gamma_kp1, step_direction);
162  const double num_entropy_npgamma = compute_numerical_entropy(temp,dg);
163  const double FR_entropy_this_tstep = num_entropy_npgamma - num_entropy_n + FR_entropy_contribution;
164  this->FR_entropy_cumulative += FR_entropy_this_tstep;
165 
166  this->pcout << "Convergence reached!" << std::endl;
167  this->pcout << " Entropy at prev timestep (DG) : " << num_entropy_n << std::endl
168  << " Entropy at current timestep (DG) : " << num_entropy_npgamma << std::endl;
169  this->pcout << " Estimate entropy change (M norm): " << entropy_change_est << std::endl
170  << " Actual entropy change (DG): " << num_entropy_npgamma - num_entropy_n << std::endl
171  << " FR contribution: " << FR_entropy_contribution << std::endl
172  << " Corrected entropy change (FR): " << FR_entropy_this_tstep << std::endl
173  << " Cumulative entropy change (FR): " << this->FR_entropy_cumulative << std::endl
174  << std::endl;
175  }
176 
177  return gamma_kp1;
178  }
179 }
180 
181 
182 template <int dim, typename real, typename MeshType>
184  const double gamma,
185  const dealii::LinearAlgebra::distributed::Vector<double> &u_n,
186  const dealii::LinearAlgebra::distributed::Vector<double> &step_direction,
187  const double num_entropy_n,
188  const double entropy_change_est,
189  std::shared_ptr<DGBase<dim,real,MeshType>> dg) const
190 {
191  dealii::LinearAlgebra::distributed::Vector<double> temp = u_n;
192  temp.add(gamma, step_direction);
193  double num_entropy_np1 = compute_numerical_entropy(temp,dg);
194  return num_entropy_np1 - num_entropy_n - gamma * entropy_change_est;
195 }
196 
197 
198 template <int dim, typename real, typename MeshType>
200  const dealii::LinearAlgebra::distributed::Vector<double> &u,
201  std::shared_ptr<DGBase<dim,real,MeshType>> dg) const
202 {
203  real num_entropy = compute_integrated_numerical_entropy(u,dg);
204 
205  return num_entropy;
206 
207 }
208 
209 
210 template <int dim, typename real, typename MeshType>
212  const dealii::LinearAlgebra::distributed::Vector<double> &u,
213  std::shared_ptr<DGBase<dim,real,MeshType>> dg) const
214 {
215  // This function is reproduced from flow_solver_cases/periodic_turbulence
216  // Check that poly_degree is uniform everywhere
217  if (dg->get_max_fe_degree() != dg->get_min_fe_degree()) {
218  // Note: This function may have issues with nonuniform p. Should test in debug mode if developing in the future.
219  this->pcout << "ERROR: compute_integrated_quantities() is untested for nonuniform p. Aborting..." << std::endl;
220  std::abort();
221  }
222 
223  PHiLiP::Parameters::AllParameters parameters_euler = *(dg->all_parameters);
224  if (parameters_euler.pde_type != Parameters::AllParameters::PartialDifferentialEquation::euler
225  &&
226  parameters_euler.pde_type != Parameters::AllParameters::PartialDifferentialEquation::navier_stokes){
227  this->pcout << "ERROR: Only implemented for Euler or Navier-Stokes. Aborting..." << std::endl;
228  std::abort();
229  }
230  std::shared_ptr < Physics::Euler<dim, dim+2, double > > euler_physics = std::dynamic_pointer_cast<Physics::Euler<dim,dim+2,double>>(
232 
233  const int nstate = dim+2;
234  double integrated_quantity = 0.0;
235 
236  const double poly_degree = dg->all_parameters->flow_solver_param.poly_degree;
237 
238  const unsigned int n_dofs_cell = dg->fe_collection[poly_degree].dofs_per_cell;
239  const unsigned int n_quad_pts = dg->volume_quadrature_collection[poly_degree].size();
240  const unsigned int n_shape_fns = n_dofs_cell / nstate;
241 
242  OPERATOR::vol_projection_operator<dim,2*dim,double> vol_projection(1, poly_degree, dg->max_grid_degree);
243  vol_projection.build_1D_volume_operator(dg->oneD_fe_collection_1state[poly_degree],
244  dg->oneD_quadrature_collection[poly_degree]);
245 
246  // Construct the basis functions and mapping shape functions.
247  OPERATOR::basis_functions<dim,2*dim,double> soln_basis(1, poly_degree, dg->max_grid_degree);
248  soln_basis.build_1D_volume_operator(dg->oneD_fe_collection_1state[poly_degree],
249  dg->oneD_quadrature_collection[poly_degree]);
250 
251  OPERATOR::mapping_shape_functions<dim,2*dim,double> mapping_basis(1, poly_degree, dg->max_grid_degree);
252  mapping_basis.build_1D_shape_functions_at_grid_nodes(dg->high_order_grid->oneD_fe_system,
253  dg->high_order_grid->oneD_grid_nodes);
254  mapping_basis.build_1D_shape_functions_at_flux_nodes(dg->high_order_grid->oneD_fe_system,
255  dg->oneD_quadrature_collection[poly_degree],
256  dg->oneD_face_quadrature);
257 
258  std::vector<dealii::types::global_dof_index> dofs_indices (n_dofs_cell);
259 
260  const std::vector<double> &quad_weights = dg->volume_quadrature_collection[poly_degree].get_weights();
261 
262  // If in the future we need the physical quadrature node location, turn these flags to true and the constructor will
263  // automatically compute it for you. Currently set to false as to not compute extra unused terms.
264  const bool store_vol_flux_nodes = false;//currently doesn't need the volume physical nodal position
265  const bool store_surf_flux_nodes = false;//currently doesn't need the surface physical nodal position
266 
267  auto metric_cell = dg->high_order_grid->dof_handler_grid.begin_active();
268 
269  // Changed for loop to update metric_cell.
270  for (auto cell = dg->dof_handler.begin_active(); cell!= dg->dof_handler.end(); ++cell, ++metric_cell) {
271  if (!cell->is_locally_owned()) continue;
272  cell->get_dof_indices (dofs_indices);
273 
274  // We first need to extract the mapping support points (grid nodes) from high_order_grid.
275  const dealii::FESystem<dim> &fe_metric = dg->high_order_grid->fe_system;
276  const unsigned int n_metric_dofs = fe_metric.dofs_per_cell;
277  const unsigned int n_grid_nodes = n_metric_dofs / dim;
278  std::vector<dealii::types::global_dof_index> metric_dof_indices(n_metric_dofs);
279  metric_cell->get_dof_indices (metric_dof_indices);
280  std::array<std::vector<double>,dim> mapping_support_points;
281  for(int idim=0; idim<dim; idim++){
282  mapping_support_points[idim].resize(n_grid_nodes);
283  }
284  // Get the mapping support points (physical grid nodes) from high_order_grid.
285  // Store it in such a way we can use sum-factorization on it with the mapping basis functions.
286  const std::vector<unsigned int > &index_renumbering = dealii::FETools::hierarchic_to_lexicographic_numbering<dim>(dg->max_grid_degree);
287  for (unsigned int idof = 0; idof< n_metric_dofs; ++idof) {
288  const double val = (dg->high_order_grid->volume_nodes[metric_dof_indices[idof]]);
289  const unsigned int istate = fe_metric.system_to_component_index(idof).first;
290  const unsigned int ishape = fe_metric.system_to_component_index(idof).second;
291  const unsigned int igrid_node = index_renumbering[ishape];
292  mapping_support_points[istate][igrid_node] = val;
293  }
294  // Construct the metric operators.
295  OPERATOR::metric_operators<double, dim, 2*dim> metric_oper(nstate, poly_degree, dg->max_grid_degree, store_vol_flux_nodes, store_surf_flux_nodes);
296  // Build the metric terms to compute the gradient and volume node positions.
297  // This functions will compute the determinant of the metric Jacobian and metric cofactor matrix.
298  // If flags store_vol_flux_nodes and store_surf_flux_nodes set as true it will also compute the physical quadrature positions.
299  metric_oper.build_volume_metric_operators(
300  n_quad_pts, n_grid_nodes,
301  mapping_support_points,
302  mapping_basis,
303  dg->all_parameters->use_invariant_curl_form);
304 
305  // Fetch the modal soln coefficients
306  // We immediately separate them by state as to be able to use sum-factorization
307  // in the interpolation operator. If we left it by n_dofs_cell, then the matrix-vector
308  // mult would sum the states at the quadrature point.
309  // That is why the basis functions are based off the 1state oneD fe_collection.
310  std::array<std::vector<double>,nstate> soln_coeff;
311  for (unsigned int idof = 0; idof < n_dofs_cell; ++idof) {
312  const unsigned int istate = dg->fe_collection[poly_degree].system_to_component_index(idof).first;
313  const unsigned int ishape = dg->fe_collection[poly_degree].system_to_component_index(idof).second;
314  if(ishape == 0){
315  soln_coeff[istate].resize(n_shape_fns);
316  }
317 
318  soln_coeff[istate][ishape] = u(dofs_indices[idof]);
319  }
320 
321  // Interpolate each state to the quadrature points using sum-factorization
322  // with the basis functions in each reference direction.
323  std::array<std::vector<double>,nstate> soln_at_q_vect;
324  for(int istate=0; istate<nstate; istate++){
325  soln_at_q_vect[istate].resize(n_quad_pts);
326  // Interpolate soln coeff to volume cubature nodes.
327  soln_basis.matrix_vector_mult_1D(soln_coeff[istate], soln_at_q_vect[istate],
328  soln_basis.oneD_vol_operator);
329  }
330 
331  // Loop over quadrature nodes, compute quantities to be integrated, and integrate them.
332  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
333 
334  std::array<double,nstate> soln_at_q;
335  // Extract solution and gradient in a way that the physics ca n use them.
336  for(int istate=0; istate<nstate; istate++){
337  soln_at_q[istate] = soln_at_q_vect[istate][iquad];
338  }
339 
340  //#####################################################################
341  // Compute integrated quantities here
342  //#####################################################################
343  const double quadrature_entropy = euler_physics->compute_numerical_entropy_function(soln_at_q);
344  integrated_quantity += quadrature_entropy * quad_weights[iquad] * metric_oper.det_Jac_vol[iquad];
345  //#####################################################################
346  }
347  }
348  //MPI
349  integrated_quantity = dealii::Utilities::MPI::sum(integrated_quantity, this->mpi_communicator);
350 
351  return integrated_quantity;
352 }
353 
354 template <int dim, typename real, typename MeshType>
356  std::shared_ptr<DGBase<dim,real,MeshType>> dg,
357  const std::vector<dealii::LinearAlgebra::distributed::Vector<double>> &rk_stage,
358  const bool use_M_norm_for_entropy_change_est) const
359 {
360  double entropy_change_estimate = 0;
361 
362  for (int istage = 0; istage<this->n_rk_stages; ++istage){
363 
364  // Recall rk_stage is IMM * RHS
365  // therefore, RHS = M * rk_stage = M * du/dt
366  dealii::LinearAlgebra::distributed::Vector<double> mass_matrix_times_rk_stage(dg->solution);
367  if(dg->all_parameters->use_inverse_mass_on_the_fly)
368  {
369  if (use_M_norm_for_entropy_change_est)
370  dg->apply_global_mass_matrix(rk_stage[istage], mass_matrix_times_rk_stage,
371  false, // use_auxiliary_eq,
372  true // use_M_norm
373  );
374  else
375  dg->apply_global_mass_matrix(rk_stage[istage],mass_matrix_times_rk_stage);
376 
377  }
378  else
379  dg->global_mass_matrix.vmult( mass_matrix_times_rk_stage, rk_stage[istage]);
380 
381  //transform solution into entropy variables
382  dealii::LinearAlgebra::distributed::Vector<double> entropy_var_hat_global = this->compute_entropy_vars(this->rk_stage_solution[istage],dg);
383 
384  double entropy = entropy_var_hat_global * mass_matrix_times_rk_stage;
385 
386  entropy_change_estimate += this->butcher_tableau->get_b(istage) * entropy;
387  }
388 
389  return dt * entropy_change_estimate;
390 }
391 
394 #if PHILIP_DIM != 1
396 #endif
397 
398 } // ODESolver namespace
399 } // PHiLiP namespace
const int n_rk_stages
Number of RK stages.
PartialDifferentialEquation pde_type
Store the PDE type to be solved.
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
dealii::LinearAlgebra::distributed::Vector< double > compute_entropy_vars(const dealii::LinearAlgebra::distributed::Vector< double > &u, std::shared_ptr< DGBase< dim, real, MeshType >> dg) const
Return the entropy variables from a solution vector u.
real compute_root_function(const double gamma, const dealii::LinearAlgebra::distributed::Vector< double > &u_n, const dealii::LinearAlgebra::distributed::Vector< double > &d, const double eta_n, const double e, std::shared_ptr< DGBase< dim, real, MeshType >> dg) const
Compute the remainder of the root function Ranocha 2020 Eq. 2.4.
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:1732
RootFindingRRKODESolver(std::shared_ptr< RKTableauBase< dim, real, MeshType >> rk_tableau_input)
Default constructor that will set the constants.
real relaxation_parameter
Relaxation Runge-Kutta parameter gamma^n.
Files for the baseline physics.
Definition: ADTypes.hpp:10
real compute_integrated_numerical_entropy(const dealii::LinearAlgebra::distributed::Vector< double > &u, std::shared_ptr< DGBase< dim, real, MeshType >> dg) const
Compute numerical entropy by integrating over quadrature points.
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
Main parameter class that contains the various other sub-parameter classes.
dealii::ConditionalOStream pcout
Parallel std::cout that only outputs on mpi_rank==0.
std::vector< real > det_Jac_vol
The determinant of the metric Jacobian at volume cubature nodes.
Definition: operators.h:1171
Base metric operators class that stores functions used in both the volume and on surface.
Definition: operators.h:1086
real FR_entropy_cumulative
Storing cumulative entropy change for output.
The mapping shape functions evaluated at the desired nodes (facet set included in volume grid nodes f...
Definition: operators.h:1026
Euler equations. Derived from PhysicsBase.
Definition: euler.h:78
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.
Base class for storing the RK method.
real compute_entropy_change_estimate(const real dt, std::shared_ptr< DGBase< dim, real, MeshType >> dg, const std::vector< dealii::LinearAlgebra::distributed::Vector< double >> &rk_stage, const bool use_M_norm_for_entropy_change_est=true) const
Compute the estimated entropy change during a timestep.
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
DGBase is independent of the number of state variables.
Definition: dg_base.hpp:82
std::vector< dealii::LinearAlgebra::distributed::Vector< double > > rk_stage_solution
Storage for the solution at each Runge-Kutta stage.
std::shared_ptr< RKTableauBase< dim, real, MeshType > > butcher_tableau
Store pointer to RK tableau.
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
const MPI_Comm mpi_communicator
MPI communicator.
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
Relaxation Runge-Kutta ODE solver base class.
Projection operator corresponding to basis functions onto M-norm (L2).
Definition: operators.h:694
real compute_relaxation_parameter(const real dt, std::shared_ptr< DGBase< dim, real, MeshType >> dg, const std::vector< dealii::LinearAlgebra::distributed::Vector< double >> &rk_stage, const dealii::LinearAlgebra::distributed::Vector< double > &solution_update) override
real compute_numerical_entropy(const dealii::LinearAlgebra::distributed::Vector< double > &u, std::shared_ptr< DGBase< dim, real, MeshType >> dg) const
Compute numerical entropy.