[P]arallel [Hi]gh-order [Li]brary for [P]DEs  Latest
Parallel High-Order Library for PDEs through hp-adaptive Discontinuous Galerkin methods
strong_dg_les.cpp
1 #include <deal.II/base/tensor.h>
2 #include <deal.II/fe/fe_values.h>
3 #include <deal.II/dofs/dof_handler.h>
4 #include <deal.II/dofs/dof_tools.h>
5 #include <deal.II/dofs/dof_renumbering.h>
6 #include <deal.II/dofs/dof_accessor.h>
7 #include <deal.II/lac/vector.h>
8 #include "ADTypes.hpp"
9 #include <deal.II/fe/fe_dgq.h> // Used for flux interpolation
10 
11 #include "strong_dg_les.hpp"
12 
13 namespace PHiLiP {
14 
15 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
17  const Parameters::AllParameters *const parameters_input,
18  const unsigned int degree,
19  const unsigned int max_degree_input,
20  const unsigned int grid_degree_input,
21  const std::shared_ptr<Triangulation> triangulation_input)
22  : DGStrong<dim,nspecies,nstate,real,MeshType>::DGStrong(parameters_input, degree, max_degree_input, grid_degree_input, triangulation_input)
23 {
24  if constexpr (dim+2==nstate) {
26  }
27 }
28 
29 // Destructor
30 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
32 {
33  pcout << "Destructing DGStrongLES..." << std::endl;
34 }
35 
36 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
38 {
39  // allocate all model variables for each ModelBase object
40  // -- double
41  this->pde_model_double->cellwise_poly_degree.reinit(this->triangulation->n_active_cells(), this->mpi_communicator);
42  this->pde_model_double->cellwise_volume.reinit(this->triangulation->n_active_cells(), this->mpi_communicator);
43 }
44 
45 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
47 {
48  // allocate/reinit the model variables
50 
51  /* NOTE: While not essential, the function update_cellwise_volume_and_poly_degree()
52  could only be called once if no hp-adaptation
53  */
55 
56  // update the cellwise mean quantities
58 }
59 
60 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
62 {
63  // get FEValues of volume
64  const auto mapping = (*(this->high_order_grid->mapping_fe_field));
65  dealii::hp::MappingCollection<dim> mapping_collection(mapping);
66  const dealii::UpdateFlags update_flags = dealii::update_values | dealii::update_JxW_values;
67  dealii::hp::FEValues<dim,dim> fe_values_collection_volume (mapping_collection,
68  this->fe_collection,
70  update_flags);
71 
72  // loop through all cells
73  for (auto cell : this->dof_handler.active_cell_iterators()) {
74  if (!(cell->is_locally_owned() || cell->is_ghost())) continue;
75 
76  // get FEValues of volume for current cell
77  const int i_fele = cell->active_fe_index();
78  const int i_quad = i_fele;
79  const int i_mapp = 0;
80  fe_values_collection_volume.reinit(cell, i_quad, i_mapp, i_fele);
81  const dealii::FEValues<dim,dim> &fe_values_volume = fe_values_collection_volume.get_present_fe_values();
82 
83  // get cell polynomial degree
84  const dealii::FESystem<dim,dim> &fe_high = this->fe_collection[i_fele];
85  const unsigned int cell_poly_degree = fe_high.tensor_degree();
86 
87  // get cell volume
88  const dealii::Quadrature<dim> &quadrature = fe_values_volume.get_quadrature();
89  const unsigned int n_quad_pts = quadrature.size();
90  const std::vector<real> &JxW = fe_values_volume.get_JxW_values();
91  real cell_volume_estimate = 0.0;
92  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
93  cell_volume_estimate = cell_volume_estimate + JxW[iquad];
94  }
95  const real cell_volume = cell_volume_estimate;
96 
97  // get cell index for assignment
98  const dealii::types::global_dof_index cell_index = cell->active_cell_index();
99  // const dealii::types::global_dof_index cell_index = cell->global_active_cell_index(); // https://www.dealii.org/current/doxygen/deal.II/classCellAccessor.html
100 
101  // assign values
102  // -- double
103  this->pde_model_double->cellwise_poly_degree[cell_index] = cell_poly_degree;
104  this->pde_model_double->cellwise_volume[cell_index] = cell_volume;
105  }
106  this->pde_model_double->cellwise_poly_degree.update_ghost_values();
107  this->pde_model_double->cellwise_volume.update_ghost_values();
108 }
109 
110 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
112 {
113  // do nothing
114 }
115 
116 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
118  const Parameters::AllParameters *const parameters_input,
119  const unsigned int degree,
120  const unsigned int max_degree_input,
121  const unsigned int grid_degree_input,
122  const std::shared_ptr<Triangulation> triangulation_input)
123  : DGStrongLES<dim,nspecies,nstate,real,MeshType>::DGStrongLES(parameters_input, degree, max_degree_input, grid_degree_input, triangulation_input)
124 {
125  // do nothing
126 }
127 
128 // Destructor
129 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
131 {
132  pcout << "Destructing DGStrongLES_ShearImproved..." << std::endl;
133 }
134 
135 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
137 {
138  // allocate all model variables for each ModelBase object
139  // -- double
140  this->pde_model_double->cellwise_poly_degree.reinit(this->triangulation->n_active_cells(), this->mpi_communicator);
141  this->pde_model_double->cellwise_volume.reinit(this->triangulation->n_active_cells(), this->mpi_communicator);
142  // allocate the cellwise mean strain rate tensor magnitude distributed vector
143  // -- double
144  this->pde_model_double->cellwise_mean_strain_rate_tensor_magnitude.reinit(this->triangulation->n_active_cells(), this->mpi_communicator);
145 }
146 
147 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
149 {
150  // Overintegrate the error to make sure there is not integration error in the error estimate
151  int overintegrate = 10; // set to zero to reduce computational cost; currently set to 10 for peace of mind
152 
153  // Set the quadrature of size dim and 1D for sum-factorization.
154  dealii::QGauss<dim> quad_extra(this->max_degree+1+overintegrate);
155  dealii::QGauss<1> quad_extra_1D(this->max_degree+1+overintegrate);
156 
157  const unsigned int n_quad_pts = quad_extra.size();
158  const unsigned int grid_degree = this->high_order_grid->fe_system.tensor_degree();
159  const unsigned int poly_degree = this->max_degree;
160  // Construct the basis functions and mapping shape functions.
161  OPERATOR::basis_functions<dim,2*dim> soln_basis(1, poly_degree, grid_degree);
162  OPERATOR::mapping_shape_functions<dim,2*dim> mapping_basis(1, poly_degree, grid_degree);
163  // Build basis function volume operator and gradient operator from 1D finite element for 1 state.
164  soln_basis.build_1D_volume_operator(this->oneD_fe_collection_1state[poly_degree], quad_extra_1D);
165  soln_basis.build_1D_gradient_operator(this->oneD_fe_collection_1state[poly_degree], quad_extra_1D);
166  // Build mapping shape functions operators using the oneD high_ordeR_grid finite element
167  mapping_basis.build_1D_shape_functions_at_grid_nodes(this->high_order_grid->oneD_fe_system, this->high_order_grid->oneD_grid_nodes);
168  mapping_basis.build_1D_shape_functions_at_flux_nodes(this->high_order_grid->oneD_fe_system, quad_extra_1D, this->oneD_face_quadrature);
169  const std::vector<double> &quad_weights = quad_extra.get_weights();
170  // If in the future we need the physical quadrature node location, turn these flags to true and the constructor will
171  // automatically compute it for you. Currently set to false as to not compute extra unused terms.
172  const bool store_vol_flux_nodes = false;//currently doesn't need the volume physical nodal position
173  const bool store_surf_flux_nodes = false;//currently doesn't need the surface physical nodal position
174 
175  const unsigned int n_dofs = this->fe_collection[poly_degree].n_dofs_per_cell();
176  const unsigned int n_shape_fns = n_dofs / nstate;
177  std::vector<dealii::types::global_dof_index> dofs_indices (n_dofs);
178  auto metric_cell = this->high_order_grid->dof_handler_grid.begin_active();
179  // Changed for loop to update metric_cell.
180  for (auto cell = this->dof_handler.begin_active(); cell!= this->dof_handler.end(); ++cell, ++metric_cell) {
181  if (!(cell->is_locally_owned() || cell->is_ghost())) continue;
182  cell->get_dof_indices (dofs_indices);
183 
184  // Initialize the strain rate tensor integral (for computing the mean) to zero
185  dealii::Tensor<2,dim,double> cell_strain_rate_tensor_integral;
186  for (int d1=0; d1<dim; ++d1) {
187  for (int d2=0; d2<dim; ++d2) {
188  cell_strain_rate_tensor_integral[d1][d2] = 0.0;
189  }
190  }
191 
192  // We first need to extract the mapping support points (grid nodes) from high_order_grid.
193  const dealii::FESystem<dim> &fe_metric = this->high_order_grid->fe_system;
194  const unsigned int n_metric_dofs = fe_metric.dofs_per_cell;
195  const unsigned int n_grid_nodes = n_metric_dofs / dim;
196  std::vector<dealii::types::global_dof_index> metric_dof_indices(n_metric_dofs);
197  metric_cell->get_dof_indices (metric_dof_indices);
198  std::array<std::vector<double>,dim> mapping_support_points;
199  for(int idim=0; idim<dim; idim++){
200  mapping_support_points[idim].resize(n_grid_nodes);
201  }
202  // Get the mapping support points (physical grid nodes) from high_order_grid.
203  // Store it in such a way we can use sum-factorization on it with the mapping basis functions.
204  const std::vector<unsigned int > &index_renumbering = dealii::FETools::hierarchic_to_lexicographic_numbering<dim>(grid_degree);
205  for (unsigned int idof = 0; idof< n_metric_dofs; ++idof) {
206  const double val = (this->high_order_grid->volume_nodes[metric_dof_indices[idof]]);
207  const unsigned int istate = fe_metric.system_to_component_index(idof).first;
208  const unsigned int ishape = fe_metric.system_to_component_index(idof).second;
209  const unsigned int igrid_node = index_renumbering[ishape];
210  mapping_support_points[istate][igrid_node] = val;
211  }
212  // Construct the metric operators.
213  OPERATOR::metric_operators<real, dim, 2*dim> metric_oper(nstate, poly_degree, grid_degree, store_vol_flux_nodes, store_surf_flux_nodes);
214  // Build the metric terms to compute the gradient and volume node positions.
215  // This functions will compute the determinant of the metric Jacobian and metric cofactor matrix.
216  // If flags store_vol_flux_nodes and store_surf_flux_nodes set as true it will also compute the physical quadrature positions.
217  metric_oper.build_volume_metric_operators(
218  n_quad_pts, n_grid_nodes,
219  mapping_support_points,
220  mapping_basis,
222 
223  // Fetch the modal soln coefficients
224  // We immediately separate them by state as to be able to use sum-factorization
225  // in the interpolation operator. If we left it by n_dofs_cell, then the matrix-vector
226  // mult would sum the states at the quadrature point.
227  // That is why the basis functions are based off the 1state oneD fe_collection.
228  std::array<std::vector<double>,nstate> soln_coeff;
229  for (unsigned int idof = 0; idof < n_dofs; ++idof) {
230  const unsigned int istate = this->fe_collection[poly_degree].system_to_component_index(idof).first;
231  const unsigned int ishape = this->fe_collection[poly_degree].system_to_component_index(idof).second;
232  if(ishape == 0){
233  soln_coeff[istate].resize(n_shape_fns);
234  }
235 
236  soln_coeff[istate][ishape] = this->solution(dofs_indices[idof]);
237  }
238  // Interpolate each state to the quadrature points using sum-factorization
239  // with the basis functions in each reference direction.
240  std::array<std::vector<double>,nstate> soln_at_q_vect;
241  std::array<dealii::Tensor<1,dim,std::vector<double>>,nstate> soln_grad_at_q_vect;
242  for(int istate=0; istate<nstate; istate++){
243  soln_at_q_vect[istate].resize(n_quad_pts);
244  // Interpolate soln coeff to volume cubature nodes.
245  soln_basis.matrix_vector_mult_1D(soln_coeff[istate], soln_at_q_vect[istate],
246  soln_basis.oneD_vol_operator);
247  // We need to first compute the reference gradient of the solution, then transform that to a physical gradient.
248  dealii::Tensor<1,dim,std::vector<double>> ref_gradient_basis_fns_times_soln;
249  for(int idim=0; idim<dim; idim++){
250  ref_gradient_basis_fns_times_soln[idim].resize(n_quad_pts);
251  soln_grad_at_q_vect[istate][idim].resize(n_quad_pts);
252  }
253  // Apply gradient of reference basis functions on the solution at volume cubature nodes.
254  soln_basis.gradient_matrix_vector_mult_1D(soln_coeff[istate], ref_gradient_basis_fns_times_soln,
255  soln_basis.oneD_vol_operator,
256  soln_basis.oneD_grad_operator);
257  // Transform the reference gradient into a physical gradient operator.
258  for(int idim=0; idim<dim; idim++){
259  for(unsigned int iquad=0; iquad<n_quad_pts; iquad++){
260  for(int jdim=0; jdim<dim; jdim++){
261  //transform into the physical gradient
262  soln_grad_at_q_vect[istate][idim][iquad] += metric_oper.metric_cofactor_vol[idim][jdim][iquad]
263  * ref_gradient_basis_fns_times_soln[jdim][iquad]
264  / metric_oper.det_Jac_vol[iquad];
265  }
266  }
267  }
268  }
269 
270  // -- Solution at legendre poly
271  std::array<std::vector<real>,nstate> legendre_soln_at_q_vect;
272  std::array<dealii::Tensor<1,dim,std::vector<real>>,nstate> legendre_aux_soln_at_q_vect; // legendre auxiliary sol at flux nodes
273  if(this->do_compute_filtered_solution) {
274  // NOTE: This only pertains to advanced SGS models for LES
275  const unsigned int p_min_filtered = this->poly_degree_max_large_scales + 1;
276  //==================================================
277  // GET THE PRIMITIVE SOLUTION
278  //==================================================
279  std::array<std::vector<real>,nstate> primitive_soln_at_q;
280  std::array<dealii::Tensor<1,dim,std::vector<real>>,nstate> primitive_aux_soln_at_q; // primitive auxiliary sol at flux nodes
281  // Resize the primitive soln arrays
282  for(int istate=0; istate<nstate; istate++){
283  primitive_soln_at_q[istate].resize(n_quad_pts);
284  for(int idim=0; idim<dim; idim++){
285  primitive_aux_soln_at_q[istate][idim].resize(n_quad_pts);
286  }
287  }
288  // Compute the primitive soln at all iquad and fill arrays
289  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
290  // extract conservative soln state
291  std::array<real,nstate> soln_state;
292  std::array<dealii::Tensor<1,dim,real>,nstate> aux_soln_state;
293  for(int istate=0; istate<nstate; istate++){
294  soln_state[istate] = soln_at_q_vect[istate][iquad];
295  for(int idim=0; idim<dim; idim++){
296  aux_soln_state[istate][idim] = soln_grad_at_q_vect[istate][idim][iquad];
297  }
298  }
299  // compute primitive soln state from conservative
300  std::array<real,nstate> primitive_soln_state = this->pde_physics_double->convert_conservative_to_primitive(soln_state);
301  std::array<dealii::Tensor<1,dim,real>,nstate> primitive_aux_soln_state = this->pde_physics_double->convert_conservative_gradient_to_primitive_gradient(soln_state,aux_soln_state);
302  // store primitive soln at quadrature point
303  for(int istate=0; istate<nstate; istate++){
304  primitive_soln_at_q[istate][iquad] = primitive_soln_state[istate];
305  for(int idim=0; idim<dim; idim++){
306  primitive_aux_soln_at_q[istate][idim][iquad] = primitive_aux_soln_state[istate][idim];
307  }
308  }
309  }
310 
311  //==================================================
312  // PROJECT TO LEGENDRE BASIS AND MODALLY FILTER
313  //==================================================
314  // -- Primitive solution at legendre poly
315  std::array<std::vector<real>,nstate> primitive_legendre_soln_at_q;
316  std::array<dealii::Tensor<1,dim,std::vector<real>>,nstate> primitive_legendre_aux_soln_at_q; // legendre auxiliary sol at quad points
317 
318  // Details: this projects to Legendre basis, truncates, then interpolates back to quad nodes.
319  // -- Constructor for tensor product polynomials based on Polynomials::Legendre interpolation.
320  dealii::FE_DGQLegendre<1,1> legendre_poly_1D(poly_degree);
321  // -- Projection operator for legendre basis
322  OPERATOR::vol_projection_operator<dim,2*dim> legendre_soln_basis_projection_oper(1, poly_degree, grid_degree);
323  legendre_soln_basis_projection_oper.build_1D_volume_operator(legendre_poly_1D, quad_extra_1D);
324  // -- Legendre basis functions
325  OPERATOR::basis_functions<dim,2*dim> legendre_soln_basis(1, poly_degree, grid_degree);
326  legendre_soln_basis.build_1D_volume_operator(legendre_poly_1D, quad_extra_1D);
327  legendre_soln_basis.build_1D_gradient_operator(legendre_poly_1D, quad_extra_1D);
328  for(int istate=0; istate<nstate; istate++){
329  //==================================================
330  // Solution and Solution Gradient
331  //==================================================
332  // -- (1) Project to Legendre basis
333  std::vector<real> legendre_soln_coeff(n_shape_fns);
334  legendre_soln_basis_projection_oper.matrix_vector_mult_1D(primitive_soln_at_q[istate], legendre_soln_coeff,
335  legendre_soln_basis_projection_oper.oneD_vol_operator);
336  // -- (2) Truncate modes for high-pass filter (i.e. DG-VMS like)
337  if(this->apply_modal_high_pass_filter_on_filtered_solution && (istate!=0 && istate!=(nstate-1))) {
338  for(unsigned int ishape=0; ishape<n_shape_fns; ishape++){
339  if(ishape < p_min_filtered){
340  legendre_soln_coeff[ishape] = 0.0;
341  }
342  }
343  }
344  // -- (3) Interpolate filtered solution back to quadrature points
345  primitive_legendre_soln_at_q[istate].resize(n_quad_pts);
346  legendre_soln_basis.matrix_vector_mult_1D(legendre_soln_coeff, primitive_legendre_soln_at_q[istate],
347  legendre_soln_basis.oneD_vol_operator);
348 
349  // We need to first compute the reference gradient of the solution, then transform that to a physical gradient.
350  dealii::Tensor<1,dim,std::vector<double>> ref_gradient_basis_fns_times_soln;
351  for(int idim=0; idim<dim; idim++){
352  ref_gradient_basis_fns_times_soln[idim].resize(n_quad_pts);
353  primitive_legendre_aux_soln_at_q[istate][idim].resize(n_quad_pts);
354  }
355  // Apply gradient of reference basis functions on the solution at volume cubature nodes.
356  legendre_soln_basis.gradient_matrix_vector_mult_1D(legendre_soln_coeff, ref_gradient_basis_fns_times_soln,
357  legendre_soln_basis.oneD_vol_operator,
358  legendre_soln_basis.oneD_grad_operator);
359  // Transform the reference gradient into a physical gradient operator.
360  for(int idim=0; idim<dim; idim++){
361  for(unsigned int iquad=0; iquad<n_quad_pts; iquad++){
362  for(int jdim=0; jdim<dim; jdim++){
363  //transform into the physical gradient
364  primitive_legendre_aux_soln_at_q[istate][idim][iquad] += metric_oper.metric_cofactor_vol[idim][jdim][iquad]
365  * ref_gradient_basis_fns_times_soln[jdim][iquad]
366  / metric_oper.det_Jac_vol[iquad];
367  }
368  }
369  }
370  //==================================================
371  }
372  //=======================================================
373  // CONVERT PRIMITIVE LEGENDRE SOLUTION TO CONSERVATIVE
374  //=======================================================
375  // Resize the conservative soln arrays
376  for(int istate=0; istate<nstate; istate++){
377  legendre_soln_at_q_vect[istate].resize(n_quad_pts);
378  for(int idim=0; idim<dim; idim++){
379  legendre_aux_soln_at_q_vect[istate][idim].resize(n_quad_pts);
380  }
381  }
382  // Compute the primitive soln at all iquad and fill arrays
383  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
384  // extract conservative soln state
385  std::array<real,nstate> primitive_legendre_soln_state;
386  std::array<dealii::Tensor<1,dim,real>,nstate> primitive_legendre_aux_soln_state;
387  for(int istate=0; istate<nstate; istate++){
388  primitive_legendre_soln_state[istate] = primitive_legendre_soln_at_q[istate][iquad];
389  for(int idim=0; idim<dim; idim++){
390  primitive_legendre_aux_soln_state[istate][idim] = primitive_legendre_aux_soln_at_q[istate][idim][iquad];
391  }
392  }
393  // compute conservative soln state from primitive
394  std::array<real,nstate> legendre_soln_state = this->pde_physics_double->convert_primitive_to_conservative(primitive_legendre_soln_state);
395  std::array<dealii::Tensor<1,dim,real>,nstate> legendre_aux_soln_state = this->pde_physics_double->convert_primitive_gradient_to_conservative_gradient(primitive_legendre_soln_state,primitive_legendre_aux_soln_state);
396  // store conservative soln at quadrature point
397  for(int istate=0; istate<nstate; istate++){
398  legendre_soln_at_q_vect[istate][iquad] = legendre_soln_state[istate];
399  for(int idim=0; idim<dim; idim++){
400  legendre_aux_soln_at_q_vect[istate][idim][iquad] = legendre_aux_soln_state[istate][idim];
401  }
402  }
403  }
404  }
405 
406  // Loop over quadrature nodes, compute quantities to be integrated, and integrate them.
407  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
408 
409  std::array<double,nstate> soln_at_q;
410  std::array<dealii::Tensor<1,dim,double>,nstate> soln_grad_at_q;
411  // Extract solution and gradient in a way that the physics can use them.
412  for(int istate=0; istate<nstate; istate++){
413  if(this->do_compute_filtered_solution) soln_at_q[istate] = legendre_soln_at_q_vect[istate][iquad];
414  else soln_at_q[istate] = soln_at_q_vect[istate][iquad];
415  for(int idim=0; idim<dim; idim++){
416  if(this->do_compute_filtered_solution) soln_grad_at_q[istate][idim] = legendre_aux_soln_at_q_vect[istate][idim][iquad];
417  else soln_grad_at_q[istate][idim] = soln_grad_at_q_vect[istate][idim][iquad];
418  }
419  }
420 
421  // Get strain rate tensor
422  const dealii::Tensor<2,dim,double> strain_rate_tensor = this->pde_model_les_double->navier_stokes_physics->compute_strain_rate_tensor_from_conservative(soln_at_q,soln_grad_at_q);
423  for (int d1=0; d1<dim; ++d1) {
424  for (int d2=0; d2<dim; ++d2) {
425  cell_strain_rate_tensor_integral[d1][d2] += strain_rate_tensor[d1][d2] * quad_weights[iquad] * metric_oper.det_Jac_vol[iquad];
426  }
427  }
428  }
429 
430  // get cell index
431  const dealii::types::global_dof_index cell_index = cell->active_cell_index();
432  // get mean strain rate tensor
433  dealii::Tensor<2,dim,double> cell_mean_strain_rate_tensor;
434  for (int d1=0; d1<dim; ++d1) {
435  for (int d2=0; d2<dim; ++d2) {
436  cell_mean_strain_rate_tensor[d1][d2] = cell_strain_rate_tensor_integral[d1][d2];
437  cell_mean_strain_rate_tensor[d1][d2] /= this->pde_model_double->cellwise_volume[cell_index]; // divide by current cell volume
438  }
439  }
440  // update the cellwise mean strain rate tensor magnitude at the current cell
441  const double cell_mean_strain_rate_tensor_magnitude = this->pde_model_les_double->navier_stokes_physics->get_tensor_magnitude(cell_mean_strain_rate_tensor);
442  this->pde_model_double->cellwise_mean_strain_rate_tensor_magnitude[cell_index] = cell_mean_strain_rate_tensor_magnitude;
443  }
444  // update ghost values
445  this->pde_model_double->cellwise_mean_strain_rate_tensor_magnitude.update_ghost_values();
446 }
447 
448 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
450  const Parameters::AllParameters *const parameters_input,
451  const unsigned int degree,
452  const unsigned int max_degree_input,
453  const unsigned int grid_degree_input,
454  const std::shared_ptr<Triangulation> triangulation_input)
455  : DGStrongLES<dim,nspecies,nstate,real,MeshType>::DGStrongLES(parameters_input, degree, max_degree_input, grid_degree_input, triangulation_input)
456  , dynamic_smagorinsky_model_constant_clipping_limit(this->all_parameters->physics_model_param.dynamic_smagorinsky_model_constant_clipping_limit)
457 {
458  // do nothing
459 }
460 
461 // Destructor
462 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
464 {
465  pcout << "Destructing DGStrongLES_DynamicSmagorinsky..." << std::endl;
466 }
467 
468 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
470 {
471  // allocate all model variables for each ModelBase object
472  // -- double
473  this->pde_model_double->cellwise_poly_degree.reinit(this->triangulation->n_active_cells(), this->mpi_communicator);
474  this->pde_model_double->cellwise_volume.reinit(this->triangulation->n_active_cells(), this->mpi_communicator);
475  // allocate the cellwise dynamic Smagorinsky model constant filter width squared
476  // -- double
477  this->pde_model_double->dynamic_smagorinsky_model_constant_times_filter_width_sqr.reinit(this->triangulation->n_active_cells(), this->mpi_communicator);
478 }
479 
480 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
482 {
483  // Overintegrate the error to make sure there is not integration error in the error estimate
484  int overintegrate = 10; // set to zero to reduce computational cost; currently set to 10 for peace of mind
485 
486  // Set the quadrature of size dim and 1D for sum-factorization.
487  dealii::QGauss<dim> quad_extra(this->max_degree+1+overintegrate);
488  dealii::QGauss<1> quad_extra_1D(this->max_degree+1+overintegrate);
489 
490  const unsigned int n_quad_pts = quad_extra.size();
491  const unsigned int grid_degree = this->high_order_grid->fe_system.tensor_degree();
492  const unsigned int poly_degree = this->max_degree;
493  // Construct the basis functions and mapping shape functions.
494  OPERATOR::basis_functions<dim,2*dim> soln_basis(1, poly_degree, grid_degree);
495  OPERATOR::mapping_shape_functions<dim,2*dim> mapping_basis(1, poly_degree, grid_degree);
496  // Build basis function volume operator and gradient operator from 1D finite element for 1 state.
497  soln_basis.build_1D_volume_operator(this->oneD_fe_collection_1state[poly_degree], quad_extra_1D);
498  soln_basis.build_1D_gradient_operator(this->oneD_fe_collection_1state[poly_degree], quad_extra_1D);
499  // Build mapping shape functions operators using the oneD high_ordeR_grid finite element
500  mapping_basis.build_1D_shape_functions_at_grid_nodes(this->high_order_grid->oneD_fe_system, this->high_order_grid->oneD_grid_nodes);
501  mapping_basis.build_1D_shape_functions_at_flux_nodes(this->high_order_grid->oneD_fe_system, quad_extra_1D, this->oneD_face_quadrature);
502  const std::vector<double> &quad_weights = quad_extra.get_weights();
503  // If in the future we need the physical quadrature node location, turn these flags to true and the constructor will
504  // automatically compute it for you. Currently set to false as to not compute extra unused terms.
505  const bool store_vol_flux_nodes = false;//currently doesn't need the volume physical nodal position
506  const bool store_surf_flux_nodes = false;//currently doesn't need the surface physical nodal position
507 
508  const unsigned int n_dofs = this->fe_collection[poly_degree].n_dofs_per_cell();
509  const unsigned int n_shape_fns = n_dofs / nstate;
510  std::vector<dealii::types::global_dof_index> dofs_indices (n_dofs);
511  auto metric_cell = this->high_order_grid->dof_handler_grid.begin_active();
512  // Changed for loop to update metric_cell.
513  for (auto cell = this->dof_handler.begin_active(); cell!= this->dof_handler.end(); ++cell, ++metric_cell) {
514  if (!(cell->is_locally_owned() || cell->is_ghost())) continue;
515  cell->get_dof_indices (dofs_indices);
516 
517  // Initialize the matrix product integrals (for computing the means) to zero
518  real cell_matrix_L_times_matrix_M_integral = 0.0;
519  real cell_matrix_M_times_matrix_M_integral = 0.0;
520 
521  // We first need to extract the mapping support points (grid nodes) from high_order_grid.
522  const dealii::FESystem<dim> &fe_metric = this->high_order_grid->fe_system;
523  const unsigned int n_metric_dofs = fe_metric.dofs_per_cell;
524  const unsigned int n_grid_nodes = n_metric_dofs / dim;
525  std::vector<dealii::types::global_dof_index> metric_dof_indices(n_metric_dofs);
526  metric_cell->get_dof_indices (metric_dof_indices);
527  std::array<std::vector<double>,dim> mapping_support_points;
528  for(int idim=0; idim<dim; idim++){
529  mapping_support_points[idim].resize(n_grid_nodes);
530  }
531  // Get the mapping support points (physical grid nodes) from high_order_grid.
532  // Store it in such a way we can use sum-factorization on it with the mapping basis functions.
533  const std::vector<unsigned int > &index_renumbering = dealii::FETools::hierarchic_to_lexicographic_numbering<dim>(grid_degree);
534  for (unsigned int idof = 0; idof< n_metric_dofs; ++idof) {
535  const double val = (this->high_order_grid->volume_nodes[metric_dof_indices[idof]]);
536  const unsigned int istate = fe_metric.system_to_component_index(idof).first;
537  const unsigned int ishape = fe_metric.system_to_component_index(idof).second;
538  const unsigned int igrid_node = index_renumbering[ishape];
539  mapping_support_points[istate][igrid_node] = val;
540  }
541  // Construct the metric operators.
542  OPERATOR::metric_operators<real, dim, 2*dim> metric_oper(nstate, poly_degree, grid_degree, store_vol_flux_nodes, store_surf_flux_nodes);
543  // Build the metric terms to compute the gradient and volume node positions.
544  // This functions will compute the determinant of the metric Jacobian and metric cofactor matrix.
545  // If flags store_vol_flux_nodes and store_surf_flux_nodes set as true it will also compute the physical quadrature positions.
546  metric_oper.build_volume_metric_operators(
547  n_quad_pts, n_grid_nodes,
548  mapping_support_points,
549  mapping_basis,
551 
552  // Fetch the modal soln coefficients
553  // We immediately separate them by state as to be able to use sum-factorization
554  // in the interpolation operator. If we left it by n_dofs_cell, then the matrix-vector
555  // mult would sum the states at the quadrature point.
556  // That is why the basis functions are based off the 1state oneD fe_collection.
557  std::array<std::vector<double>,nstate> soln_coeff;
558  for (unsigned int idof = 0; idof < n_dofs; ++idof) {
559  const unsigned int istate = this->fe_collection[poly_degree].system_to_component_index(idof).first;
560  const unsigned int ishape = this->fe_collection[poly_degree].system_to_component_index(idof).second;
561  if(ishape == 0){
562  soln_coeff[istate].resize(n_shape_fns);
563  }
564 
565  soln_coeff[istate][ishape] = this->solution(dofs_indices[idof]);
566  }
567  // Interpolate each state to the quadrature points using sum-factorization
568  // with the basis functions in each reference direction.
569  std::array<std::vector<double>,nstate> soln_at_q_vect;
570  std::array<dealii::Tensor<1,dim,std::vector<double>>,nstate> soln_grad_at_q_vect;
571  for(int istate=0; istate<nstate; istate++){
572  soln_at_q_vect[istate].resize(n_quad_pts);
573  // Interpolate soln coeff to volume cubature nodes.
574  soln_basis.matrix_vector_mult_1D(soln_coeff[istate], soln_at_q_vect[istate],
575  soln_basis.oneD_vol_operator);
576  // We need to first compute the reference gradient of the solution, then transform that to a physical gradient.
577  dealii::Tensor<1,dim,std::vector<double>> ref_gradient_basis_fns_times_soln;
578  for(int idim=0; idim<dim; idim++){
579  ref_gradient_basis_fns_times_soln[idim].resize(n_quad_pts);
580  soln_grad_at_q_vect[istate][idim].resize(n_quad_pts);
581  }
582  // Apply gradient of reference basis functions on the solution at volume cubature nodes.
583  soln_basis.gradient_matrix_vector_mult_1D(soln_coeff[istate], ref_gradient_basis_fns_times_soln,
584  soln_basis.oneD_vol_operator,
585  soln_basis.oneD_grad_operator);
586  // Transform the reference gradient into a physical gradient operator.
587  for(int idim=0; idim<dim; idim++){
588  for(unsigned int iquad=0; iquad<n_quad_pts; iquad++){
589  for(int jdim=0; jdim<dim; jdim++){
590  //transform into the physical gradient
591  soln_grad_at_q_vect[istate][idim][iquad] += metric_oper.metric_cofactor_vol[idim][jdim][iquad]
592  * ref_gradient_basis_fns_times_soln[jdim][iquad]
593  / metric_oper.det_Jac_vol[iquad];
594  }
595  }
596  }
597  }
598 
599  // -- Solution at legendre poly
600  std::array<std::vector<real>,nstate> legendre_soln_at_q_vect;
601  std::array<dealii::Tensor<1,dim,std::vector<real>>,nstate> legendre_aux_soln_at_q_vect; // legendre auxiliary sol at flux nodes
602  dealii::Tensor<2,dim,std::vector<real>> legendre_matrix_L_component_at_q_vect;
603  dealii::Tensor<2,dim,std::vector<real>> legendre_matrix_M_component_at_q_vect;
604  /*if(this->do_compute_filtered_solution) {*/
605  const unsigned int p_min_filtered = this->poly_degree_max_large_scales + 1;
606  //==================================================
607  // GET THE PRIMITIVE SOLUTION AND DSM MATRICES
608  //==================================================
609  std::array<std::vector<real>,nstate> primitive_soln_at_q;
610  std::array<dealii::Tensor<1,dim,std::vector<real>>,nstate> primitive_aux_soln_at_q; // primitive auxiliary sol at flux nodes
611  dealii::Tensor<2,dim,std::vector<real>> matrix_L_component_at_q;
612  dealii::Tensor<2,dim,std::vector<real>> matrix_M_component_at_q;
613 
614  // Resize the primitive soln arrays
615  for(int istate=0; istate<nstate; istate++){
616  primitive_soln_at_q[istate].resize(n_quad_pts);
617  for(int idim=0; idim<dim; idim++){
618  primitive_aux_soln_at_q[istate][idim].resize(n_quad_pts);
619  }
620  }
621  for(int jdim=0; jdim<dim; jdim++){
622  for(int idim=0; idim<dim; idim++){
623  matrix_L_component_at_q[jdim][idim].resize(n_quad_pts);
624  matrix_M_component_at_q[jdim][idim].resize(n_quad_pts);
625  }
626  }
627 
628  // Compute the primitive soln at all iquad and fill arrays
629  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
630  // extract conservative soln state
631  std::array<real,nstate> soln_state;
632  std::array<dealii::Tensor<1,dim,real>,nstate> aux_soln_state;
633  for(int istate=0; istate<nstate; istate++){
634  soln_state[istate] = soln_at_q_vect[istate][iquad];
635  for(int idim=0; idim<dim; idim++){
636  aux_soln_state[istate][idim] = soln_grad_at_q_vect[istate][idim][iquad];
637  }
638  }
639  // compute primitive soln state from conservative
640  const std::array<real,nstate> primitive_soln_state = this->pde_physics_double->convert_conservative_to_primitive(soln_state);
641  const std::array<dealii::Tensor<1,dim,real>,nstate> primitive_aux_soln_state = this->pde_physics_double->convert_conservative_gradient_to_primitive_gradient(soln_state,aux_soln_state);
642  // store primitive soln at quadrature point
643  for(int istate=0; istate<nstate; istate++){
644  primitive_soln_at_q[istate][iquad] = primitive_soln_state[istate];
645  for(int idim=0; idim<dim; idim++){
646  primitive_aux_soln_at_q[istate][idim][iquad] = primitive_aux_soln_state[istate][idim];
647  }
648  }
649  // compute the DMS matrices
650  // -- matrix L
651  const dealii::Tensor<2,dim,real> matrix_L_component_state = this->pde_model_les_double->navier_stokes_physics->compute_germano_idendity_matrix_L_component(soln_state);
652  // store the DMS matrix L
653  for(int jdim=0; jdim<dim; jdim++){
654  for(int idim=0; idim<dim; idim++){
655  matrix_L_component_at_q[jdim][idim][iquad] = matrix_L_component_state[jdim][idim];
656  }
657  }
658  // -- matrix M
659  const dealii::Tensor<2,dim,real> matrix_M_component_state = this->pde_model_les_double->navier_stokes_physics->compute_germano_identity_matrix_M_component(soln_state,aux_soln_state);
660  // store the DMS matrix M
661  for(int jdim=0; jdim<dim; jdim++){
662  for(int idim=0; idim<dim; idim++){
663  matrix_M_component_at_q[jdim][idim][iquad] = matrix_M_component_state[jdim][idim];
664  }
665  }
666  }
667 
668  //==================================================
669  // PROJECT TO LEGENDRE BASIS AND MODALLY FILTER
670  //==================================================
671  // -- Primitive solution at legendre poly
672  std::array<std::vector<real>,nstate> primitive_legendre_soln_at_q;
673  std::array<dealii::Tensor<1,dim,std::vector<real>>,nstate> primitive_legendre_aux_soln_at_q; // legendre auxiliary sol at quad points
674  // Details: this projects to Legendre basis, truncates, then interpolates back to quad nodes.
675  // -- Constructor for tensor product polynomials based on Polynomials::Legendre interpolation.
676  dealii::FE_DGQLegendre<1,1> legendre_poly_1D(poly_degree);
677  // -- Projection operator for legendre basis
678  OPERATOR::vol_projection_operator<dim,2*dim> legendre_soln_basis_projection_oper(1, poly_degree, grid_degree);
679  legendre_soln_basis_projection_oper.build_1D_volume_operator(legendre_poly_1D, quad_extra_1D);
680  // -- Legendre basis functions
681  OPERATOR::basis_functions<dim,2*dim> legendre_soln_basis(1, poly_degree, grid_degree);
682  legendre_soln_basis.build_1D_volume_operator(legendre_poly_1D, quad_extra_1D);
683  legendre_soln_basis.build_1D_gradient_operator(legendre_poly_1D, quad_extra_1D);
684  for(int istate=0; istate<nstate; istate++){
685  //==================================================
686  // Solution and Solution Gradient
687  //==================================================
688  // -- (1) Project to Legendre basis
689  std::vector<real> legendre_soln_coeff(n_shape_fns);
690  legendre_soln_basis_projection_oper.matrix_vector_mult_1D(primitive_soln_at_q[istate], legendre_soln_coeff,
691  legendre_soln_basis_projection_oper.oneD_vol_operator);
692  // -- (2) Truncate modes for high-pass filter (i.e. DG-VMS like)
693  if(/*this->apply_modal_high_pass_filter_on_filtered_solution && */(istate!=0 && istate!=(nstate-1))) {
694  for(unsigned int ishape=0; ishape<n_shape_fns; ishape++){
695  if(ishape < p_min_filtered){
696  legendre_soln_coeff[ishape] = 0.0;
697  }
698  }
699  }
700  // -- (3) Interpolate filtered solution back to quadrature points
701  primitive_legendre_soln_at_q[istate].resize(n_quad_pts);
702  legendre_soln_basis.matrix_vector_mult_1D(legendre_soln_coeff, primitive_legendre_soln_at_q[istate],
703  legendre_soln_basis.oneD_vol_operator);
704 
705  // We need to first compute the reference gradient of the solution, then transform that to a physical gradient.
706  dealii::Tensor<1,dim,std::vector<double>> ref_gradient_basis_fns_times_soln;
707  for(int idim=0; idim<dim; idim++){
708  ref_gradient_basis_fns_times_soln[idim].resize(n_quad_pts);
709  primitive_legendre_aux_soln_at_q[istate][idim].resize(n_quad_pts);
710  }
711  // Apply gradient of reference basis functions on the solution at volume cubature nodes.
712  legendre_soln_basis.gradient_matrix_vector_mult_1D(legendre_soln_coeff, ref_gradient_basis_fns_times_soln,
713  legendre_soln_basis.oneD_vol_operator,
714  legendre_soln_basis.oneD_grad_operator);
715  // Transform the reference gradient into a physical gradient operator.
716  for(int idim=0; idim<dim; idim++){
717  for(unsigned int iquad=0; iquad<n_quad_pts; iquad++){
718  for(int jdim=0; jdim<dim; jdim++){
719  //transform into the physical gradient
720  primitive_legendre_aux_soln_at_q[istate][idim][iquad] += metric_oper.metric_cofactor_vol[idim][jdim][iquad]
721  * ref_gradient_basis_fns_times_soln[jdim][iquad]
722  / metric_oper.det_Jac_vol[iquad];
723  }
724  }
725  }
726  //==================================================
727  }
728 
729 
730  //==================================================
731  // PROJECT TO LEGENDRE BASIS AND MODALLY FILTER
732  //==================================================
733  // Compute the filtered DSM matrix components
734  dealii::Tensor<2,dim,std::vector<real>> legendre_matrix_L_component_at_q;
735  dealii::Tensor<2,dim,std::vector<real>> legendre_matrix_M_component_at_q;
736  for(int jdim=0; jdim<dim; jdim++){
737  dealii::Tensor<1,dim,std::vector<real>> legendre_matrix_L_component_coeff;
738  dealii::Tensor<1,dim,std::vector<real>> legendre_matrix_M_component_coeff;
739  for(int idim=0; idim<dim; idim++){
740  // -- (1) Project to Legendre basis
741  legendre_matrix_L_component_coeff[idim].resize(n_shape_fns);
742  legendre_soln_basis_projection_oper.matrix_vector_mult_1D(matrix_L_component_at_q[jdim][idim], legendre_matrix_L_component_coeff[idim],
743  legendre_soln_basis_projection_oper.oneD_vol_operator);
744  legendre_matrix_M_component_coeff[idim].resize(n_shape_fns);
745  legendre_soln_basis_projection_oper.matrix_vector_mult_1D(matrix_M_component_at_q[jdim][idim], legendre_matrix_M_component_coeff[idim],
746  legendre_soln_basis_projection_oper.oneD_vol_operator);
747  // -- (2) Truncate modes for high-pass filter (i.e. DG-VMS like)
748  /*if(this->apply_modal_high_pass_filter_on_filtered_solution) {*/
749  for(unsigned int ishape=0; ishape<n_shape_fns; ishape++){
750  if(ishape < p_min_filtered){
751  legendre_matrix_L_component_coeff[idim][ishape] = 0.0;
752  legendre_matrix_M_component_coeff[idim][ishape] = 0.0;
753  }
754  }
755  /*}*/
756 
757  // -- (3) Interpolate filtered solution back to quadrature points
758  legendre_matrix_L_component_at_q[jdim][idim].resize(n_quad_pts);
759  legendre_soln_basis.matrix_vector_mult_1D(legendre_matrix_L_component_coeff[idim], legendre_matrix_L_component_at_q[jdim][idim],
760  legendre_soln_basis.oneD_vol_operator);
761  legendre_matrix_M_component_at_q[jdim][idim].resize(n_quad_pts);
762  legendre_soln_basis.matrix_vector_mult_1D(legendre_matrix_M_component_coeff[idim], legendre_matrix_M_component_at_q[jdim][idim],
763  legendre_soln_basis.oneD_vol_operator);
764  }
765  //==================================================
766  //==================================================
767  }
768 
769  //=======================================================
770  // CONVERT PRIMITIVE LEGENDRE SOLUTION TO CONSERVATIVE
771  //=======================================================
772  // Resize the conservative soln arrays
773  for(int istate=0; istate<nstate; istate++){
774  legendre_soln_at_q_vect[istate].resize(n_quad_pts);
775  for(int idim=0; idim<dim; idim++){
776  legendre_aux_soln_at_q_vect[istate][idim].resize(n_quad_pts);
777  }
778  }
779  for(int jdim=0; jdim<dim; jdim++){
780  for(int idim=0; idim<dim; idim++){
781  legendre_matrix_L_component_at_q_vect[jdim][idim].resize(n_quad_pts);
782  legendre_matrix_M_component_at_q_vect[jdim][idim].resize(n_quad_pts);
783  }
784  }
785  // Compute the primitive soln at all iquad and fill arrays
786  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
787  // extract conservative soln state
788  std::array<real,nstate> primitive_legendre_soln_state;
789  std::array<dealii::Tensor<1,dim,real>,nstate> primitive_legendre_aux_soln_state;
790  for(int istate=0; istate<nstate; istate++){
791  primitive_legendre_soln_state[istate] = primitive_legendre_soln_at_q[istate][iquad];
792  for(int idim=0; idim<dim; idim++){
793  primitive_legendre_aux_soln_state[istate][idim] = primitive_legendre_aux_soln_at_q[istate][idim][iquad];
794  }
795  }
796  // compute conservative soln state from primitive
797  std::array<real,nstate> legendre_soln_state = this->pde_physics_double->convert_primitive_to_conservative(primitive_legendre_soln_state);
798  std::array<dealii::Tensor<1,dim,real>,nstate> legendre_aux_soln_state = this->pde_physics_double->convert_primitive_gradient_to_conservative_gradient(primitive_legendre_soln_state,primitive_legendre_aux_soln_state);
799  // store conservative soln at quadrature point
800  for(int istate=0; istate<nstate; istate++){
801  legendre_soln_at_q_vect[istate][iquad] = legendre_soln_state[istate];
802  for(int idim=0; idim<dim; idim++){
803  legendre_aux_soln_at_q_vect[istate][idim][iquad] = legendre_aux_soln_state[istate][idim];
804  }
805  }
806  for(int jdim=0; jdim<dim; jdim++){
807  for(int idim=0; idim<dim; idim++){
808  legendre_matrix_L_component_at_q_vect[jdim][idim][iquad] = legendre_matrix_L_component_at_q[jdim][idim][iquad];
809  legendre_matrix_M_component_at_q_vect[jdim][idim][iquad] = legendre_matrix_M_component_at_q[jdim][idim][iquad];
810  }
811  }
812  }
813  /*}*/
814 
815  // get cell index
816  const dealii::types::global_dof_index cell_index = cell->active_cell_index();
817  const real filter_width = this->pde_model_les_double->get_filter_width(cell_index);
818  const real test_filter_width = this->pde_model_les_double->get_filter_width_from_poly_degree(cell_index,(int)this->poly_degree_max_large_scales);
819 
820  // Loop over quadrature nodes, compute quantities to be integrated, and integrate them.
821  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
822 
823  std::array<double,nstate> soln_at_q;
824  std::array<dealii::Tensor<1,dim,double>,nstate> soln_grad_at_q;
825  std::array<double,nstate> filtered_soln_at_q;
826  std::array<dealii::Tensor<1,dim,double>,nstate> filtered_soln_grad_at_q;
827  // Extract solution and gradient in a way that the physics can use them.
828  for(int istate=0; istate<nstate; istate++){
829  filtered_soln_at_q[istate] = legendre_soln_at_q_vect[istate][iquad];
830  soln_at_q[istate] = soln_at_q_vect[istate][iquad];
831  for(int idim=0; idim<dim; idim++){
832  filtered_soln_grad_at_q[istate][idim] = legendre_aux_soln_at_q_vect[istate][idim][iquad];
833  soln_grad_at_q[istate][idim] = soln_grad_at_q_vect[istate][idim][iquad];
834  }
835  }
836 
837  // Get strain rate tensor
838  const dealii::Tensor<2,dim,real> matrix_L_component_state_from_filtered_soln = this->pde_model_les_double->navier_stokes_physics->compute_germano_idendity_matrix_L_component(filtered_soln_at_q);
839  const dealii::Tensor<2,dim,real> matrix_M_component_state_from_filtered_soln = this->pde_model_les_double->navier_stokes_physics->compute_germano_identity_matrix_M_component(filtered_soln_at_q,filtered_soln_grad_at_q);
840 
841  dealii::Tensor<2,dim,real> filtered_matrix_L_component_state;
842  dealii::Tensor<2,dim,real> filtered_matrix_M_component_state;
843  for(int jdim=0; jdim<dim; jdim++){
844  for(int idim=0; idim<dim; idim++){
845  filtered_matrix_L_component_state[jdim][idim] = legendre_matrix_L_component_at_q_vect[jdim][idim][iquad];
846  filtered_matrix_M_component_state[jdim][idim] = legendre_matrix_M_component_at_q_vect[jdim][idim][iquad];
847  }
848  }
849 
850  dealii::Tensor<2,dim,real> matrix_L; // Leonard stress tensor associated with the test filter
851  dealii::Tensor<2,dim,real> matrix_M;
852  for (int d1=0; d1<dim; ++d1) {
853  for (int d2=0; d2<dim; ++d2) {
854  matrix_L[d1][d2] = filtered_matrix_L_component_state[d1][d2] - matrix_L_component_state_from_filtered_soln[d1][d2];
855  matrix_M[d1][d2] = filter_width*filter_width*filtered_matrix_M_component_state[d1][d2] - test_filter_width*test_filter_width*matrix_M_component_state_from_filtered_soln[d1][d2];
856  }
857  }
858 
859  const real matrix_L_times_matrix_M = this->pde_model_les_double->navier_stokes_physics->get_tensor_product_magnitude_sqr(matrix_L,matrix_M);
860  const real matrix_M_times_matrix_M = this->pde_model_les_double->navier_stokes_physics->get_tensor_product_magnitude_sqr(matrix_M,matrix_M);
861 
862  cell_matrix_L_times_matrix_M_integral += matrix_L_times_matrix_M * quad_weights[iquad] * metric_oper.det_Jac_vol[iquad];
863  cell_matrix_M_times_matrix_M_integral += matrix_M_times_matrix_M * quad_weights[iquad] * metric_oper.det_Jac_vol[iquad];
864  }
865  // get the mean
866  const real cell_volume = this->pde_model_double->cellwise_volume[cell_index];
867  const real cell_averaged_matrix_L_times_matrix_M = cell_matrix_L_times_matrix_M_integral/cell_volume;
868  const real cell_averaged_matrix_M_times_matrix_M = cell_matrix_M_times_matrix_M_integral/cell_volume;
869  // update the DSM constant times filter width (all) squared
870  real dynamic_smagorinsky_model_constant = -0.5*cell_averaged_matrix_L_times_matrix_M/cell_averaged_matrix_M_times_matrix_M;
871  if(dynamic_smagorinsky_model_constant < 0.0) {
872  // clip values less than zero
873  dynamic_smagorinsky_model_constant = 0.0;
874  } else if(dynamic_smagorinsky_model_constant > dynamic_smagorinsky_model_constant_clipping_limit) {
875  // clip values greater than the chosen clipping limit
876  dynamic_smagorinsky_model_constant = dynamic_smagorinsky_model_constant_clipping_limit;
877  }
878  this->pde_model_double->dynamic_smagorinsky_model_constant_times_filter_width_sqr[cell_index] = dynamic_smagorinsky_model_constant*filter_width*filter_width;
879  }
880  // update ghost values
881  this->pde_model_double->dynamic_smagorinsky_model_constant_times_filter_width_sqr.update_ghost_values();
882 }
883 
884 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
886  const Parameters::AllParameters *const parameters_input,
887  const unsigned int degree,
888  const unsigned int max_degree_input,
889  const unsigned int grid_degree_input,
890  const std::shared_ptr<Triangulation> triangulation_input)
891  : DGStrong<dim,nspecies,nstate,real,MeshType>::DGStrong(parameters_input, degree, max_degree_input, grid_degree_input, triangulation_input)
892  , channel_height(parameters_input->flow_solver_param.turbulent_channel_domain_length_y_direction)
893  , half_channel_height(channel_height/2.0)
894  , channel_friction_velocity_reynolds_number(parameters_input->flow_solver_param.turbulent_channel_friction_velocity_reynolds_number)
895  , number_of_cells_x_direction(parameters_input->flow_solver_param.turbulent_channel_number_of_cells_x_direction)
896  , number_of_cells_y_direction(parameters_input->flow_solver_param.turbulent_channel_number_of_cells_y_direction)
897  , number_of_cells_z_direction(parameters_input->flow_solver_param.turbulent_channel_number_of_cells_z_direction)
898  , pi_val(3.141592653589793238)
899  , domain_length_x(parameters_input->flow_solver_param.turbulent_channel_domain_length_x_direction)
900  , domain_length_y(channel_height)
901  , domain_length_z(parameters_input->flow_solver_param.turbulent_channel_domain_length_z_direction)
902  , domain_volume(domain_length_x*domain_length_y*domain_length_z)
903  , channel_bulk_velocity_reynolds_number(pow(0.073, -4.0/7.0)*pow(2.0, 5.0/7.0)*pow(channel_friction_velocity_reynolds_number, 8.0/7.0))
904  , channel_centerline_velocity_reynolds_number(1.28*pow(2.0, -0.0116)*pow(channel_bulk_velocity_reynolds_number,1.0-0.0116))
905  , total_wall_area(2.0*domain_length_x*domain_length_z) // times two because 2 walls
906 {
907  if constexpr (dim+2==nstate) {
909  }
910 }
911 
912 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
914 {
915  pcout << "Destructing DGStrong_ChannelFlow..." << std::endl;
916 }
917 
918 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
920 {
921  // set the constant model variables
922  this->pde_model_double->domain_volume = this->domain_volume;
923  this->pde_model_double->half_channel_height = this->half_channel_height;
924 }
925 
926 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
928 {
930  this->pde_model_double->resultant_wall_shear_force = get_average_wall_shear_stress()*this->total_wall_area;
931 }
932 
933 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
935 {
936  const int NUMBER_OF_INTEGRATED_QUANTITIES = 2;
937  std::array<double,NUMBER_OF_INTEGRATED_QUANTITIES> integrated_quantities;
939  enum IntegratedQuantitiesEnum {
940  bulk_density,
941  bulk_mass_flow_rate
942  };
943  std::array<double,NUMBER_OF_INTEGRATED_QUANTITIES> integral_values;
944  std::fill(integral_values.begin(), integral_values.end(), 0.0);
945 
946  // Overintegrate the error to make sure there is not integration error in the error estimate
947  int overintegrate = 10; // NOTE: could reduce this to reduce computational cost
948  dealii::QGauss<dim> quad_extra(this->max_degree+1+overintegrate);
949  dealii::FEValues<dim,dim> fe_values_extra(*(this->high_order_grid->mapping_fe_field), this->fe_collection[this->max_degree], quad_extra,
950  dealii::update_values /*| dealii::update_gradients*/ | dealii::update_JxW_values | dealii::update_quadrature_points);
951 
952  const unsigned int n_quad_pts = fe_values_extra.n_quadrature_points;
953  std::array<double,nstate> soln_at_q;
954  // std::array<dealii::Tensor<1,dim,double>,nstate> soln_grad_at_q;
955 
956  std::vector<dealii::types::global_dof_index> dofs_indices (fe_values_extra.dofs_per_cell);
957  for (auto cell : this->dof_handler.active_cell_iterators()) {
958  if (!cell->is_locally_owned()) continue;
959  fe_values_extra.reinit (cell);
960  cell->get_dof_indices (dofs_indices);
961 
962  // double cellwise_integrand_value = 0.0;
963  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
964 
965  std::fill(soln_at_q.begin(), soln_at_q.end(), 0.0);
966  // for (int s=0; s<nstate; ++s) {
967  // for (int d=0; d<dim; ++d) {
968  // soln_grad_at_q[s][d] = 0.0;
969  // }
970  // }
971  for (unsigned int idof=0; idof<fe_values_extra.dofs_per_cell; ++idof) {
972  const unsigned int istate = fe_values_extra.get_fe().system_to_component_index(idof).first;
973  soln_at_q[istate] += this->solution[dofs_indices[idof]] * fe_values_extra.shape_value_component(idof, iquad, istate);
974  // soln_grad_at_q[istate] += this->solution[dofs_indices[idof]] * fe_values_extra.shape_grad_component(idof,iquad,istate);
975  }
976  // const dealii::Point<dim> qpoint = (fe_values_extra.quadrature_point(iquad));
977 
978  std::array<double,NUMBER_OF_INTEGRATED_QUANTITIES> integrand_values;
979  std::fill(integrand_values.begin(), integrand_values.end(), 0.0);
980  integrand_values[IntegratedQuantitiesEnum::bulk_density] = soln_at_q[0]; // density
981  integrand_values[IntegratedQuantitiesEnum::bulk_mass_flow_rate] = soln_at_q[1]; // x-momentum
982 
983  // cellwise_integrand_value += integrand_value * fe_values_extra.JxW(iquad);
984 
985  for(int i_quantity=0; i_quantity<NUMBER_OF_INTEGRATED_QUANTITIES; ++i_quantity) {
986  integral_values[i_quantity] += integrand_values[i_quantity] * fe_values_extra.JxW(iquad);
987  }
988  }
989  // // get cell index
990  // const dealii::types::global_dof_index cell_index = cell->active_cell_index();
991  // const double cellwise_average = cellwise_integrand_value/this->pde_model_double->cellwise_volume[cell_index];
992  // integral_value += cellwise_average;
993  }
994  // update integrated quantities
995  for(int i_quantity=0; i_quantity<NUMBER_OF_INTEGRATED_QUANTITIES; ++i_quantity) {
996  integrated_quantities[i_quantity] = dealii::Utilities::MPI::sum(integral_values[i_quantity], this->mpi_communicator);
997  integrated_quantities[i_quantity] /= this->domain_volume; // divide by total domain volume
998  }
999  // set the bulk density, mass flow rate, and velocity for the source term used to force the mass flow rate
1000  this->pde_model_double->bulk_density = integrated_quantities[IntegratedQuantitiesEnum::bulk_density];
1001  this->pde_model_double->bulk_mass_flow_rate = integrated_quantities[IntegratedQuantitiesEnum::bulk_mass_flow_rate];
1002  this->pde_model_double->bulk_velocity = this->pde_model_double->bulk_mass_flow_rate/this->pde_model_double->bulk_density;
1003 }
1004 
1005 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
1007 {
1009  const dealii::UpdateFlags face_update_flags = dealii::update_values | dealii::update_gradients | dealii::update_quadrature_points | dealii::update_JxW_values | dealii::update_normal_vectors;
1010  double integral_value = 0.0;
1011  double integral_area_value = 0.0;
1012 
1013  // Overintegrate the error to make sure there is not integration error in the error estimate
1014  int overintegrate = 10;
1015  dealii::QGauss<dim-1> quad_extra(this->max_degree+1+overintegrate);
1016  dealii::FEFaceValues<dim,dim> fe_face_values_extra(*(this->high_order_grid->mapping_fe_field), this->fe_collection[this->max_degree], quad_extra,
1017  face_update_flags);
1018 
1019  std::array<double,nstate> soln_at_q;
1020  std::array<dealii::Tensor<1,dim,double>,nstate> soln_grad_at_q;
1021 
1022  std::vector<dealii::types::global_dof_index> dofs_indices (fe_face_values_extra.dofs_per_cell);
1023  for (auto cell : this->dof_handler.active_cell_iterators()) {
1024  if (!cell->is_locally_owned()) continue;
1025 
1026  cell->get_dof_indices (dofs_indices);
1027 
1028  for(unsigned int iface = 0; iface < dealii::GeometryInfo<dim>::faces_per_cell; ++iface){
1029  auto face = cell->face(iface);
1030 
1031  if(face->at_boundary()){
1032  const unsigned int boundary_id = face->boundary_id();
1033  if(boundary_id==1001){
1034  fe_face_values_extra.reinit (cell,iface);
1035  const unsigned int n_quad_pts = fe_face_values_extra.n_quadrature_points;
1036  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
1037  std::fill(soln_at_q.begin(), soln_at_q.end(), 0.0);
1038  for (int s=0; s<nstate; ++s) {
1039  for (int d=0; d<dim; ++d) {
1040  soln_grad_at_q[s][d] = 0.0;
1041  }
1042  }
1043  for (unsigned int idof=0; idof<fe_face_values_extra.dofs_per_cell; ++idof) {
1044  const unsigned int istate = fe_face_values_extra.get_fe().system_to_component_index(idof).first;
1045  soln_at_q[istate] += this->solution[dofs_indices[idof]] * fe_face_values_extra.shape_value_component(idof, iquad, istate);
1046  soln_grad_at_q[istate] += this->solution[dofs_indices[idof]] * fe_face_values_extra.shape_grad_component(idof,iquad,istate);
1047  }
1048  // const dealii::Point<dim> qpoint = (fe_face_values_extra.quadrature_point(iquad));
1049  const dealii::Tensor<1,dim,double> normal_vector = -fe_face_values_extra.normal_vector(iquad); // minus for wall normal from face normal
1050  double integrand_value = this->pde_model_navier_stokes_double->navier_stokes_physics->compute_wall_shear_stress(soln_at_q,soln_grad_at_q,normal_vector);
1051  integral_value += integrand_value * fe_face_values_extra.JxW(iquad);
1052  integral_area_value += fe_face_values_extra.JxW(iquad);
1053  }
1054  }
1055  }
1056  }
1057  }
1058  const double mpi_sum_integral_value = dealii::Utilities::MPI::sum(integral_value, this->mpi_communicator);
1059  const double mpi_sum_integral_area_value = dealii::Utilities::MPI::sum(integral_area_value, this->mpi_communicator);
1060  const double averaged_value = mpi_sum_integral_value/mpi_sum_integral_area_value;
1061  return averaged_value;
1062 }
1063 
1064 #if PHILIP_DIM==3
1077 #endif
1078 
1079 } // PHiLiP namespace
dealii::Tensor< 2, dim, std::vector< real > > metric_cofactor_vol
The volume metric cofactor matrix.
Definition: operators.h:1210
~DGStrongLES()
Destructor.
void set_bulk_flow_quantities()
< Parallel std::cout that only outputs on mpi_rank==0
virtual void update_cellwise_mean_quantities()
Update the cellwise mean quantities.
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:1150
DGStrongLES_DynamicSmagorinsky class templated on the number of state variables.
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
dealii::ConditionalOStream pcout
Parallel std::cout that only outputs on mpi_rank==0.
Definition: dg_base.hpp:1259
const double domain_volume
Domain volume.
std::shared_ptr< Physics::NavierStokesWithModelSourceTerms< dim, nspecies, nstate, real > > pde_model_navier_stokes_double
Contains the Navier-Stokes with model source terms object.
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:402
Files for the baseline physics.
Definition: ADTypes.hpp:10
DGStrong_ChannelFlow class templated on the number of state variables.
double get_average_wall_shear_stress() const
computes the average wall shear stress
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:2313
bool use_invariant_curl_form
Flag to use invariant curl form for metric cofactor operator.
std::shared_ptr< Physics::ModelBase< dim, nspecies, nstate, real > > pde_model_double
Contains the model terms of the PDEType == PhysicsModel with real type.
std::shared_ptr< HighOrderGrid< dim, real, MeshType > > high_order_grid
High order grid that will provide the MappingFEField.
Definition: dg_base.hpp:1178
const int nstate
Number of state variables.
Definition: dg_base.hpp:96
dealii::hp::QCollection< dim > volume_quadrature_collection
Finite Element Collection to represent the high-order grid.
Definition: dg_base.hpp:1131
Main parameter class that contains the various other sub-parameter classes.
std::vector< real > det_Jac_vol
The determinant of the metric Jacobian at volume cubature nodes.
Definition: operators.h:1216
dealii::DoFHandler< dim > dof_handler
Finite Element Collection to represent the high-order grid.
Definition: dg_base.hpp:1175
unsigned int n_dofs() const
Number of degrees of freedom.
Definition: dg_base.cpp:3023
const bool do_compute_filtered_solution
Flag to compute the filtered solution.
Definition: strong_dg.hpp:31
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 > &mapping_basis, const bool use_invariant_curl_form=false)
Builds the volume metric operators.
Definition: operators.cpp:2444
DGStrong class templated on the number of state variables.
Definition: strong_dg.hpp:16
dealii::Vector< double > cell_volume
Time it takes for the maximum wavespeed to cross the cell domain.
Definition: dg_base.hpp:454
DGStrongLES class templated on the number of state variables.
const Parameters::AllParameters *const all_parameters
Pointer to all parameters.
Definition: dg_base.hpp:91
Large Eddy Simulation equations. Derived from Navier-Stokes for modifying the stress tensor and heat ...
MPI_Comm mpi_communicator
MPI communicator.
Definition: dg_base.hpp:1258
Base metric operators class that stores functions used in both the volume and on surface.
Definition: operators.h:1131
void allocate_model_variables() override
Allocate the necessary variables declared in src/physics/model.h.
dealii::FullMatrix< double > oneD_vol_operator
Stores the one dimensional volume operator.
Definition: operators.h:380
The mapping shape functions evaluated at the desired nodes (facet set included in volume grid nodes f...
Definition: operators.h:1071
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:1873
void update_model_variables() override
Update the necessary variables declared in src/physics/model.h.
const bool apply_modal_high_pass_filter_on_filtered_solution
Flag to apply modal high pass filter on the filtered solution.
Definition: strong_dg.hpp:32
DGStrongLES_ShearImproved class templated on the number of state variables.
const double dynamic_smagorinsky_model_constant_clipping_limit
Clipping limit for the Dynamic Smagorinsky model constant.
const double half_channel_height
Half channel height.
void allocate_model_variables() override
Allocate the necessary variables declared in src/physics/model.h.
dealii::FullMatrix< double > oneD_grad_operator
Stores the one dimensional gradient operator.
Definition: operators.h:388
void allocate_model_variables() override
Allocate the necessary variables declared in src/physics/model.h.
DGStrongLES_DynamicSmagorinsky(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)
Constructor.
dealii::LinearAlgebra::distributed::Vector< double > solution
Current modal coefficients of the solution.
Definition: dg_base.hpp:409
Navier Stokes equations with model source term.
const double total_wall_area
Total wall area.
bool store_surf_flux_nodes
Flag for storing surface flux nodes.
Definition: dg_base.hpp:1313
void update_model_variables() override
Update the necessary variables declared in src/physics/model.h.
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:2304
const unsigned int max_degree
Maximum degree used for p-refi1nement.
Definition: dg_base.hpp:104
DGStrongLES(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)
Constructor.
DGStrong_ChannelFlow(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)
Constructor.
virtual void allocate_model_variables() override
Allocate the necessary variables declared in src/physics/model.h.
const dealii::UpdateFlags face_update_flags
Update flags needed at face points.
Definition: dg_base.hpp:1215
void update_cellwise_mean_quantities() override
Update the cellwise mean quantities.
const unsigned int poly_degree_max_large_scales
For filtered solution; lower bound of high pass filter.
Definition: strong_dg.hpp:33
DGStrongLES_ShearImproved(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)
Constructor.
std::shared_ptr< Triangulation > triangulation
Mesh.
Definition: dg_base.hpp:160
void update_cellwise_mean_quantities() override
Update the cellwise mean quantities.
void update_cellwise_volume_and_poly_degree()
Update the cellwise volume and polynomial degree.
const dealii::hp::FECollection< dim > fe_collection
Finite Element Collection for p-finite-element to represent the solution.
Definition: dg_base.hpp:1120
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:528
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:1237
std::shared_ptr< Physics::PhysicsBase< dim, nspecies, nstate, real > > pde_physics_double
Contains the physics of the PDE with real type.
Projection operator corresponding to basis functions onto M-norm (L2).
Definition: operators.h:723
std::shared_ptr< Physics::LargeEddySimulationBase< dim, nspecies, nstate, real > > pde_model_les_double
Contains the large eddy simulation object.
bool store_vol_flux_nodes
Flag for storing volume flux nodes.
Definition: dg_base.hpp:1309