[P]arallel [Hi]gh-order [Li]brary for [P]DEs  Latest
Parallel High-Order Library for PDEs through hp-adaptive Discontinuous Galerkin methods
positivity_preserving_limiter.cpp
1 #include "positivity_preserving_limiter.h"
2 #include "tvb_limiter.h"
3 #include "physics/physics_factory.h"
4 #include <eigen/unsupported/Eigen/Polynomials>
5 #include <eigen/Eigen/Dense>
6 
7 namespace PHiLiP {
8 /**********************************
9 *
10 * Positivity Preserving Limiter Class
11 *
12 **********************************/
13 // Constructor
14 template <int dim, int nspecies, int nstate, typename real>
16  const Parameters::AllParameters* const parameters_input)
17  : BoundPreservingLimiterState<dim,nspecies,nstate,real>::BoundPreservingLimiterState(parameters_input)
18  , flow_solver_param(parameters_input->flow_solver_param)
19  , dx((flow_solver_param.grid_right_bound-flow_solver_param.grid_left_bound)/flow_solver_param.number_of_grid_elements_x)
20  , dy((flow_solver_param.grid_top_bound-flow_solver_param.grid_bottom_bound)/flow_solver_param.number_of_grid_elements_y)
21  , dz((flow_solver_param.grid_z_upper_bound-flow_solver_param.grid_z_lower_bound)/flow_solver_param.number_of_grid_elements_z)
22 {
24  PDE_enum pde_type = parameters_input->pde_type;
25 
26  if (nstate == dim + nspecies + 1) {
27  using limiter_enum = Parameters::LimiterParam::LimiterType;
28  limiter_enum limiter_type = parameters_input->limiter_param.bound_preserving_limiter;
29 
30  if(pde_type == PDE_enum::real_gas && limiter_type == limiter_enum::positivity_preservingZhang2010) {
31  std::cout << "Error: Zhang 2010 limiting has not been implemented for multispecies flow" << std::endl;
32  std::abort();
33  } else if (pde_type == PDE_enum::euler || pde_type == PDE_enum::real_gas || pde_type == PDE_enum::navier_stokes) {
34  //create the Physics object
35  this->pde_physics = std::dynamic_pointer_cast<Physics::PhysicsBase<dim,nspecies,nstate,double>>(
37  }
38  } else {
39  std::cout << "Error: Positivity-Preserving Limiter can only be applied for pde_type==euler or pde_type==real_gas" << std::endl;
40  std::abort();
41  }
42 
43  // Create pointer to TVB Limiter class if use_tvb_limiter==true && dim == 1
44  if (parameters_input->limiter_param.use_tvb_limiter) {
45  if (dim == 1) {
46  tvbLimiter = std::make_shared < TVBLimiter<dim, nspecies, nstate, real> >(parameters_input);
47  }
48  else {
49  std::cout << "Error: Cannot create TVB limiter for dim > 1" << std::endl;
50  std::abort();
51  }
52  }
53 
55  std::cout << "Error: number_of_grid_elements must be passed for all directions to use PPL Limiter." << std::endl;
56  std::abort();
57  }
58 
59  if(dim == 3 && flow_solver_param.number_of_grid_elements_z == 1) {
60  std::cout << "Error: number_of_grid_elements must be passed for all directions to use PPL Limiter." << std::endl;
61  std::abort();
62  }
63 }
64 
65 template <int dim, int nspecies, int nstate, typename real>
67  const std::vector< real >& p_lim,
68  const std::array<real, nstate>& soln_cell_avg,
69  const std::array<std::vector<real>, nstate>& soln_at_q,
70  const unsigned int n_quad_pts,
71  const double eps,
72  const double gamma)
73 {
74  std::vector<real> theta2(n_quad_pts, 1); // Value used to linearly scale state variables
75  Eigen::PolynomialSolver<double, 2> solver; // Solver to find smallest root
76 
77  for (unsigned int iquad = 0; iquad < n_quad_pts; ++iquad) {
78  if (p_lim[iquad] >= eps) {
79  theta2[iquad] = 1;
80  }
81  else {
82  real s_coeff1 = (soln_at_q[nstate - 1][iquad] - soln_cell_avg[nstate - 1]) * (soln_at_q[0][iquad] - soln_cell_avg[0]) - 0.5 * pow(soln_at_q[1][iquad] - soln_cell_avg[1], 2);
83 
84  real s_coeff2 = soln_cell_avg[0] * (soln_at_q[nstate - 1][iquad] - soln_cell_avg[nstate - 1]) + soln_cell_avg[nstate - 1] * (soln_at_q[0][iquad] - soln_cell_avg[0])
85  - soln_cell_avg[1] * (soln_at_q[1][iquad] - soln_cell_avg[1]) - (eps / gamma) * (soln_at_q[0][iquad] - soln_cell_avg[0]);
86 
87  real s_coeff3 = (soln_cell_avg[nstate - 1] * soln_cell_avg[0]) - 0.5 * pow(soln_cell_avg[1], 2) - (eps / gamma) * soln_cell_avg[0];
88 
89  if (dim > 1) {
90  s_coeff1 -= 0.5 * pow(soln_at_q[2][iquad] - soln_cell_avg[2], 2);
91  s_coeff2 -= soln_cell_avg[2] * (soln_at_q[2][iquad] - soln_cell_avg[2]);
92  s_coeff3 -= 0.5 * pow(soln_cell_avg[2], 2);
93  }
94 
95  if (dim > 2) {
96  s_coeff1 -= 0.5 * pow(soln_at_q[3][iquad] - soln_cell_avg[3], 2);
97  s_coeff2 -= soln_cell_avg[3] * (soln_at_q[3][iquad] - soln_cell_avg[3]);
98  s_coeff3 -= 0.5 * pow(soln_cell_avg[3], 2);
99  }
100 
101  Eigen::Vector3d coeff(s_coeff3, s_coeff2, s_coeff1);
102  solver.compute(coeff);
103  const Eigen::PolynomialSolver<double, 2>::RootType &r = solver.smallestRoot();
104  theta2[iquad] = r.real();
105  }
106  }
107 
108  return theta2;
109 }
110 
111 template <int dim, int nspecies, int nstate, typename real>
113  const std::array<std::vector<real>, nstate>& soln_at_q,
114  const unsigned int n_quad_pts,
115  const double p_avg)
116 {
117  std::vector<real> t2(n_quad_pts, 1);
118  real theta2 = 1.0; // Value used to linearly scale state variables
119  std::array<real, nstate> soln_at_iquad;
120 
121  // Obtain theta2 value
122  for (unsigned int iquad = 0; iquad < n_quad_pts; ++iquad) {
123  for (unsigned int istate = 0; istate < nstate; ++istate) {
124  soln_at_iquad[istate] = soln_at_q[istate][iquad];
125  }
126  real p_lim = 0;
127 
128  if (nstate == dim + nspecies + 1)
129  p_lim = pde_physics->compute_pressure(soln_at_iquad);
130 
131  if (p_lim >= 0)
132  t2[iquad] = 1;
133  else
134  t2[iquad] = p_avg / (p_avg - p_lim);
135 
136  if (t2[iquad] != 1) {
137  if (t2[iquad] >= 0 && t2[iquad] <= 1 && t2[iquad] < theta2) {
138  theta2 = t2[iquad];
139  }
140  }
141  }
142 
143  return theta2;
144 }
145 
146 template <int dim, int nspecies, int nstate, typename real>
148  const double density_avg,
149  const double density_min,
150  const double lower_bound,
151  const double p_avg)
152 {
153  // Get epsilon (lower bound for rho) for theta limiter
154  real eps = std::min({ lower_bound, density_avg, p_avg });
155  if (eps < 0) eps = lower_bound;
156 
157  real theta = 1.0; // Value used to linearly scale density
158  if (density_avg - density_min > 1e-13)
159  theta = (density_avg - density_min == 0) ? 1.0 : std::min((density_avg - eps) / (density_avg - density_min), 1.0);
160 
161  if (theta < 0 || theta > 1)
162  theta = 0;
163 
164  return theta;
165 }
166 
167 template <int dim, int nspecies, int nstate, typename real>
169  const double species_avg,
170  const double species_quad,
171  const double mixture_avg,
172  const double mixture_quad)
173 {
174  real theta = 1.0; // Value used to linearly scale density
175  real denominator = (species_avg*mixture_quad)-(species_quad*mixture_avg);
176 
177  if (denominator > 1e-13)
178  theta = (-1.0*species_quad*mixture_avg) / denominator;
179 
180  return theta;
181 }
182 
183 template <int dim, int nspecies, int nstate, typename real>
185  dealii::LinearAlgebra::distributed::Vector<double>& solution,
186  const std::array<std::vector<real>, nstate>& soln_coeff,
187  const unsigned int n_shape_fns,
188  const std::vector<dealii::types::global_dof_index>& current_dofs_indices)
189 {
190  // Write limited solution dofs to the global solution vector.
191  for (int istate = 0; istate < nstate; istate++) {
192  for (unsigned int ishape = 0; ishape < n_shape_fns; ++ishape) {
193  const unsigned int idof = istate * n_shape_fns + ishape;
194  solution[current_dofs_indices[idof]] = soln_coeff[istate][ishape]; //
195 
196  // Verify that positivity of density is preserved after application of theta2 limiter
197  if (istate == 0 && solution[current_dofs_indices[idof]] < 0) {
198  std::cout << "Error: Density is a negative value - Aborting... " << std::endl << solution[current_dofs_indices[idof]] << std::endl << std::flush;
199  std::abort();
200  }
201 
202  // Verify that positivity of Total Energy is preserved after application of theta2 limiter
203  if (istate == (dim + 1) && solution[current_dofs_indices[idof]] < 0) {
204  std::cout << "Error: Total Energy is a negative value - Aborting... " << std::endl << solution[current_dofs_indices[idof]] << std::endl << std::flush;
205  std::abort();
206  }
207 
208  // Verify that the solution values haven't been changed to NaN as a result of all quad pts in a cell having negative density
209  // (all quad pts having negative density would result in the local maximum convective eigenvalue being zero leading to division by zero)
210  if (isnan(solution[current_dofs_indices[idof]])) {
211  std::cout << "Error: Solution is NaN - Aborting... " << std::endl << solution[current_dofs_indices[idof]] << std::endl << std::flush;
212  std::abort();
213  }
214  }
215  }
216 }
217 
218 template <int dim, int nspecies, int nstate, typename real>
220  const std::array<std::array<std::vector<real>, nstate>, dim>& soln_at_q,
221  const unsigned int n_quad_pts,
222  const std::vector<real>& quad_weights_GLL,
223  const std::vector<real>& quad_weights_GL,
224  double& dt)
225 {
226  std::array<real, nstate> soln_cell_avg;
227 
228  // Obtain solution cell average
229  if (dim == 1) {
230  soln_cell_avg = get_soln_cell_avg(soln_at_q[0], n_quad_pts, quad_weights_GLL);
231  } else if (dim > 1) {
232  std::array<std::array<real, nstate>,dim> soln_cell_avg_dim;
233 
234  for(unsigned int idim = 0; idim < dim; ++idim) {
235  for(unsigned int istate = 0; istate < nstate; ++istate) {
236  soln_cell_avg_dim[idim][istate] = 0;
237  }
238  }
239 
240  if constexpr (dim == 2) {
241  // Calculating average in x-dir - GLL used for x direction to include surface nodes, GL for rest
242  for(unsigned int istate = 0; istate < nstate; ++istate) {
243  unsigned int quad_pt = 0;
244  for(unsigned int iquad=0; iquad<quad_weights_GLL.size(); ++iquad) {
245  for(unsigned int jquad=0; jquad<quad_weights_GL.size(); ++jquad) {
246  soln_cell_avg_dim[0][istate] += quad_weights_GLL[iquad]*quad_weights_GL[jquad]*soln_at_q[0][istate][quad_pt];
247  quad_pt++;
248  }
249  }
250  }
251 
252  // Calculating average in y-dir - GLL used for y direction to include surface nodes, GL for rest
253  for(unsigned int istate = 0; istate < nstate; ++istate) {
254  unsigned int quad_pt = 0;
255  for(unsigned int iquad=0; iquad<quad_weights_GL.size(); ++iquad) {
256  for(unsigned int jquad=0; jquad<quad_weights_GLL.size(); ++jquad) {
257  soln_cell_avg_dim[1][istate] += quad_weights_GL[iquad]*quad_weights_GLL[jquad]*soln_at_q[1][istate][quad_pt];
258  quad_pt++;
259  }
260  }
261  }
262  }
263 
264  if constexpr (dim == 3) {
265  // Calculating average in x-dir - GLL used for x direction to include surface nodes, GL for rest
266  for(unsigned int istate = 0; istate < nstate; ++istate) {
267  unsigned int quad_pt = 0;
268  for(unsigned int iquad=0; iquad<quad_weights_GLL.size(); ++iquad) {
269  for(unsigned int jquad=0; jquad<quad_weights_GL.size(); ++jquad) {
270  for(unsigned int kquad=0; kquad<quad_weights_GL.size(); ++kquad)
271  soln_cell_avg_dim[0][istate] += quad_weights_GLL[iquad]*quad_weights_GL[jquad]*quad_weights_GL[kquad]*soln_at_q[0][istate][quad_pt];
272  quad_pt++;
273  }
274  }
275  }
276 
277  // Calculating average in y-dir - GLL used for y direction to include surface nodes, GL for rest
278  for(unsigned int istate = 0; istate < nstate; ++istate) {
279  unsigned int quad_pt = 0;
280  for(unsigned int iquad=0; iquad<quad_weights_GL.size(); ++iquad) {
281  for(unsigned int jquad=0; jquad<quad_weights_GLL.size(); ++jquad) {
282  for(unsigned int kquad=0; kquad<quad_weights_GL.size(); ++kquad)
283  soln_cell_avg_dim[1][istate] += quad_weights_GL[iquad]*quad_weights_GLL[jquad]*quad_weights_GL[kquad]*soln_at_q[1][istate][quad_pt];
284  quad_pt++;
285  }
286  }
287  }
288 
289  // Calculating average in z-dir - GLL used for z direction to include surface nodes, GL for rest
290  for(unsigned int istate = 0; istate < nstate; ++istate) {
291  unsigned int quad_pt = 0;
292  for(unsigned int iquad=0; iquad<quad_weights_GL.size(); ++iquad) {
293  for(unsigned int jquad=0; jquad<quad_weights_GL.size(); ++jquad) {
294  for(unsigned int kquad=0; kquad<quad_weights_GLL.size(); ++kquad)
295  soln_cell_avg_dim[2][istate] += quad_weights_GL[iquad]*quad_weights_GL[jquad]*quad_weights_GLL[kquad]*soln_at_q[2][istate][quad_pt];
296  quad_pt++;
297  }
298  }
299  }
300  }
301 
302  for (unsigned int istate = 0; istate < nstate; ++istate) {
303  soln_cell_avg[istate] = 0;
304  }
305 
306  // Values required to weight the averages of each set of mixed nodes (refer to Eqn3.8 in Zhang,Shu paper)
307  const real lambda_1 = dt/this->dx; const real lambda_2 = dt/this->dy; real lambda_3 = 0.0;
308  if constexpr(dim == 3)
309  lambda_3 = dt/this->dz;
310 
311  real max_local_wave_speed_1 = 0.0;
312  real max_local_wave_speed_2 = 0.0;
313  real max_local_wave_speed_3 = 0.0;
314  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
315  std::array<real,nstate> local_soln_at_q_1;
316  std::array<real,nstate> local_soln_at_q_2;
317  std::array<real,nstate> local_soln_at_q_3;
318  for(unsigned int istate = 0; istate < nstate; ++istate){
319  local_soln_at_q_1[istate] = soln_at_q[0][istate][iquad];
320  local_soln_at_q_2[istate] = soln_at_q[1][istate][iquad];
321  if(dim == 3)
322  local_soln_at_q_3[istate] = soln_at_q[2][istate][iquad];
323  else
324  local_soln_at_q_3[istate] = 0.0;
325  }
326  // Update the maximum local wave speed (i.e. convective eigenvalue)
327  real local_wave_speed_1 = 0.0;
328  real local_wave_speed_2 = 0.0;
329  real local_wave_speed_3 = 0.0;
330 
331  local_wave_speed_1 = this->pde_physics->max_convective_eigenvalue(local_soln_at_q_1);
332  local_wave_speed_2 = this->pde_physics->max_convective_eigenvalue(local_soln_at_q_2);
333 
334  if(dim == 3)
335  local_wave_speed_3 = this->pde_physics->max_convective_eigenvalue(local_soln_at_q_3);
336 
337  if(local_wave_speed_1 > max_local_wave_speed_1) max_local_wave_speed_1 = local_wave_speed_1;
338  if(local_wave_speed_2 > max_local_wave_speed_2) max_local_wave_speed_2 = local_wave_speed_2;
339  if(dim == 3 && local_wave_speed_3 > max_local_wave_speed_3) max_local_wave_speed_3 = local_wave_speed_3;
340 
341  }
342 
343  real mu = max_local_wave_speed_1*lambda_1 + max_local_wave_speed_2*lambda_2 + max_local_wave_speed_3*lambda_3;
344  real avg_weight_1 = (max_local_wave_speed_1*lambda_1)/mu;
345  real avg_weight_2 = (max_local_wave_speed_2*lambda_2)/mu;
346  real avg_weight_3 = (max_local_wave_speed_3*lambda_3)/mu;
347 
348  for (unsigned int istate = 0; istate < nstate; istate++) {
349  soln_cell_avg[istate] = avg_weight_1*soln_cell_avg_dim[0][istate] + avg_weight_2*soln_cell_avg_dim[1][istate];
350  if(dim == 3)
351  soln_cell_avg[istate] += avg_weight_3*soln_cell_avg_dim[2][istate];
352 
353  if (isnan(soln_cell_avg[istate])) {
354  std::cout << "Error: Solution Cell Avg is NaN - Aborting... " << std::endl << std::flush;
355  std::abort();
356  }
357  }
358  }
359  return soln_cell_avg;
360 }
361 
362 template <int dim, int nspecies, int nstate, typename real>
364  dealii::LinearAlgebra::distributed::Vector<double>& solution,
365  const dealii::DoFHandler<dim>& dof_handler,
366  const dealii::hp::FECollection<dim>& fe_collection,
367  const dealii::hp::QCollection<dim>& volume_quadrature_collection,
368  const unsigned int grid_degree,
369  const unsigned int max_degree,
370  const dealii::hp::FECollection<1> oneD_fe_collection_1state,
371  const dealii::hp::QCollection<1> oneD_quadrature_collection,
372  double dt)
373 {
374 
375  // If use_tvb_limiter is true, apply TVB limiter before applying maximum-principle-satisfying limiter
376  if (this->all_parameters->limiter_param.use_tvb_limiter == true)
377  this->tvbLimiter->limit(solution, dof_handler, fe_collection, volume_quadrature_collection, grid_degree, max_degree, oneD_fe_collection_1state, oneD_quadrature_collection, dt);
378 
379  //create 1D solution polynomial basis functions to interpolate the solution to the quadrature nodes
380  const unsigned int init_grid_degree = grid_degree;
381 
382  // Construct 1D Quad Points
383  dealii::QGauss<1> oneD_quad_GL(max_degree + 1);
384  dealii::QGaussLobatto<1> oneD_quad_GLL(max_degree + 1);
385  // Constructor for the operators
386  OPERATOR::basis_functions<dim, 2 * dim> soln_basis_GLL(1, max_degree, init_grid_degree);
387  soln_basis_GLL.build_1D_volume_operator(oneD_fe_collection_1state[max_degree], oneD_quad_GLL);
388  OPERATOR::basis_functions<dim, 2 * dim> soln_basis_GL(1, max_degree, init_grid_degree);
389  soln_basis_GL.build_1D_volume_operator(oneD_fe_collection_1state[max_degree], oneD_quad_GL);
390 
391  for (auto soln_cell : dof_handler.active_cell_iterators()) {
392  if (!soln_cell->is_locally_owned()) continue;
393 
394  std::vector<dealii::types::global_dof_index> current_dofs_indices;
395  // Current reference element related to this physical cell
396  const int i_fele = soln_cell->active_fe_index();
397  const dealii::FESystem<dim, dim>& current_fe_ref = fe_collection[i_fele];
398  const int poly_degree = current_fe_ref.tensor_degree();
399 
400  const unsigned int n_dofs_curr_cell = current_fe_ref.n_dofs_per_cell();
401 
402  // Obtain the mapping from local dof indices to global dof indices
403  current_dofs_indices.resize(n_dofs_curr_cell);
404  soln_cell->get_dof_indices(current_dofs_indices);
405 
406  // Extract the local solution dofs in the cell from the global solution dofs
407  std::array<std::vector<real>, nstate> soln_coeff;
408 
409  const unsigned int n_shape_fns = n_dofs_curr_cell / nstate;
410  real local_min_density = 1e6;
411 
412  for (unsigned int istate = 0; istate < nstate; ++istate) {
413  soln_coeff[istate].resize(n_shape_fns);
414  }
415 
416  bool nan_check = false;
417  // Allocate solution dofs and set local min
418  for (unsigned int idof = 0; idof < n_dofs_curr_cell; ++idof) {
419  const unsigned int istate = fe_collection[poly_degree].system_to_component_index(idof).first;
420  const unsigned int ishape = fe_collection[poly_degree].system_to_component_index(idof).second;
421  soln_coeff[istate][ishape] = solution[current_dofs_indices[idof]];
422 
423  if (isnan(soln_coeff[istate][ishape])) {
424  nan_check = true;
425  }
426  }
427 
428  const unsigned int n_quad_pts = n_shape_fns;
429 
430  if (nan_check) {
431  for (unsigned int istate = 0; istate < nstate; ++istate) {
432  std::cout << "Error: Density passed to limiter is NaN - Aborting... " << std::endl;
433 
434  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
435  std::cout << soln_coeff[istate][iquad] << " ";
436  }
437 
438  std::cout << std::endl;
439 
440  std::abort();
441  }
442  }
443 
444  std::array<std::array<std::vector<real>, nstate>, dim> soln_at_q;
445  std::array<std::vector<real>, nstate> soln_at_q_dim;
446  // Interpolate solution dofs to quadrature pts.
447  for(unsigned int idim = 0; idim < dim; idim++) {
448  for (int istate = 0; istate < nstate; istate++) {
449  soln_at_q_dim[istate].resize(n_quad_pts);
450 
451  if(idim == 0) {
452  soln_basis_GLL.matrix_vector_mult(soln_coeff[istate], soln_at_q_dim[istate],
453  soln_basis_GLL.oneD_vol_operator, soln_basis_GL.oneD_vol_operator, soln_basis_GL.oneD_vol_operator);
454  }
455 
456  if(idim == 1) {
457  soln_basis_GLL.matrix_vector_mult(soln_coeff[istate], soln_at_q_dim[istate],
458  soln_basis_GL.oneD_vol_operator, soln_basis_GLL.oneD_vol_operator, soln_basis_GL.oneD_vol_operator);
459  }
460 
461  if(idim == 2) {
462  soln_basis_GLL.matrix_vector_mult(soln_coeff[istate], soln_at_q_dim[istate],
463  soln_basis_GL.oneD_vol_operator, soln_basis_GL.oneD_vol_operator, soln_basis_GLL.oneD_vol_operator);
464  }
465  }
466  soln_at_q[idim] = soln_at_q_dim;
467  }
468 
469  for (unsigned int idim = 0; idim < dim; ++idim) {
470  for (unsigned int iquad = 0; iquad < n_quad_pts; ++iquad) {
471  if (soln_coeff[0][iquad] < local_min_density)
472  local_min_density = soln_coeff[0][iquad];
473  if (soln_at_q[idim][0][iquad] < local_min_density)
474  local_min_density = soln_at_q[idim][0][iquad];
475  }
476  }
477 
478  std::vector< real > GLL_weights = oneD_quad_GLL.get_weights();
479  std::vector< real > GL_weights = oneD_quad_GL.get_weights();
480  std::array<real, nstate> soln_cell_avg;
481  // Obtain solution cell average
482  soln_cell_avg = get_soln_cell_avg_PPL(soln_at_q, n_quad_pts, oneD_quad_GLL.get_weights(), oneD_quad_GL.get_weights(), dt);
483 
484  real lower_bound = this->all_parameters->limiter_param.min_density;
485  real p_avg = 1e-13;
486 
487  real nth_species_avg = 0.0;
488  if (nspecies > 1) {
489  real avg_sum = 0.0;
490  for(unsigned int ispecies = 0; ispecies < nspecies - 1; ++ispecies) {
491  int index = dim + 2 + ispecies;
492  avg_sum += soln_cell_avg[index];
493  }
494  nth_species_avg = soln_cell_avg[0] - avg_sum;
495  }
496 
497  if (nstate == dim + nspecies + 1) {
498  // Compute average value of pressure using soln_cell_avg
499  p_avg = pde_physics->compute_pressure(soln_cell_avg);
500  }
501  // Obtain value used to linearly scale density
502  real theta = get_density_scaling_value(soln_cell_avg[0], local_min_density, lower_bound, p_avg);
503 
504  // Apply limiter on density values at quadrature points
505  for (unsigned int ishape = 0; ishape < n_shape_fns; ++ishape) {
506  soln_coeff[0][ishape] = theta*(soln_coeff[0][ishape] - soln_cell_avg[0]) + soln_cell_avg[0];
507  }
508 
509  if(nspecies > 1) {
510  std::array<real, nstate> soln_at_iquad;
511  real theta_species = 1.0;
512  for (unsigned int iquad = 0; iquad < n_quad_pts; ++iquad) {
513  for (unsigned int istate = 0; istate < nstate; ++istate) {
514  soln_at_iquad[istate] = soln_coeff[istate][iquad];
515  }
516  std::array<real,nspecies> species_densities;
517  for(int ispecies = 0; ispecies < nspecies; ++ispecies) {
518  real density_sum = 0.0;
519  if(ispecies != nspecies-1) {
520  species_densities[ispecies] = soln_at_iquad[dim+2+ispecies];
521  density_sum += species_densities[ispecies];
522  }
523  else
524  species_densities[ispecies] = soln_at_iquad[0] - density_sum;
525  }
526 
527  real theta_species_quad = 0.0;
528 
529  for(unsigned int ispecies = 0; ispecies < (nspecies - 1); ++ispecies) {
530  int index = dim + 2 + ispecies;
531  theta_species_quad = 0.0;
532 
533  if (species_densities[ispecies]<0)
534  theta_species_quad = get_density_scaling_value_species(soln_cell_avg[index],species_densities[ispecies],soln_cell_avg[0],soln_coeff[0][iquad]);
535 
536  if (theta_species_quad > theta_species)
537  theta_species = theta_species_quad;
538  }
539 
540  theta_species_quad = 0.0;
541  if (species_densities[nspecies - 1]<0)
542  theta_species_quad = get_density_scaling_value_species(nth_species_avg,species_densities[nspecies - 1],soln_cell_avg[0],soln_coeff[0][iquad]);
543  if (theta_species_quad > theta_species)
544  theta_species = theta_species_quad;
545  }
546 
547  if(theta_species < 1)
548  std::cout << "The species density is limited." << std::endl;
549 
550  for (unsigned int iquad = 0; iquad < n_quad_pts; ++iquad) {
551  for(unsigned int ispecies = 0; ispecies < (nspecies - 1); ++ispecies) {
552  int index = dim + 2 + ispecies;
553  soln_coeff[index][iquad] = soln_coeff[index][iquad] + theta_species*((soln_cell_avg[index]/soln_cell_avg[0])*soln_coeff[0][iquad]-soln_coeff[index][iquad]);
554  }
555  }
556  }
557 
558  // Interpolate new density values to mixed quadrature points
559  if constexpr(dim >= 1) {
560  soln_basis_GLL.matrix_vector_mult(soln_coeff[0], soln_at_q[0][0],
561  soln_basis_GLL.oneD_vol_operator, soln_basis_GL.oneD_vol_operator, soln_basis_GL.oneD_vol_operator);
562  if(nspecies > 1) {
563  for(unsigned int ispecies = 0; ispecies < (nspecies - 1); ++ispecies) {
564  int index = dim + 2 + ispecies;
565  soln_basis_GLL.matrix_vector_mult(soln_coeff[index], soln_at_q[0][index],
566  soln_basis_GLL.oneD_vol_operator, soln_basis_GL.oneD_vol_operator, soln_basis_GL.oneD_vol_operator);
567  }
568  }
569  }
570 
571  if constexpr(dim >= 2) {
572  soln_basis_GLL.matrix_vector_mult(soln_coeff[0], soln_at_q[1][0],
573  soln_basis_GL.oneD_vol_operator, soln_basis_GLL.oneD_vol_operator, soln_basis_GL.oneD_vol_operator);
574  if(nspecies > 1) {
575  for(unsigned int ispecies = 0; ispecies < (nspecies - 1); ++ispecies) {
576  int index = dim + 2 + ispecies;
577  soln_basis_GLL.matrix_vector_mult(soln_coeff[index], soln_at_q[1][index],
578  soln_basis_GLL.oneD_vol_operator, soln_basis_GL.oneD_vol_operator, soln_basis_GL.oneD_vol_operator);
579  }
580  }
581  }
582 
583  if constexpr(dim == 3) {
584  soln_basis_GLL.matrix_vector_mult(soln_coeff[0], soln_at_q[2][0],
585  soln_basis_GL.oneD_vol_operator, soln_basis_GL.oneD_vol_operator, soln_basis_GLL.oneD_vol_operator);
586  if(nspecies > 1) {
587  for(unsigned int ispecies = 0; ispecies < (nspecies - 1); ++ispecies) {
588  int index = dim + 2 + ispecies;
589  soln_basis_GLL.matrix_vector_mult(soln_coeff[index], soln_at_q[2][index],
590  soln_basis_GLL.oneD_vol_operator, soln_basis_GL.oneD_vol_operator, soln_basis_GL.oneD_vol_operator);
591  }
592  }
593  }
594 
595 
596  real theta2 = 1.0;
597  using limiter_enum = Parameters::LimiterParam::LimiterType;
598  limiter_enum limiter_type = this->all_parameters->limiter_param.bound_preserving_limiter;
599 
600  if (limiter_type == limiter_enum::positivity_preservingWang2012 && nstate == dim + nspecies + 1) {
601  std::array<real, dim> theta2_quad;
602  for(unsigned int idim = 0; idim < dim; ++idim) {
603  theta2_quad[idim] = get_theta2_Wang2012(soln_at_q[idim], n_quad_pts, p_avg);
604  }
605 
606  for(unsigned int idim = 0; idim < dim; ++idim) {
607  if(theta2_quad[idim] < theta2)
608  theta2 = theta2_quad[idim];
609  }
610 
611  real theta2_soln = get_theta2_Wang2012(soln_coeff, n_quad_pts, p_avg);
612  if(theta2_soln < theta2)
613  theta2 = theta2_soln;
614 
615  // Limit values at quadrature points
616  for (unsigned int istate = 0; istate < nstate; ++istate) {
617  for (unsigned int iquad = 0; iquad < n_quad_pts; ++iquad) {
618  soln_coeff[istate][iquad] = theta2 * (soln_coeff[istate][iquad] - soln_cell_avg[istate])
619  + soln_cell_avg[istate];
620  }
621  }
622  }
623 
624  if (limiter_type == limiter_enum::positivity_preservingZhang2010 && nstate == dim + 2) {
625 
626  std::array<std::vector< real >, dim> p_lim_quad;
627  std::array<real, nstate> soln_at_iquad;
628 
629  for(unsigned int idim = 0; idim < dim; ++idim) {
630  p_lim_quad[idim].resize(n_quad_pts);
631  // Compute pressure at quadrature points
632  for (unsigned int iquad = 0; iquad < n_quad_pts; ++iquad) {
633  for (unsigned int istate = 0; istate < nstate; ++istate) {
634  soln_at_iquad[istate] = soln_at_q[idim][istate][iquad];
635  }
636  p_lim_quad[idim][iquad] = pde_physics->compute_pressure(soln_at_iquad);
637  }
638  }
639 
640  std::array<std::vector< real >, dim> theta2_quad;
641  // Obtain value used to linearly scale state variables
642  for(unsigned int idim = 0; idim < dim; ++idim) {
643  theta2_quad[idim].resize(n_quad_pts);
644  theta2_quad[idim] = get_theta2_Zhang2010(p_lim_quad[idim], soln_cell_avg, soln_at_q[idim], n_quad_pts, lower_bound, 1.4);
645  }
646 
647  // Compute pressure at solution points
648  std::vector< real > p_lim;
649  p_lim.resize(n_quad_pts);
650  for (unsigned int iquad = 0; iquad < n_quad_pts; ++iquad) {
651  for (unsigned int istate = 0; istate < nstate; ++istate) {
652  soln_at_iquad[istate] = soln_coeff[istate][iquad];
653  }
654  p_lim[iquad] = pde_physics->compute_pressure(soln_at_iquad);
655  }
656  std::vector<real> theta2_soln = get_theta2_Zhang2010(p_lim, soln_cell_avg, soln_coeff, n_quad_pts, lower_bound, 1.4);
657 
658  // Limit values at quadrature points
659  for (unsigned int istate = 0; istate < nstate; ++istate) {
660  for (unsigned int iquad = 0; iquad < n_quad_pts; ++iquad) {
661  real min_theta2_quad = 1e6;
662  for(unsigned int idim = 0; idim < dim; ++idim) {
663  if(theta2_quad[idim][iquad] < min_theta2_quad)
664  min_theta2_quad = theta2_quad[idim][iquad];
665  }
666 
667  theta2 = std::min({ min_theta2_quad, theta2_soln[iquad] });
668  soln_coeff[istate][iquad] = theta2 * (soln_coeff[istate][iquad] - soln_cell_avg[istate])
669  + soln_cell_avg[istate];
670  }
671  }
672  }
673 
674  if (isnan(theta2)) {
675  std::cout << "Error: Theta2 is NaN - Aborting... " << std::endl << theta2 << std::endl << std::flush;
676  std::abort();
677  }
678 
679  // Write limited solution back and verify that positivity of density is satisfied
680  write_limited_solution(solution, soln_coeff, n_shape_fns, current_dofs_indices);
681  }
682 }
683 
685 
686 } // PHiLiP namespace
PartialDifferentialEquation pde_type
Store the PDE type to be solved.
LimiterType
Limiter type to be applied on the solution.
LimiterParam limiter_param
Contains parameters for limiter.
unsigned int number_of_grid_elements_z
Number of subdivisions in z direction for a rectangle grid.
std::vector< real > get_theta2_Zhang2010(const std::vector< real > &p_lim, const std::array< real, nstate > &soln_cell_avg, const std::array< std::vector< real >, nstate > &soln_at_q, const unsigned int n_quad_pts, const double eps, const double gamma)
LimiterType bound_preserving_limiter
Variable to store specified limiter type.
std::shared_ptr< Physics::PhysicsBase< dim, nspecies, nstate, double > > pde_physics
Pointer to Physics object for computing things on the fly.
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:1217
Base Class for bound preserving limiters templated on state.
double min_density
Epsilon value for Positivity-Preserving Limiter.
std::array< real, nstate > get_soln_cell_avg_PPL(const std::array< std::array< std::vector< real >, nstate >, dim > &soln_at_q, const unsigned int n_quad_pts, const std::vector< real > &quad_weights_GLL, const std::vector< real > &quad_weights_GL, double &dt)
std::shared_ptr< BoundPreservingLimiterState< dim, nspecies, nstate, real > > tvbLimiter
Pointer to TVB limiter class (TVB limiter can be applied in conjunction with this limiter) ...
const Parameters::AllParameters *const all_parameters
Pointer to parameters object.
PartialDifferentialEquation
Possible Partial Differential Equations to solve.
void matrix_vector_mult(const std::vector< real > &input_vect, std::vector< real > &output_vect, const dealii::FullMatrix< double > &basis_x, const dealii::FullMatrix< double > &basis_y, const dealii::FullMatrix< double > &basis_z, const bool adding=false, const double factor=1.0)
Computes a matrix-vector product using sum-factorization. Pass the one-dimensional basis...
Definition: operators.cpp:294
Files for the baseline physics.
Definition: ADTypes.hpp:10
real get_theta2_Wang2012(const std::array< std::vector< real >, nstate > &soln_at_q, const unsigned int n_quad_pts, const double p_avg)
const Parameters::FlowSolverParam flow_solver_param
Flow solver parameters.
real get_density_scaling_value_species(const double species_avg, const double species_quad, const double mixture_avg, const double mixture_quad)
Main parameter class that contains the various other sub-parameter classes.
const int nstate
Number of states.
unsigned int number_of_grid_elements_x
Number of subdivisions in x direction for a rectangle grid.
real dx
Value required to compute solution cell average in 2D/3D, calculated using xmax and xmin parameters...
dealii::FullMatrix< double > oneD_vol_operator
Stores the one dimensional volume operator.
Definition: operators.h:380
void limit(dealii::LinearAlgebra::distributed::Vector< double > &solution, const dealii::DoFHandler< dim > &dof_handler, const dealii::hp::FECollection< dim > &fe_collection, const dealii::hp::QCollection< dim > &volume_quadrature_collection, const unsigned int grid_degree, const unsigned int max_degree, const dealii::hp::FECollection< 1 > oneD_fe_collection_1state, const dealii::hp::QCollection< 1 > oneD_quadrature_collection, double dt) override
Class for implementation of two forms of the Positivity-Preserving limiter derived from BoundPreservi...
std::array< real, nstate > get_soln_cell_avg(const std::array< std::vector< real >, nstate > &soln_at_q, const unsigned int n_quad_pts, const std::vector< real > &quad_weights)
Function to obtain the solution cell average.
unsigned int number_of_grid_elements_y
Number of subdivisions in y direction for a rectangle grid.
real dz
Value required to compute solution cell average in 2D/3D, calculated using zmax and zmin parameters...
static std::shared_ptr< PhysicsBase< dim, nspecies, nstate, real > > create_Physics(const Parameters::AllParameters *const parameters_input, std::shared_ptr< ModelBase< dim, nspecies, nstate, real > > model_input=nullptr)
Factory to return the correct physics given input file.
PositivityPreservingLimiter(const Parameters::AllParameters *const parameters_input)
Constructor.
bool use_tvb_limiter
Flag for applying TVB Limiter.
real dy
Value required to compute solution cell average in 2D/3D, calculated using ymax and ymin parameters...
real get_density_scaling_value(const double density_avg, const double density_min, const double pos_eps, const double p_avg)
void write_limited_solution(dealii::LinearAlgebra::distributed::Vector< double > &solution, const std::array< std::vector< real >, nstate > &soln_coeff, const unsigned int n_shape_fns, const std::vector< dealii::types::global_dof_index > &current_dofs_indices)