[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.cpp
1 #include <deal.II/base/tensor.h>
2 
3 #include <deal.II/fe/fe_values.h>
4 
5 #include <deal.II/dofs/dof_handler.h>
6 #include <deal.II/dofs/dof_tools.h>
7 
8 #include <deal.II/dofs/dof_renumbering.h>
9 
10 #include <deal.II/dofs/dof_accessor.h>
11 
12 #include <deal.II/lac/vector.h>
13 
14 #include "ADTypes.hpp"
15 
16 #include <deal.II/fe/fe_dgq.h> // Used for flux interpolation
17 
18 #include "strong_dg.hpp"
19 
21 
23 template <typename real>
24 double getValue(const real &x) {
25  if constexpr (std::is_same<real, double>::value) {
26  return x;
27  } else {
28  return getValue(x.value());
29  }
30 }
31 
32 namespace PHiLiP {
33 
34 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
36  const Parameters::AllParameters *const parameters_input,
37  const unsigned int degree,
38  const unsigned int max_degree_input,
39  const unsigned int grid_degree_input,
40  const std::shared_ptr<Triangulation> triangulation_input)
41  : DGBaseState<dim,nspecies,nstate,real,MeshType>::DGBaseState(parameters_input, degree, max_degree_input, grid_degree_input, triangulation_input)
42  , do_compute_filtered_solution(this->all_parameters->physics_model_param.do_compute_filtered_solution)
43  , apply_modal_high_pass_filter_on_filtered_solution(this->all_parameters->physics_model_param.apply_modal_high_pass_filter_on_filtered_solution)
44  , poly_degree_max_large_scales(this->all_parameters->physics_model_param.poly_degree_max_large_scales)
45  , using_wall_model(this->all_parameters->using_wall_model)
46  , wall_model_input_from_second_element(this->all_parameters->wall_model_input_from_second_element)
47  , use_projected_entropy_variables_for_nsfr_boundary_term(this->all_parameters->use_projected_entropy_variables_for_nsfr_boundary_term)
48 { }
49 
50 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
51 template <typename adtype>
53  const unsigned int poly_degree,
54  const unsigned int grid_degree,
55  const std::vector<adtype> &metric_coeffs,
58  std::array<std::vector<adtype>,dim> &mapping_support_points)
59 {
60  const dealii::FESystem<dim> &fe_metric = this->high_order_grid->fe_system;
61  const unsigned int n_metric_dofs = fe_metric.dofs_per_cell;
62  const unsigned int n_grid_nodes = n_metric_dofs / dim;
63  //Rewrite the high_order_grid->volume_nodes in a way we can use sum-factorization on.
64  //That is, splitting up the vector by the dimension.
65  for(int idim=0; idim<dim; idim++){
66  mapping_support_points[idim].resize(n_grid_nodes);
67  }
68  const std::vector<unsigned int > &index_renumbering = dealii::FETools::hierarchic_to_lexicographic_numbering<dim>(grid_degree);
69  for (unsigned int idof = 0; idof< n_metric_dofs; ++idof) {
70  const adtype val = metric_coeffs[idof];
71  const unsigned int istate = fe_metric.system_to_component_index(idof).first;
72  const unsigned int ishape = fe_metric.system_to_component_index(idof).second;
73  const unsigned int igrid_node = index_renumbering[ishape];
74  mapping_support_points[istate][igrid_node] = val;
75  }
77  this->volume_quadrature_collection[poly_degree].size(), n_grid_nodes,
78  mapping_support_points,
79  mapping_basis,
81 }
82 
83 /***********************************************************
84 *
85 * Build operators and solve for RHS
86 *
87 ***********************************************************/
88 
89 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
90 template <typename adtype>
92  typename dealii::DoFHandler<dim>::active_cell_iterator cell,
93  const dealii::types::global_dof_index current_cell_index,
94  const std::vector<adtype> &soln_coeffs,
95  const dealii::Tensor<1,dim,std::vector<adtype>> &aux_soln_coeffs,
96  const std::vector<adtype> &/*metric_coeffs*/,
97  const std::vector<real> &local_dual,
98  const std::vector<dealii::types::global_dof_index> &/*soln_dofs_indices*/,
99  const std::vector<dealii::types::global_dof_index> &/*metric_dofs_indices*/,
100  const unsigned int poly_degree,
101  const unsigned int grid_degree,
105  OPERATOR::local_basis_stiffness<dim,2*dim> &flux_basis_stiffness,
106  OPERATOR::vol_projection_operator<dim,2*dim> &soln_basis_projection_oper_int,
107  OPERATOR::vol_projection_operator<dim,2*dim> &soln_basis_projection_oper_ext,
110  std::array<std::vector<adtype>,dim> &/*mapping_support_points*/,
111  dealii::hp::FEValues<dim,dim> &/*fe_values_collection_volume*/,
112  dealii::hp::FEValues<dim,dim> &/*fe_values_collection_volume_lagrange*/,
113  const dealii::FESystem<dim,dim> &/*fe_soln*/,
114  std::vector<adtype> &rhs,
115  dealii::Tensor<1,dim,std::vector<adtype>> &local_auxiliary_RHS,
116  const bool compute_auxiliary_right_hand_side,
117  adtype &dual_dot_residual)
118 {
119  // Check if the current cell's poly degree etc is different then previous cell's.
120  // If the current cell's poly degree is different, then we recompute the 1D
121  // polynomial basis functions. Otherwise, we use the previous values in reference space.
122  if(poly_degree != soln_basis.current_degree){
123  soln_basis.current_degree = poly_degree;
124  flux_basis.current_degree = poly_degree;
125  mapping_basis.current_degree = poly_degree;
126  this->reinit_operators_for_cell_residual_loop(poly_degree, poly_degree, grid_degree,
127  soln_basis, soln_basis,
128  flux_basis, flux_basis,
129  flux_basis_stiffness,
130  soln_basis_projection_oper_int, soln_basis_projection_oper_ext,
131  mapping_basis);
132  }
133 
134  //Fetch the modal soln coefficients and the modal auxiliary soln coefficients
135  //We immediately separate them by state as to be able to use sum-factorization
136  //in the interpolation operator. If we left it by n_dofs_cell, then the matrix-vector
137  //mult would sum the states at the quadrature point.
138  const unsigned int n_dofs_cell = this->fe_collection[poly_degree].dofs_per_cell;
139  const unsigned int n_shape_fns = n_dofs_cell / nstate;
140  std::array<std::vector<adtype>,nstate> soln_coeff;
141  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> aux_soln_coeff;
142  for (unsigned int idof = 0; idof < n_dofs_cell; ++idof) {
143  const unsigned int istate = this->fe_collection[poly_degree].system_to_component_index(idof).first;
144  const unsigned int ishape = this->fe_collection[poly_degree].system_to_component_index(idof).second;
145  if(ishape == 0)
146  soln_coeff[istate].resize(n_shape_fns);
147  soln_coeff[istate][ishape] = soln_coeffs[idof];
148  for(int idim=0; idim<dim; idim++){
149  if(ishape == 0)
150  aux_soln_coeff[istate][idim].resize(n_shape_fns);
151  if(this->use_auxiliary_eq){
152  aux_soln_coeff[istate][idim][ishape] = aux_soln_coeffs[idim][idof];
153  }
154  else{
155  aux_soln_coeff[istate][idim][ishape] = 0.0;
156  }
157  }
158  }
159 
160 
161  if(compute_auxiliary_right_hand_side){
162  assemble_volume_term_auxiliary_equation<adtype>(
163  soln_coeff,
164  poly_degree,
165  soln_basis,
166  flux_basis,
167  metric_oper,
168  local_auxiliary_RHS);
169  }
170  else{
171  assemble_volume_term_strong<adtype>(
172  cell,
173  current_cell_index,
174  soln_coeff,
175  aux_soln_coeff,
176  poly_degree,
177  soln_basis,
178  flux_basis,
179  flux_basis_stiffness,
180  soln_basis_projection_oper_int,
181  metric_oper,
182  physics,
183  rhs);
184  for(unsigned int idof=0; idof<n_dofs_cell; idof++){
185  dual_dot_residual += rhs[idof] * local_dual[idof];
186  }
187  }
188 }
189 
190 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
191 template<typename adtype>
193  typename dealii::DoFHandler<dim>::active_cell_iterator cell,
194  const dealii::types::global_dof_index current_cell_index,
195  const std::vector<adtype> &soln_coeffs,
196  const dealii::Tensor<1,dim,std::vector<adtype>> &aux_soln_coeffs,
197  const std::vector<adtype> &/*metric_coeffs*/,
198  const std::vector<real> &local_dual,
199  const unsigned int face_number,
200  const unsigned int boundary_id,
204  const unsigned int poly_degree,
205  const unsigned int /*grid_degree*/,
208  OPERATOR::vol_projection_operator<dim,2*dim> &soln_basis_projection_oper_int,
211  std::array<std::vector<adtype>,dim> &mapping_support_points,
212  dealii::hp::FEFaceValues<dim,dim> &/*fe_values_collection_face_int*/,
213  const dealii::FESystem<dim,dim> &/*fe_soln*/,
214  const real penalty,
215  std::vector<adtype> &rhs,
216  dealii::Tensor<1,dim,std::vector<adtype>> &local_auxiliary_RHS,
217  const bool compute_auxiliary_right_hand_side,
218  adtype &dual_dot_residual)
219 {
220 
221  const dealii::FESystem<dim> &fe_metric = this->high_order_grid->fe_system;
222  const unsigned int n_metric_dofs = fe_metric.dofs_per_cell;
223  const unsigned int n_grid_nodes = n_metric_dofs / dim;
224  //build the surface metric operators for interior
225  metric_oper.build_facet_metric_operators(
226  face_number,
227  this->face_quadrature_collection[poly_degree].size(),
228  n_grid_nodes,
229  mapping_support_points,
230  mapping_basis,
232  //Fetch the modal soln coefficients and the modal auxiliary soln coefficients
233  //We immediately separate them by state as to be able to use sum-factorization
234  //in the interpolation operator. If we left it by n_dofs_cell, then the matrix-vector
235  //mult would sum the states at the quadrature point.
236  const unsigned int n_dofs_cell = this->fe_collection[poly_degree].dofs_per_cell;
237  const unsigned int n_shape_fns = n_dofs_cell / nstate;
238  //Check face quadrature point ordering
239  std::vector<bool> face_orientation = {cell->face_orientation(face_number), cell->face_rotation(face_number), cell->face_flip(face_number)};
240  std::array<std::vector<adtype>,nstate> soln_coeff;
241  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> aux_soln_coeff;
242  for (unsigned int idof = 0; idof < n_dofs_cell; ++idof) {
243  const unsigned int istate = this->fe_collection[poly_degree].system_to_component_index(idof).first;
244  const unsigned int ishape = this->fe_collection[poly_degree].system_to_component_index(idof).second;
245  if(ishape == 0)
246  soln_coeff[istate].resize(n_shape_fns);
247  soln_coeff[istate][ishape] = soln_coeffs[idof];
248  for(int idim=0; idim<dim; idim++){
249  if(ishape == 0)
250  aux_soln_coeff[istate][idim].resize(n_shape_fns);
251  if(this->use_auxiliary_eq){
252  aux_soln_coeff[istate][idim][ishape] = aux_soln_coeffs[idim][idof];
253  }
254  else{
255  aux_soln_coeff[istate][idim][ishape] = 0.0;
256  }
257  }
258  }
259 
260 
261  if(compute_auxiliary_right_hand_side){
262  assemble_boundary_term_auxiliary_equation<adtype> (
263  face_number, current_cell_index,
264  face_orientation,
265  soln_coeff,
266  poly_degree,
267  boundary_id,
268  soln_basis, metric_oper,
269  physics,
270  diss_num_flux,
271  local_auxiliary_RHS);
272  }
273  else{
274  assemble_boundary_term_strong<adtype> (
275  cell,
276  face_number,
277  current_cell_index,
278  face_orientation,
279  soln_coeff, aux_soln_coeff,
280  boundary_id, poly_degree, penalty,
281  soln_basis,
282  flux_basis,
283  soln_basis_projection_oper_int,
284  metric_oper,
285  physics, conv_num_flux, diss_num_flux,
286  rhs);
287  for(unsigned int idof=0; idof<n_dofs_cell; idof++){
288  dual_dot_residual += rhs[idof] * local_dual[idof];
289  }
290  }
291 
292 }
293 
294 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
295 template <typename adtype>
297  typename dealii::DoFHandler<dim>::active_cell_iterator cell,
298  typename dealii::DoFHandler<dim>::active_cell_iterator neighbor_cell,
299  const dealii::types::global_dof_index current_cell_index,
300  const dealii::types::global_dof_index neighbor_cell_index,
301  const unsigned int iface,
302  const unsigned int neighbor_iface,
303  const std::vector<adtype> &soln_coeffs_int,
304  const std::vector<adtype> &soln_coeffs_ext,
305  const dealii::Tensor<1,dim,std::vector<adtype>> &aux_soln_coeffs_int,
306  const dealii::Tensor<1,dim,std::vector<adtype>> &aux_soln_coeffs_ext,
307  const std::vector<adtype> &/*metric_coeff_int*/,
308  const std::vector<adtype> &metric_coeff_ext,
309  const std::vector< double > &dual_int,
310  const std::vector< double > &dual_ext,
311  const unsigned int poly_degree_int,
312  const unsigned int poly_degree_ext,
313  const unsigned int /*grid_degree_int*/,
314  const unsigned int grid_degree_ext,
315  OPERATOR::basis_functions<dim,2*dim> &soln_basis_int,
316  OPERATOR::basis_functions<dim,2*dim> &soln_basis_ext,
317  OPERATOR::basis_functions<dim,2*dim> &flux_basis_int,
318  OPERATOR::basis_functions<dim,2*dim> &flux_basis_ext,
319  OPERATOR::local_basis_stiffness<dim,2*dim> &flux_basis_stiffness,
320  OPERATOR::vol_projection_operator<dim,2*dim> &soln_basis_projection_oper_int,
321  OPERATOR::vol_projection_operator<dim,2*dim> &soln_basis_projection_oper_ext,
325  std::array<std::vector<adtype>,dim> &mapping_support_points,
329  dealii::hp::FEFaceValues<dim,dim> &/*fe_values_collection_face_int*/,
330  dealii::hp::FEFaceValues<dim,dim> &/*fe_values_collection_face_ext*/,
331  dealii::hp::FESubfaceValues<dim,dim> &/*fe_values_collection_subface*/,
332  const dealii::FESystem<dim,dim> &/*fe_int*/,
333  const dealii::FESystem<dim,dim> &/*fe_ext*/,
334  const real penalty,
335  std::vector<adtype> &rhs_int,
336  std::vector<adtype> &rhs_ext,
337  dealii::Tensor<1,dim,std::vector<adtype>> &aux_rhs_int,
338  dealii::Tensor<1,dim,std::vector<adtype>> &aux_rhs_ext,
339  const bool compute_auxiliary_right_hand_side,
340  adtype &dual_dot_residual,
341  const bool /*is_a_subface*/,
342  const unsigned int /*neighbor_i_subface*/)
343 {
344 
345  const dealii::FESystem<dim> &fe_metric = this->high_order_grid->fe_system;
346  const unsigned int n_metric_dofs = fe_metric.dofs_per_cell;
347  const unsigned int n_grid_nodes = n_metric_dofs / dim;
348 
349  //build the surface metric operators for interior
350  metric_oper_int.build_facet_metric_operators(
351  iface,
352  this->face_quadrature_collection[poly_degree_int].size(),
353  n_grid_nodes,
354  mapping_support_points,
355  mapping_basis,
357 
358 
359  if(poly_degree_ext != soln_basis_ext.current_degree){
360  soln_basis_ext.current_degree = poly_degree_ext;
361  flux_basis_ext.current_degree = poly_degree_ext;
362  mapping_basis.current_degree = poly_degree_ext;
363  this->reinit_operators_for_cell_residual_loop(poly_degree_int, poly_degree_ext, grid_degree_ext,
364  soln_basis_int, soln_basis_ext,
365  flux_basis_int, flux_basis_ext,
366  flux_basis_stiffness,
367  soln_basis_projection_oper_int, soln_basis_projection_oper_ext,
368  mapping_basis);
369  }
370 
371  if(!compute_auxiliary_right_hand_side){//only for primary equations
372  //get neighbor metric operator
373  //rewrite the high_order_grid->volume_nodes in a way we can use sum-factorization on.
374  //that is, splitting up the vector by the dimension.
375  std::array<std::vector<adtype>,dim> mapping_support_points_neigh;
376  for(int idim=0; idim<dim; idim++){
377  mapping_support_points_neigh[idim].resize(n_grid_nodes);
378  }
379  const std::vector<unsigned int > &index_renumbering = dealii::FETools::hierarchic_to_lexicographic_numbering<dim>(grid_degree_ext);
380  for (unsigned int idof = 0; idof< n_metric_dofs; ++idof) {
381  const adtype val = metric_coeff_ext[idof];
382  const unsigned int istate = fe_metric.system_to_component_index(idof).first;
383  const unsigned int ishape = fe_metric.system_to_component_index(idof).second;
384  const unsigned int igrid_node = index_renumbering[ishape];
385  mapping_support_points_neigh[istate][igrid_node] = val;
386  }
387  //build the metric operators for strong form
388  metric_oper_ext.build_volume_metric_operators(
389  this->volume_quadrature_collection[poly_degree_ext].size(), n_grid_nodes,
390  mapping_support_points_neigh,
391  mapping_basis,
393  }
394 
395  const unsigned int n_dofs_int = this->fe_collection[poly_degree_int].dofs_per_cell;
396  const unsigned int n_dofs_ext = this->fe_collection[poly_degree_ext].dofs_per_cell;
397  const unsigned int n_shape_fns_int = n_dofs_int / nstate;
398  const unsigned int n_shape_fns_ext = n_dofs_ext / nstate;
399  //Check interior quadrature point ordering
400  std::vector<bool> face_orientation_int = {cell->face_orientation(iface), cell->face_rotation(iface), cell->face_flip(iface)};
401  //Check exterior quadrature point ordering
402  std::vector<bool> face_orientation_ext = {neighbor_cell->face_orientation(neighbor_iface), neighbor_cell->face_rotation(neighbor_iface), neighbor_cell->face_flip(neighbor_iface)};
403  // Extract interior modal coefficients of solution
404  std::array<std::vector<adtype>,nstate> soln_coeff_int;
405  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> aux_soln_coeff_int;
406  for (unsigned int idof = 0; idof < n_dofs_int; ++idof) {
407  const unsigned int istate = this->fe_collection[poly_degree_int].system_to_component_index(idof).first;
408  const unsigned int ishape = this->fe_collection[poly_degree_int].system_to_component_index(idof).second;
409  if(ishape == 0)
410  soln_coeff_int[istate].resize(n_shape_fns_int);
411 
412  soln_coeff_int[istate][ishape] = soln_coeffs_int[idof];
413  for(int idim=0; idim<dim; idim++){
414  if(ishape == 0){
415  aux_soln_coeff_int[istate][idim].resize(n_shape_fns_int);
416  }
417  if(this->use_auxiliary_eq){
418  aux_soln_coeff_int[istate][idim][ishape] = aux_soln_coeffs_int[idim][idof];
419  }
420  else{
421  aux_soln_coeff_int[istate][idim][ishape] = 0.0;
422  }
423  }
424  }
425 
426  // Extract exterior modal coefficients of solution
427  std::array<std::vector<adtype>,nstate> soln_coeff_ext;
428  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> aux_soln_coeff_ext;
429  for (unsigned int idof = 0; idof < n_dofs_ext; ++idof) {
430  const unsigned int istate = this->fe_collection[poly_degree_ext].system_to_component_index(idof).first;
431  const unsigned int ishape = this->fe_collection[poly_degree_ext].system_to_component_index(idof).second;
432  if(ishape == 0){
433  soln_coeff_ext[istate].resize(n_shape_fns_ext);
434  }
435  soln_coeff_ext[istate][ishape] = soln_coeffs_ext[idof];
436  for(int idim=0; idim<dim; idim++){
437  if(ishape == 0){
438  aux_soln_coeff_ext[istate][idim].resize(n_shape_fns_ext);
439  }
440  if(this->use_auxiliary_eq){
441  aux_soln_coeff_ext[istate][idim][ishape] = aux_soln_coeffs_ext[idim][idof];
442  }
443  else{
444  aux_soln_coeff_ext[istate][idim][ishape] = 0.0;
445  }
446  }
447  }
448 
449  if(compute_auxiliary_right_hand_side){
450  assemble_face_term_auxiliary_equation<adtype> (
451  iface, neighbor_iface,
452  current_cell_index, neighbor_cell_index,
453  face_orientation_int, face_orientation_ext,
454  soln_coeff_int, soln_coeff_ext,
455  poly_degree_int, poly_degree_ext,
456  soln_basis_int, soln_basis_ext,
457  metric_oper_int,
458  physics,
459  diss_num_flux,
460  aux_rhs_int, aux_rhs_ext);
461  }
462  else{
463  assemble_face_term_strong<adtype> (
464  iface, neighbor_iface,
465  current_cell_index,
466  neighbor_cell_index,
467  face_orientation_int,
468  face_orientation_ext,
469  soln_coeff_int, soln_coeff_ext,
470  aux_soln_coeff_int, aux_soln_coeff_ext,
471  poly_degree_int, poly_degree_ext,
472  penalty,
473  soln_basis_int, soln_basis_ext,
474  flux_basis_int, flux_basis_ext,
475  soln_basis_projection_oper_int, soln_basis_projection_oper_ext,
476  metric_oper_int, metric_oper_ext,
477  physics, conv_num_flux, diss_num_flux,
478  rhs_int, rhs_ext);
479  for(unsigned int idof=0; idof<n_dofs_int; idof++){
480  dual_dot_residual += rhs_int[idof] * dual_int[idof];
481  }
482  for(unsigned int idof=0; idof<n_dofs_ext; idof++){
483  dual_dot_residual += rhs_ext[idof] * dual_ext[idof];
484  }
485  }
486 
487 }
488 
489 /*******************************************************************
490  *
491  *
492  * AUXILIARY EQUATIONS
493  *
494  *
495  *******************************************************************/
496 
497 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
498 void DGStrong<dim,nspecies,nstate,real,MeshType>::assemble_auxiliary_residual(const bool compute_dRdW, const bool compute_dRdX, const bool compute_d2R)
499 {
502  const PDE_enum pde_type = this->all_parameters->pde_type;
503 
504  if(pde_type == PDE_enum::burgers_viscous){
505  pcout << "DG Strong not yet verified for Burgers' viscous. Aborting..." << std::endl;
506  std::abort();
507  }
508 
509  // NOTE: auxiliary currently only works explicit time advancement - not implicit
510  if (this->use_auxiliary_eq && !(this->all_parameters->ode_solver_param.ode_solver_type == ODE_enum::implicit_solver)) {
511 
512  if(compute_dRdW || compute_dRdX || compute_d2R)
513  {
514  pcout << "DG Strong's viscous terms cannot yet be automatically differentiated. Aborting..."<<std::endl;
515  std::abort();
516  }
517  //set auxiliary rhs to 0
518  for(int idim=0; idim<dim; idim++){
519  this->auxiliary_right_hand_side[idim] = 0;
520  }
521  //initialize this to use DG cell residual loop. Note, FEValues to be deprecated in future.
522  const auto mapping = (*(this->high_order_grid->mapping_fe_field));
523 
524  dealii::hp::MappingCollection<dim> mapping_collection(mapping);
525 
526  dealii::hp::FEValues<dim,dim> fe_values_collection_volume (mapping_collection, this->fe_collection, this->volume_quadrature_collection, this->volume_update_flags);
527  dealii::hp::FEFaceValues<dim,dim> fe_values_collection_face_int (mapping_collection, this->fe_collection, this->face_quadrature_collection, this->face_update_flags);
528  dealii::hp::FEFaceValues<dim,dim> fe_values_collection_face_ext (mapping_collection, this->fe_collection, this->face_quadrature_collection, this->neighbor_face_update_flags);
529  dealii::hp::FESubfaceValues<dim,dim> fe_values_collection_subface (mapping_collection, this->fe_collection, this->face_quadrature_collection, this->face_update_flags);
530 
531  dealii::hp::FEValues<dim,dim> fe_values_collection_volume_lagrange (mapping_collection, this->fe_collection_lagrange, this->volume_quadrature_collection, this->volume_update_flags);
532 
533  OPERATOR::basis_functions<dim,2*dim> soln_basis_int(1, this->max_degree, this->max_grid_degree);
534  OPERATOR::basis_functions<dim,2*dim> soln_basis_ext(1, this->max_degree, this->max_grid_degree);
535  OPERATOR::basis_functions<dim,2*dim> flux_basis_int(1, this->max_degree, this->max_grid_degree);
536  OPERATOR::basis_functions<dim,2*dim> flux_basis_ext(1, this->max_degree, this->max_grid_degree);
537  OPERATOR::local_basis_stiffness<dim,2*dim> flux_basis_stiffness(1, this->max_degree, this->max_grid_degree);
539  OPERATOR::vol_projection_operator<dim,2*dim> soln_basis_projection_oper_int(1, this->max_degree, this->max_grid_degree);
540  OPERATOR::vol_projection_operator<dim,2*dim> soln_basis_projection_oper_ext(1, this->max_degree, this->max_grid_degree);
541 
543  this->max_degree, this->max_degree, this->max_grid_degree,
544  soln_basis_int, soln_basis_ext,
545  flux_basis_int, flux_basis_ext,
546  flux_basis_stiffness,
547  soln_basis_projection_oper_int, soln_basis_projection_oper_ext,
548  mapping_basis);
549 
550  auto metric_cell = this->high_order_grid->dof_handler_grid.begin_active();
551 
552  // Add right-hand side contributions this cell can compute
553  if(compute_d2R)
554  {
555  //loop over cells solving for auxiliary rhs
556  for (auto soln_cell = this->dof_handler.begin_active(); soln_cell != this->dof_handler.end(); ++soln_cell, ++metric_cell) {
557  if (!soln_cell->is_locally_owned()) continue;
558  this->template assemble_cell_residual_and_ad_derivatives<codi_HessianComputationType>(
559  soln_cell,
560  metric_cell,
561  compute_dRdW, compute_dRdX, compute_d2R,
562  fe_values_collection_volume,
563  fe_values_collection_face_int,
564  fe_values_collection_face_ext,
565  fe_values_collection_subface,
566  fe_values_collection_volume_lagrange,
567  soln_basis_int,
568  soln_basis_ext,
569  flux_basis_int,
570  flux_basis_ext,
571  flux_basis_stiffness,
572  soln_basis_projection_oper_int,
573  soln_basis_projection_oper_ext,
574  mapping_basis,
575  true,
576  this->right_hand_side,
578  } // end of cell loop
579  }
580  else if(compute_dRdW || compute_dRdX)
581  {
582  //loop over cells solving for auxiliary rhs
583  for (auto soln_cell = this->dof_handler.begin_active(); soln_cell != this->dof_handler.end(); ++soln_cell, ++metric_cell) {
584  if (!soln_cell->is_locally_owned()) continue;
585  this->template assemble_cell_residual_and_ad_derivatives<codi_JacobianComputationType>(
586  soln_cell,
587  metric_cell,
588  compute_dRdW, compute_dRdX, compute_d2R,
589  fe_values_collection_volume,
590  fe_values_collection_face_int,
591  fe_values_collection_face_ext,
592  fe_values_collection_subface,
593  fe_values_collection_volume_lagrange,
594  soln_basis_int,
595  soln_basis_ext,
596  flux_basis_int,
597  flux_basis_ext,
598  flux_basis_stiffness,
599  soln_basis_projection_oper_int,
600  soln_basis_projection_oper_ext,
601  mapping_basis,
602  true,
603  this->right_hand_side,
605  } // end of cell loop
606  }
607  else
608  {
609  //loop over cells solving for auxiliary rhs
610  for (auto soln_cell = this->dof_handler.begin_active(); soln_cell != this->dof_handler.end(); ++soln_cell, ++metric_cell) {
611  if (!soln_cell->is_locally_owned()) continue;
612  this->template assemble_cell_residual_and_ad_derivatives<double>(
613  soln_cell,
614  metric_cell,
615  compute_dRdW, compute_dRdX, compute_d2R,
616  fe_values_collection_volume,
617  fe_values_collection_face_int,
618  fe_values_collection_face_ext,
619  fe_values_collection_subface,
620  fe_values_collection_volume_lagrange,
621  soln_basis_int,
622  soln_basis_ext,
623  flux_basis_int,
624  flux_basis_ext,
625  flux_basis_stiffness,
626  soln_basis_projection_oper_int,
627  soln_basis_projection_oper_ext,
628  mapping_basis,
629  true,
630  this->right_hand_side,
632  } // end of cell loop
633  }
634 
635  for(int idim=0; idim<dim; idim++){
636  //compress auxiliary rhs for solution transfer across mpi ranks
637  this->auxiliary_right_hand_side[idim].compress(dealii::VectorOperation::add);
638  //update ghost values
639  this->auxiliary_right_hand_side[idim].update_ghost_values();
640 
641  //solve for auxiliary solution for each dimension
644  else
646 
647  //update ghost values of auxiliary solution
648  this->auxiliary_solution[idim].update_ghost_values();
649  }
650  }//end of if statement for diffusive
651  else if (this->use_auxiliary_eq && (this->all_parameters->ode_solver_param.ode_solver_type == ODE_enum::implicit_solver)) {
652  pcout << "ERROR: " << "auxiliary currently only works for explicit time advancement. Aborting..." << std::endl;
653  std::abort();
654  } else {
655  // Do nothing
656  }
657 }
658 
659 /**************************************************
660  *
661  * AUXILIARY RESIDUAL FUNCTIONS
662  *
663  **************************************************/
664 
665 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
666 template <typename adtype>
668  const std::array<std::vector<adtype>,nstate> &soln_coeff,
669  const unsigned int poly_degree,
673  dealii::Tensor<1,dim,std::vector<adtype>> &local_auxiliary_RHS)
674 {
675  //Please see header file for exact formula we are solving.
676  const unsigned int n_quad_pts = this->volume_quadrature_collection[poly_degree].size();
677  const unsigned int n_dofs_cell = this->fe_collection[poly_degree].dofs_per_cell;
678  const unsigned int n_shape_fns = n_dofs_cell / nstate;
679  const std::vector<double> &quad_weights = this->volume_quadrature_collection[poly_degree].get_weights();
680 
681  //Interpolate each state to the quadrature points using sum-factorization
682  //with the basis functions in each reference direction.
683  for(int istate=0; istate<nstate; istate++){
684  std::vector<adtype> soln_at_q(n_quad_pts);
685  //interpolate soln coeff to volume cubature nodes
686  soln_basis.matrix_vector_mult_1D(soln_coeff[istate], soln_at_q,
687  soln_basis.oneD_vol_operator);
688  //the volume integral for the auxiliary equation is the physical integral of the physical gradient of the solution.
689  //That is, we need to physically integrate (we have determinant of Jacobian cancel) the Eq. (12) (with u for chi) in
690  //Cicchino, Alexander, et al. "Provably stable flux reconstruction high-order methods on curvilinear elements." Journal of Computational Physics 463 (2022): 111259.
691 
692  //apply gradient of reference basis functions on the solution at volume cubature nodes
693  dealii::Tensor<1,dim,std::vector<adtype>> ref_gradient_basis_fns_times_soln;
694  for(int idim=0; idim<dim; idim++){
695  ref_gradient_basis_fns_times_soln[idim].resize(n_quad_pts);
696  }
697  flux_basis.gradient_matrix_vector_mult_1D(soln_at_q, ref_gradient_basis_fns_times_soln,
698  flux_basis.oneD_vol_operator,
699  flux_basis.oneD_grad_operator);
700  //transform the gradient into a physical gradient operator scaled by determinant of metric Jacobian
701  //then apply the inner product in each direction
702  for(int idim=0; idim<dim; idim++){
703  std::vector<adtype> phys_gradient_u(n_quad_pts);
704  for(unsigned int iquad=0; iquad<n_quad_pts; iquad++){
705  for(int jdim=0; jdim<dim; jdim++){
706  //transform into the physical gradient
707  phys_gradient_u[iquad] += metric_oper.metric_cofactor_vol[idim][jdim][iquad]
708  * ref_gradient_basis_fns_times_soln[jdim][iquad];
709  }
710  }
711  //Note that we let the determiant of the metric Jacobian cancel off between the integral and physical gradient
712  std::vector<adtype> rhs(n_shape_fns);
713  soln_basis.inner_product_1D(phys_gradient_u, quad_weights,
714  rhs,
715  soln_basis.oneD_vol_operator,
716  false, 1.0);//it's added since auxiliary is EQUAL to the gradient of the soln
717 
718  //write the the auxiliary rhs for the test function.
719  for(unsigned int ishape=0; ishape<n_shape_fns; ishape++){
720  local_auxiliary_RHS[idim][istate*n_shape_fns + ishape] += rhs[ishape];
721  }
722  }
723  }
724 }
725 
726 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
727 template <typename adtype>
729  const unsigned int iface,
730  const dealii::types::global_dof_index current_cell_index,
731  std::vector<bool> face_orientation,
732  const std::array<std::vector<adtype>,nstate> &soln_coeff,
733  const unsigned int poly_degree,
734  const unsigned int boundary_id,
739  dealii::Tensor<1,dim,std::vector<adtype>> &local_auxiliary_RHS)
740 {
741  (void) current_cell_index;
742 
743  const unsigned int n_face_quad_pts = this->face_quadrature_collection[poly_degree].size();
744  const unsigned int n_quad_pts_vol = this->volume_quadrature_collection[poly_degree].size();
745  const unsigned int n_dofs = this->fe_collection[poly_degree].dofs_per_cell;
746  const unsigned int n_shape_fns = n_dofs / nstate;
747 
748  //Interpolate soln to facet, and gradient to facet.
749  std::array<std::vector<adtype>,nstate> soln_at_surf_q;
750  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> ref_grad_soln_at_vol_q;
751  for(int istate=0; istate<nstate; ++istate){
752  //allocate
753  soln_at_surf_q[istate].resize(n_face_quad_pts);
754  //solve soln at facet cubature nodes
755  soln_basis.matrix_vector_mult_surface_1D(face_orientation,
756  iface, soln_coeff[istate], soln_at_surf_q[istate],
757  soln_basis.oneD_surf_operator,
758  soln_basis.oneD_vol_operator);
759  //solve reference gradient of soln at facet cubature nodes
760  for(int idim=0; idim<dim; idim++){
761  ref_grad_soln_at_vol_q[istate][idim].resize(n_quad_pts_vol);
762  }
763  soln_basis.gradient_matrix_vector_mult_1D(soln_coeff[istate], ref_grad_soln_at_vol_q[istate],
764  soln_basis.oneD_vol_operator,
765  soln_basis.oneD_grad_operator);
766  }
767 
768  // Get physical gradient of solution on the surface
769  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> phys_grad_soln_at_surf_q;
770  for(int istate=0; istate<nstate; istate++){
771  //transform the gradient into a physical gradient operator
772  for(int idim=0; idim<dim; idim++){
773  std::vector<adtype> phys_gradient_u(n_quad_pts_vol);
774  for(unsigned int iquad=0; iquad<n_quad_pts_vol; iquad++){
775  for(int jdim=0; jdim<dim; jdim++){
776  //transform into the physical gradient
777  phys_gradient_u[iquad] += metric_oper.metric_cofactor_vol[idim][jdim][iquad]
778  * ref_grad_soln_at_vol_q[istate][jdim][iquad];
779  }
780  phys_gradient_u[iquad] /= metric_oper.det_Jac_vol[iquad];
781  }
782  phys_grad_soln_at_surf_q[istate][idim].resize(n_face_quad_pts);
783  //interpolate physical volume gradient of the solution to the surface
784  soln_basis.matrix_vector_mult_surface_1D(face_orientation,
785  iface, phys_gradient_u, phys_grad_soln_at_surf_q[istate][idim],
786  soln_basis.oneD_surf_operator,
787  soln_basis.oneD_vol_operator);
788  }
789  }
790 
791  //evaluate physical facet fluxes dot product with physical unit normal scaled by determinant of metric facet Jacobian
792  //the outward reference normal dircetion.
793  const dealii::Tensor<1,dim,double> unit_ref_normal_int = dealii::GeometryInfo<dim>::unit_normal_vector[iface];
794  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> surf_num_flux_minus_surf_soln_dot_normal;
795  for(unsigned int iquad=0; iquad<n_face_quad_pts; iquad++){
796  //Copy Metric Cofactor on the facet in a way can use for transforming Tensor Blocks to reference space
797  //The way it is stored in metric_operators is to use sum-factorization in each direction,
798  //but here it is cleaner to apply a reference transformation in each Tensor block returned by physics.
799  //Note that for a conforming mesh, the facet metric cofactor matrix is the same from either interioir or exterior metric terms.
800  //This is verified for the metric computations in: unit_tests/operator_tests/surface_conforming_test.cpp
801  dealii::Tensor<2,dim,adtype> metric_cofactor_surf;
802  for(int idim=0; idim<dim; idim++){
803  for(int jdim=0; jdim<dim; jdim++){
804  metric_cofactor_surf[idim][jdim] = metric_oper.metric_cofactor_surf[idim][jdim][iquad];
805  }
806  }
807  std::array<adtype,nstate> soln_state;
808  std::array<dealii::Tensor<1,dim,adtype>,nstate> phys_grad_soln_state;
809  for(int istate=0; istate<nstate; istate++){
810  soln_state[istate] = soln_at_surf_q[istate][iquad];
811  for(int idim=0; idim<dim; idim++){
812  phys_grad_soln_state[istate][idim] = phys_grad_soln_at_surf_q[istate][idim][iquad];
813  }
814  }
815  //numerical fluxes
816  dealii::Tensor<1,dim,adtype> unit_phys_normal_int;
817  metric_oper.transform_reference_to_physical(unit_ref_normal_int,
818  metric_cofactor_surf,
819  unit_phys_normal_int);
820  adtype face_Jac_norm_scaled = 0.0;
821  for(int idim=0; idim<dim; idim++){
822  face_Jac_norm_scaled += unit_phys_normal_int[idim] * unit_phys_normal_int[idim];
823  }
824  face_Jac_norm_scaled = sqrt(face_Jac_norm_scaled);
825  unit_phys_normal_int /= face_Jac_norm_scaled;//normalize it.
826 
827  std::array<adtype,nstate> soln_boundary;
828  std::array<dealii::Tensor<1,dim,adtype>,nstate> grad_soln_boundary;
829  dealii::Point<dim,adtype> surf_flux_node;
830  for(int idim=0; idim<dim; idim++){
831  surf_flux_node[idim] = metric_oper.flux_nodes_surf[iface][idim][iquad];
832  }
833  pde_physics.boundary_face_values_viscous_flux (boundary_id, surf_flux_node, unit_phys_normal_int, soln_state, phys_grad_soln_state, soln_state, phys_grad_soln_state, soln_boundary, grad_soln_boundary);
834 
835  std::array<adtype,nstate> diss_soln_num_flux;
836  diss_soln_num_flux = diss_num_flux.evaluate_solution_flux(soln_state, soln_boundary, unit_phys_normal_int);
837 
838  for(int istate=0; istate<nstate; istate++){
839  for(int idim=0; idim<dim; idim++){
840  //allocate
841  if(iquad == 0){
842  surf_num_flux_minus_surf_soln_dot_normal[istate][idim].resize(n_face_quad_pts);
843  }
844  //solve
845  surf_num_flux_minus_surf_soln_dot_normal[istate][idim][iquad]
846  = (diss_soln_num_flux[istate] - soln_at_surf_q[istate][iquad]) * unit_phys_normal_int[idim] * face_Jac_norm_scaled;
847  }
848  }
849  }
850  //solve residual and set
851  const std::vector<double> &surf_quad_weights = this->face_quadrature_collection[poly_degree].get_weights();
852  for(int istate=0; istate<nstate; istate++){
853  for(int idim=0; idim<dim; idim++){
854  std::vector<adtype> rhs(n_shape_fns);
855 
856  soln_basis.inner_product_surface_1D(face_orientation,
857  iface,
858  surf_num_flux_minus_surf_soln_dot_normal[istate][idim],
859  surf_quad_weights, rhs,
860  soln_basis.oneD_surf_operator,
861  soln_basis.oneD_vol_operator,
862  false, 1.0);//it's added since auxiliary is EQUAL to the gradient of the soln
863  for(unsigned int ishape=0; ishape<n_shape_fns; ishape++){
864  local_auxiliary_RHS[idim][istate*n_shape_fns + ishape] += rhs[ishape];
865  }
866  }
867  }
868 }
869 /*********************************************************************************/
870 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
871 template <typename adtype>
873  const unsigned int iface,
874  const unsigned int neighbor_iface,
875  const dealii::types::global_dof_index current_cell_index,
876  const dealii::types::global_dof_index neighbor_cell_index,
877  std::vector<bool> face_orientation_int,
878  std::vector<bool> face_orientation_ext,
879  const std::array<std::vector<adtype>,nstate> &soln_coeff_int,
880  const std::array<std::vector<adtype>,nstate> &soln_coeff_ext,
881  const unsigned int poly_degree_int,
882  const unsigned int poly_degree_ext,
883  OPERATOR::basis_functions<dim,2*dim> &soln_basis_int,
884  OPERATOR::basis_functions<dim,2*dim> &soln_basis_ext,
888  dealii::Tensor<1,dim,std::vector<adtype>> &local_auxiliary_RHS_int,
889  dealii::Tensor<1,dim,std::vector<adtype>> &local_auxiliary_RHS_ext)
890 {
891  (void) current_cell_index;
892  (void) neighbor_cell_index;
893 
894  const unsigned int n_face_quad_pts = this->face_quadrature_collection[poly_degree_int].size();//assume interior cell does the work
895 
896  const unsigned int n_dofs_int = this->fe_collection[poly_degree_int].dofs_per_cell;
897  const unsigned int n_dofs_ext = this->fe_collection[poly_degree_ext].dofs_per_cell;
898 
899  const unsigned int n_shape_fns_int = n_dofs_int / nstate;
900  const unsigned int n_shape_fns_ext = n_dofs_ext / nstate;
901 
902  //Interpolate soln modal coefficients to the facet
903  std::array<std::vector<adtype>,nstate> soln_at_surf_q_int;
904  std::array<std::vector<adtype>,nstate> soln_at_surf_q_ext;
905  for(int istate=0; istate<nstate; ++istate){
906  //allocate
907  soln_at_surf_q_int[istate].resize(n_face_quad_pts);
908  soln_at_surf_q_ext[istate].resize(n_face_quad_pts);
909  //solve soln at facet cubature nodes
910  soln_basis_int.matrix_vector_mult_surface_1D(face_orientation_int,
911  iface,
912  soln_coeff_int[istate], soln_at_surf_q_int[istate],
913  soln_basis_int.oneD_surf_operator,
914  soln_basis_int.oneD_vol_operator);
915  soln_basis_ext.matrix_vector_mult_surface_1D(face_orientation_ext,
916  neighbor_iface,
917  soln_coeff_ext[istate], soln_at_surf_q_ext[istate],
918  soln_basis_ext.oneD_surf_operator,
919  soln_basis_ext.oneD_vol_operator);
920  }
921 
922  //evaluate physical facet fluxes dot product with physical unit normal scaled by determinant of metric facet Jacobian
923  //the outward reference normal dircetion.
924  const dealii::Tensor<1,dim,double> unit_ref_normal_int = dealii::GeometryInfo<dim>::unit_normal_vector[iface];
925  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> surf_num_flux_minus_surf_soln_int_dot_normal;
926  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> surf_num_flux_minus_surf_soln_ext_dot_normal;
927  for (unsigned int iquad=0; iquad<n_face_quad_pts; ++iquad) {
928  //Copy Metric Cofactor on the facet in a way can use for transforming Tensor Blocks to reference space
929  //The way it is stored in metric_operators is to use sum-factorization in each direction,
930  //but here it is cleaner to apply a reference transformation in each Tensor block returned by physics.
931  //Note that for a conforming mesh, the facet metric cofactor matrix is the same from either interioir or exterior metric terms.
932  //This is verified for the metric computations in: unit_tests/operator_tests/surface_conforming_test.cpp
933  dealii::Tensor<2,dim,adtype> metric_cofactor_surf;
934  for(int idim=0; idim<dim; idim++){
935  for(int jdim=0; jdim<dim; jdim++){
936  metric_cofactor_surf[idim][jdim] = metric_oper_int.metric_cofactor_surf[idim][jdim][iquad];
937  }
938  }
939  //numerical fluxes
940  dealii::Tensor<1,dim,adtype> unit_phys_normal_int;
941  metric_oper_int.transform_reference_to_physical(unit_ref_normal_int,
942  metric_cofactor_surf,
943  unit_phys_normal_int);
944  adtype face_Jac_norm_scaled = 0.0;
945  for(int idim=0; idim<dim; idim++){
946  face_Jac_norm_scaled += unit_phys_normal_int[idim] * unit_phys_normal_int[idim];
947  }
948  face_Jac_norm_scaled = sqrt(face_Jac_norm_scaled);
949  unit_phys_normal_int /= face_Jac_norm_scaled;//normalize it.
950 
951  std::array<adtype,nstate> diss_soln_num_flux;
952  std::array<adtype,nstate> soln_state_int;
953  std::array<adtype,nstate> soln_state_ext;
954  for(int istate=0; istate<nstate; istate++){
955  soln_state_int[istate] = soln_at_surf_q_int[istate][iquad];
956  soln_state_ext[istate] = soln_at_surf_q_ext[istate][iquad];
957  }
958  diss_soln_num_flux = diss_num_flux.evaluate_solution_flux(soln_state_int, soln_state_ext, unit_phys_normal_int);
959 
960  for(int istate=0; istate<nstate; istate++){
961  for(int idim=0; idim<dim; idim++){
962  //allocate
963  if(iquad == 0){
964  surf_num_flux_minus_surf_soln_int_dot_normal[istate][idim].resize(n_face_quad_pts);
965  surf_num_flux_minus_surf_soln_ext_dot_normal[istate][idim].resize(n_face_quad_pts);
966  }
967  //solve
968  surf_num_flux_minus_surf_soln_int_dot_normal[istate][idim][iquad]
969  = (diss_soln_num_flux[istate] - soln_at_surf_q_int[istate][iquad]) * unit_phys_normal_int[idim] * face_Jac_norm_scaled;
970 
971  surf_num_flux_minus_surf_soln_ext_dot_normal[istate][idim][iquad]
972  = (diss_soln_num_flux[istate] - soln_at_surf_q_ext[istate][iquad]) * (- unit_phys_normal_int[idim]) * face_Jac_norm_scaled;
973  }
974  }
975  }
976  //solve residual and set
977  const std::vector<double> &surf_quad_weights = this->face_quadrature_collection[poly_degree_int].get_weights();
978  for(int istate=0; istate<nstate; istate++){
979  for(int idim=0; idim<dim; idim++){
980  std::vector<adtype> rhs_int(n_shape_fns_int);
981 
982  soln_basis_int.inner_product_surface_1D(face_orientation_int,
983  iface,
984  surf_num_flux_minus_surf_soln_int_dot_normal[istate][idim],
985  surf_quad_weights, rhs_int,
986  soln_basis_int.oneD_surf_operator,
987  soln_basis_int.oneD_vol_operator,
988  false, 1.0);//it's added since auxiliary is EQUAL to the gradient of the soln
989 
990  for(unsigned int ishape=0; ishape<n_shape_fns_int; ishape++){
991  local_auxiliary_RHS_int[idim][istate*n_shape_fns_int + ishape] += rhs_int[ishape];
992  }
993  std::vector<adtype> rhs_ext(n_shape_fns_ext);
994 
995  soln_basis_ext.inner_product_surface_1D(face_orientation_ext,
996  neighbor_iface,
997  surf_num_flux_minus_surf_soln_ext_dot_normal[istate][idim],
998  surf_quad_weights, rhs_ext,
999  soln_basis_ext.oneD_surf_operator,
1000  soln_basis_ext.oneD_vol_operator,
1001  false, 1.0);//it's added since auxiliary is EQUAL to the gradient of the soln
1002 
1003  for(unsigned int ishape=0; ishape<n_shape_fns_ext; ishape++){
1004  local_auxiliary_RHS_ext[idim][istate*n_shape_fns_ext + ishape] += rhs_ext[ishape];
1005  }
1006  }
1007  }
1008 }
1009 
1010 /****************************************************
1011 *
1012 * PRIMARY EQUATIONS STRONG FORM
1013 *
1014 ****************************************************/
1015 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
1016 template <typename adtype>
1018  typename dealii::DoFHandler<dim>::active_cell_iterator cell,
1019  const dealii::types::global_dof_index current_cell_index,
1020  const std::array<std::vector<adtype>,nstate> &soln_coeff,
1021  const std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> &aux_soln_coeff,
1022  const unsigned int poly_degree,
1025  OPERATOR::local_basis_stiffness<dim,2*dim> &flux_basis_stiffness,
1026  OPERATOR::vol_projection_operator<dim,2*dim> &soln_basis_projection_oper,
1029  std::vector<adtype> &local_rhs_int_cell)
1030 {
1031  const unsigned int n_quad_pts = this->volume_quadrature_collection[poly_degree].size();
1032  const unsigned int n_dofs_cell = this->fe_collection[poly_degree].dofs_per_cell;
1033  const unsigned int n_shape_fns = n_dofs_cell / nstate;
1034  const unsigned int n_quad_pts_1D = this->oneD_quadrature_collection[poly_degree].size();
1035  assert(n_quad_pts == pow(n_quad_pts_1D, dim));
1036  const std::vector<double> &vol_quad_weights = this->volume_quadrature_collection[poly_degree].get_weights();
1037  const std::vector<double> &oneD_vol_quad_weights = this->oneD_quadrature_collection[poly_degree].get_weights();
1038 
1039 
1040  std::array<std::vector<adtype>,nstate> soln_at_q;
1041  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> aux_soln_at_q; //auxiliary sol at flux nodes
1042  std::vector<std::array<double,nstate>> soln_at_q_for_max_CFL(n_quad_pts);//Need soln written in a different for to use pre-existing max CFL function
1043  // Interpolate each state to the quadrature points using sum-factorization
1044  // with the basis functions in each reference direction.
1045  for(int istate=0; istate<nstate; istate++){
1046  soln_at_q[istate].resize(n_quad_pts);
1047  soln_basis.matrix_vector_mult_1D(soln_coeff[istate], soln_at_q[istate],
1048  soln_basis.oneD_vol_operator);
1049  for(int idim=0; idim<dim; idim++){
1050  aux_soln_at_q[istate][idim].resize(n_quad_pts);
1051  soln_basis.matrix_vector_mult_1D(aux_soln_coeff[istate][idim], aux_soln_at_q[istate][idim],
1052  soln_basis.oneD_vol_operator);
1053  }
1054  for(unsigned int iquad=0; iquad<n_quad_pts; iquad++){
1055  soln_at_q_for_max_CFL[iquad][istate] = getValue<adtype>(soln_at_q[istate][iquad]);
1056  }
1057  }
1058 
1059  // -- Solution at legendre poly
1060  std::array<std::vector<adtype>,nstate> legendre_soln_at_q;
1061  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> legendre_aux_soln_at_q; // legendre auxiliary sol at flux nodes
1062  if(this->do_compute_filtered_solution) {
1063  // NOTE: This only pertains to advanced SGS models for LES
1064  //==================================================
1065  // GET THE PRIMITIVE SOLUTION
1066  //==================================================
1067  std::array<std::vector<adtype>,nstate> primitive_soln_at_q;
1068  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> primitive_aux_soln_at_q; // primitive auxiliary sol at flux nodes
1069  // Resize the primitive soln arrays
1070  for(int istate=0; istate<nstate; istate++){
1071  primitive_soln_at_q[istate].resize(n_quad_pts);
1072  for(int idim=0; idim<dim; idim++){
1073  primitive_aux_soln_at_q[istate][idim].resize(n_quad_pts);
1074  }
1075  }
1076  // Compute the primitive soln at all iquad and fill arrays
1077  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
1078  // extract conservative soln state
1079  std::array<adtype,nstate> soln_state;
1080  std::array<dealii::Tensor<1,dim,adtype>,nstate> aux_soln_state;
1081  for(int istate=0; istate<nstate; istate++){
1082  soln_state[istate] = soln_at_q[istate][iquad];
1083  for(int idim=0; idim<dim; idim++){
1084  aux_soln_state[istate][idim] = aux_soln_at_q[istate][idim][iquad];
1085  }
1086  }
1087  // compute primitive soln state from conservative
1088  std::array<adtype,nstate> primitive_soln_state = pde_physics.convert_conservative_to_primitive(soln_state);
1089  std::array<dealii::Tensor<1,dim,adtype>,nstate> primitive_aux_soln_state = pde_physics.convert_conservative_gradient_to_primitive_gradient(soln_state,aux_soln_state);
1090  // store primitive soln at quadrature point
1091  for(int istate=0; istate<nstate; istate++){
1092  primitive_soln_at_q[istate][iquad] = primitive_soln_state[istate];
1093  for(int idim=0; idim<dim; idim++){
1094  primitive_aux_soln_at_q[istate][idim][iquad] = primitive_aux_soln_state[istate][idim];
1095  }
1096  }
1097  }
1098 
1099  //==================================================
1100  // PROJECT TO LEGENDRE BASIS AND MODALLY FILTER
1101  //==================================================
1102  // -- Primitive solution at legendre poly
1103  std::array<std::vector<adtype>,nstate> primitive_legendre_soln_at_q;
1104  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> primitive_legendre_aux_soln_at_q; // legendre auxiliary sol at flux nodes
1105 
1106  // Details: this projects to Legendre basis, truncates, then interpolates back to quad nodes.
1107  // -- Constructor for tensor product polynomials based on Polynomials::Legendre interpolation.
1108  dealii::FE_DGQLegendre<1,1> legendre_poly_1D(poly_degree);
1109  // -- Projection operator for legendre basis
1110  OPERATOR::vol_projection_operator<dim,2*dim> legendre_soln_basis_projection_oper(1, poly_degree, this->max_grid_degree);
1111  legendre_soln_basis_projection_oper.build_1D_volume_operator(legendre_poly_1D, this->oneD_quadrature_collection[poly_degree]);
1112  // -- Legendre basis functions
1113  OPERATOR::basis_functions<dim,2*dim> legendre_soln_basis(1, poly_degree, this->max_grid_degree);
1114  legendre_soln_basis.build_1D_volume_operator(legendre_poly_1D, this->oneD_quadrature_collection[poly_degree]);
1115  const unsigned int p_min_filtered = this->poly_degree_max_large_scales + 1;
1116  for(int istate=0; istate<nstate; istate++){
1117  //==================================================
1118  // Solution
1119  //==================================================
1120  // -- (1) Project to Legendre basis
1121  std::vector<adtype> legendre_soln_coeff(n_shape_fns);
1122  legendre_soln_basis_projection_oper.matrix_vector_mult_1D(primitive_soln_at_q[istate], legendre_soln_coeff,
1123  legendre_soln_basis_projection_oper.oneD_vol_operator);
1124  // -- (2) Truncate modes for high-pass filter (i.e. DG-VMS like)
1125  if(this->apply_modal_high_pass_filter_on_filtered_solution && (istate!=0 && istate!=(nstate-1))) {
1126  for(unsigned int ishape=0; ishape<n_shape_fns; ishape++){
1127  if(ishape < p_min_filtered){
1128  legendre_soln_coeff[ishape] = 0.0;
1129  }
1130  }
1131  }
1132  // -- (3) Interpolate filtered solution back to quadrature points
1133  primitive_legendre_soln_at_q[istate].resize(n_quad_pts);
1134  legendre_soln_basis.matrix_vector_mult_1D(legendre_soln_coeff, primitive_legendre_soln_at_q[istate],
1135  legendre_soln_basis.oneD_vol_operator);
1136  //==================================================
1137 
1138  //==================================================
1139  // Auxiliary Solution (gradients)
1140  //==================================================
1141  dealii::Tensor<1,dim,std::vector<adtype>> legendre_aux_soln_coeff;
1142  for(int idim=0; idim<dim; idim++){
1143  // -- (1) Project to Legendre basis
1144  legendre_aux_soln_coeff[idim].resize(n_shape_fns);
1145  if(this->use_auxiliary_eq){
1146  legendre_soln_basis_projection_oper.matrix_vector_mult_1D(primitive_aux_soln_at_q[istate][idim], legendre_aux_soln_coeff[idim],
1147  legendre_soln_basis_projection_oper.oneD_vol_operator);
1148  // -- (2) Truncate modes for high-pass filter (i.e. DG-VMS like)
1149  if(this->apply_modal_high_pass_filter_on_filtered_solution && (istate!=0 && istate!=(nstate-1))) {
1150  for(unsigned int ishape=0; ishape<n_shape_fns; ishape++){
1151  if(ishape < p_min_filtered){
1152  legendre_aux_soln_coeff[idim][ishape] = 0.0;
1153  }
1154  }
1155  }
1156  }
1157  else {
1158  for(unsigned int ishape=0; ishape<n_shape_fns; ishape++){
1159  legendre_aux_soln_coeff[idim][ishape] = 0.0;
1160  }
1161  }
1162  // -- (3) Interpolate filtered solution back to quadrature points
1163  primitive_legendre_aux_soln_at_q[istate][idim].resize(n_quad_pts);
1164  legendre_soln_basis.matrix_vector_mult_1D(legendre_aux_soln_coeff[idim], primitive_legendre_aux_soln_at_q[istate][idim],
1165  legendre_soln_basis.oneD_vol_operator);
1166  }
1167  //==================================================
1168  }
1169  //=======================================================
1170  // CONVERT PRIMITIVE LEGENDRE SOLUTION TO CONSERVATIVE
1171  //=======================================================
1172  // Resize the conservative soln arrays
1173  for(int istate=0; istate<nstate; istate++){
1174  legendre_soln_at_q[istate].resize(n_quad_pts);
1175  for(int idim=0; idim<dim; idim++){
1176  legendre_aux_soln_at_q[istate][idim].resize(n_quad_pts);
1177  }
1178  }
1179  // Compute the primitive soln at all iquad and fill arrays
1180  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
1181  // extract conservative soln state
1182  std::array<adtype,nstate> primitive_legendre_soln_state;
1183  std::array<dealii::Tensor<1,dim,adtype>,nstate> primitive_legendre_aux_soln_state;
1184  for(int istate=0; istate<nstate; istate++){
1185  primitive_legendre_soln_state[istate] = primitive_legendre_soln_at_q[istate][iquad];
1186  for(int idim=0; idim<dim; idim++){
1187  primitive_legendre_aux_soln_state[istate][idim] = primitive_legendre_aux_soln_at_q[istate][idim][iquad];
1188  }
1189  }
1190  // compute conservative soln state from primitive
1191  std::array<adtype,nstate> legendre_soln_state = pde_physics.convert_primitive_to_conservative(primitive_legendre_soln_state);
1192  std::array<dealii::Tensor<1,dim,adtype>,nstate> legendre_aux_soln_state = pde_physics.convert_primitive_gradient_to_conservative_gradient(primitive_legendre_soln_state,primitive_legendre_aux_soln_state);
1193  // store conservative soln at quadrature point
1194  for(int istate=0; istate<nstate; istate++){
1195  legendre_soln_at_q[istate][iquad] = legendre_soln_state[istate];
1196  for(int idim=0; idim<dim; idim++){
1197  legendre_aux_soln_at_q[istate][idim][iquad] = legendre_aux_soln_state[istate][idim];
1198  }
1199  }
1200  }
1201  }
1202 
1203  // For pseudotime, we need to compute the time_scaled_solution.
1204  // Thus, we need to evaluate the max_dt_cell (as previously done in dg/weak_dg.cpp -> assemble_volume_term_explicit)
1205  // Get max artificial dissipation
1206  real max_artificial_diss = 0.0;
1207  const unsigned int n_dofs_arti_diss = this->fe_q_artificial_dissipation.dofs_per_cell;
1208  typename dealii::DoFHandler<dim>::active_cell_iterator artificial_dissipation_cell(
1209  this->triangulation.get(), cell->level(), cell->index(), &(this->dof_handler_artificial_dissipation));
1210  std::vector<dealii::types::global_dof_index> dof_indices_artificial_dissipation(n_dofs_arti_diss);
1211  artificial_dissipation_cell->get_dof_indices (dof_indices_artificial_dissipation);
1212  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
1213  real artificial_diss_coeff_at_q = 0.0;
1215  const dealii::Point<dim,real> point = this->volume_quadrature_collection[poly_degree].point(iquad);
1216  for (unsigned int idof=0; idof<n_dofs_arti_diss; ++idof) {
1217  const unsigned int index = dof_indices_artificial_dissipation[idof];
1218  artificial_diss_coeff_at_q += this->artificial_dissipation_c0[index] * this->fe_q_artificial_dissipation.shape_value(idof, point);
1219  }
1220  max_artificial_diss = std::max(artificial_diss_coeff_at_q, max_artificial_diss);
1221  }
1222  }
1223  // Get max_dt_cell for time_scaled_solution with pseudotime
1224  double cell_volume_estimate = 0.0;
1225  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
1226  cell_volume_estimate += getValue<adtype>(metric_oper.det_Jac_vol[iquad]) * vol_quad_weights[iquad];
1227  }
1228  const real cell_volume = cell_volume_estimate;
1229  const real diameter = cell->diameter();
1230  const real cell_diameter = cell_volume / std::pow(diameter,dim-1);
1231  const real cell_radius = 0.5 * cell_diameter;
1232  this->cell_volume[current_cell_index] = cell_volume;
1233  this->max_dt_cell[current_cell_index] = this->evaluate_CFL ( soln_at_q_for_max_CFL, max_artificial_diss, cell_radius, poly_degree);
1234 
1235  //get entropy projected variables
1236  std::array<std::vector<adtype>,nstate> entropy_var_at_q;
1237  std::array<std::vector<adtype>,nstate> projected_entropy_var_at_q;
1238  if (this->all_parameters->use_split_form || this->all_parameters->use_curvilinear_split_form){
1239  for(int istate=0; istate<nstate; istate++){
1240  entropy_var_at_q[istate].resize(n_quad_pts);
1241  projected_entropy_var_at_q[istate].resize(n_quad_pts);
1242  }
1243  for(unsigned int iquad=0; iquad<n_quad_pts; iquad++){
1244  std::array<adtype,nstate> soln_state;
1245  for(int istate=0; istate<nstate; istate++){
1246  soln_state[istate] = soln_at_q[istate][iquad];
1247  }
1248  std::array<adtype,nstate> entropy_var;
1249  entropy_var = pde_physics.compute_entropy_variables(soln_state);
1250  for(int istate=0; istate<nstate; istate++){
1251  entropy_var_at_q[istate][iquad] = entropy_var[istate];
1252  }
1253  }
1254  for(int istate=0; istate<nstate; istate++){
1255  std::vector<adtype> entropy_var_coeff(n_shape_fns);;
1256  soln_basis_projection_oper.matrix_vector_mult_1D(entropy_var_at_q[istate],
1257  entropy_var_coeff,
1258  soln_basis_projection_oper.oneD_vol_operator);
1259  soln_basis.matrix_vector_mult_1D(entropy_var_coeff,
1260  projected_entropy_var_at_q[istate],
1261  soln_basis.oneD_vol_operator);
1262  }
1263  }
1264 
1265 
1266  //Compute the physical fluxes, then convert them into reference fluxes.
1267  //From the paper: Cicchino, Alexander, et al. "Provably stable flux reconstruction high-order methods on curvilinear elements." Journal of Computational Physics 463 (2022): 111259.
1268  //For conservative DG, we compute the reference flux as per Eq. (9), to then recover the second volume integral in Eq. (17).
1269  //For curvilinear split-form in Eq. (22), we apply a two-pt flux of the metric-cofactor matrix on the matrix operator constructed by the entropy stable/conservtive 2pt flux.
1270  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> conv_ref_flux_at_q;
1271  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> diffusive_ref_flux_at_q;
1272  std::array<std::vector<adtype>,nstate> source_at_q;
1273  std::array<std::vector<adtype>,nstate> physical_source_at_q;
1274 
1275  // The matrix of two-pt fluxes for Hadamard products
1276  std::array<std::array<std::vector<adtype>,dim>,nstate> conv_ref_2pt_flux_at_q;
1277  //Hadamard tensor-product sparsity pattern
1278  std::vector<std::array<unsigned int,dim>> Hadamard_rows_sparsity(n_quad_pts * n_quad_pts_1D);//size n^{d+1}
1279  std::vector<std::array<unsigned int,dim>> Hadamard_columns_sparsity(n_quad_pts * n_quad_pts_1D);
1280  //allocate reference 2pt flux for Hadamard product
1281  if (this->all_parameters->use_split_form || this->all_parameters->use_curvilinear_split_form){
1282  for(int istate=0; istate<nstate; istate++){
1283  for(int idim=0; idim<dim; idim++){
1284  conv_ref_2pt_flux_at_q[istate][idim].resize(n_quad_pts * n_quad_pts_1D);//size n^d x n
1285  }
1286  }
1287  //extract the dof pairs that give non-zero entries for each direction
1288  //to use the "sum-factorized" Hadamard product.
1289  flux_basis.sum_factorized_Hadamard_sparsity_pattern(n_quad_pts_1D, n_quad_pts_1D, Hadamard_rows_sparsity, Hadamard_columns_sparsity);
1290  }
1291 
1292 
1293  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
1294  //extract soln and auxiliary soln at quad pt to be used in physics
1295  std::array<adtype,nstate> soln_state;
1296  std::array<dealii::Tensor<1,dim,adtype>,nstate> aux_soln_state;
1297  std::array<adtype,nstate> filtered_soln_state;
1298  std::array<dealii::Tensor<1,dim,adtype>,nstate> filtered_aux_soln_state;
1299  for(int istate=0; istate<nstate; istate++){
1300  soln_state[istate] = soln_at_q[istate][iquad];
1301  if(this->do_compute_filtered_solution) filtered_soln_state[istate] = legendre_soln_at_q[istate][iquad];
1302  for(int idim=0; idim<dim; idim++){
1303  aux_soln_state[istate][idim] = aux_soln_at_q[istate][idim][iquad];
1304  if(this->do_compute_filtered_solution) filtered_aux_soln_state[istate][idim] = legendre_aux_soln_at_q[istate][idim][iquad];
1305  }
1306  }
1307 
1308  // Copy Metric Cofactor in a way can use for transforming Tensor Blocks to reference space
1309  // The way it is stored in metric_operators is to use sum-factorization in each direction,
1310  // but here it is cleaner to apply a reference transformation in each Tensor block returned by physics.
1311  dealii::Tensor<2,dim,adtype> metric_cofactor;
1312  for(int idim=0; idim<dim; idim++){
1313  for(int jdim=0; jdim<dim; jdim++){
1314  metric_cofactor[idim][jdim] = metric_oper.metric_cofactor_vol[idim][jdim][iquad];
1315  }
1316  }
1317 
1318  // Evaluate physical convective flux
1319  // If 2pt flux, transform to reference at construction to improve performance.
1320  // We technically use a REFERENCE 2pt flux for all entropy stable schemes.
1321  std::array<dealii::Tensor<1,dim,adtype>,nstate> conv_phys_flux;
1322  if (this->all_parameters->use_split_form || this->all_parameters->use_curvilinear_split_form){
1323  //get the soln for iquad from projected entropy variables
1324  std::array<adtype,nstate> entropy_var;
1325  for(int istate=0; istate<nstate; istate++){
1326  entropy_var[istate] = projected_entropy_var_at_q[istate][iquad];
1327  }
1328  soln_state = pde_physics.compute_conservative_variables_from_entropy_variables (entropy_var);
1329 
1330  //loop over all the non-zero entries for "sum-factorized" Hadamard product that corresponds to the iquad.
1331  for(unsigned int row_index = iquad * n_quad_pts_1D, column_index = 0;
1332  // Hadamard_rows_sparsity[row_index][0] == iquad;
1333  column_index < n_quad_pts_1D;
1334  row_index++, column_index++){
1335 
1336  if(Hadamard_rows_sparsity[row_index][0] != iquad){
1337  pcout<<"The volume Hadamard rows sparsity pattern does not match. Aborting..."<<std::endl;
1338  std::abort();
1339  }
1340 
1341  // Copy Metric Cofactor in a way can use for transforming Tensor Blocks to reference space
1342  // The way it is stored in metric_operators is to use sum-factorization in each direction,
1343  // but here it is cleaner to apply a reference transformation in each Tensor block returned by physics.
1344 
1345  for(int ref_dim=0; ref_dim<dim; ref_dim++){
1346  const unsigned int flux_quad = Hadamard_columns_sparsity[row_index][ref_dim];//extract flux_quad pt that corresponds to a non-zero entry for Hadamard product.
1347 
1348  dealii::Tensor<2,dim,adtype> metric_cofactor_flux_basis;
1349  for(int idim=0; idim<dim; idim++){
1350  for(int jdim=0; jdim<dim; jdim++){
1351  metric_cofactor_flux_basis[idim][jdim] = metric_oper.metric_cofactor_vol[idim][jdim][flux_quad];
1352  }
1353  }
1354  std::array<adtype,nstate> soln_state_flux_basis;
1355  std::array<adtype,nstate> entropy_var_flux_basis;
1356  for(int istate=0; istate<nstate; istate++){
1357  entropy_var_flux_basis[istate] = projected_entropy_var_at_q[istate][flux_quad];
1358  }
1359  soln_state_flux_basis = pde_physics.compute_conservative_variables_from_entropy_variables (entropy_var_flux_basis);
1360 
1361  //Compute the physical flux
1362  std::array<dealii::Tensor<1,dim,adtype>,nstate> conv_phys_flux_2pt;
1363  conv_phys_flux_2pt = pde_physics.convective_numerical_split_flux(soln_state, soln_state_flux_basis);
1364 
1365  for(int istate=0; istate<nstate; istate++){
1366  dealii::Tensor<1,dim,adtype> conv_ref_flux_2pt;
1367  //For each state, transform the physical flux to a reference flux.
1368  dealii::Tensor<2,dim,adtype> metric_cofactor_split;
1369  for(int idim=0; idim<dim; idim++){
1370  for(int jdim=0; jdim<dim; jdim++){
1371  metric_cofactor_split[idim][jdim] = 0.5 * (metric_cofactor[idim][jdim] + metric_cofactor_flux_basis[idim][jdim]);
1372  }
1373  }
1374  metric_oper.transform_physical_to_reference(
1375  conv_phys_flux_2pt[istate],
1376  metric_cofactor_split,
1377  conv_ref_flux_2pt);
1378  //write into reference Hadamard flux matrix
1379  conv_ref_2pt_flux_at_q[istate][ref_dim][iquad * n_quad_pts_1D + column_index] = conv_ref_flux_2pt[ref_dim];
1380  }
1381  }
1382  }
1383  }
1384  else{
1385  //Compute the physical flux
1386  conv_phys_flux = pde_physics.convective_flux (soln_state);
1387  }
1388 
1389  //Diffusion
1390  std::array<dealii::Tensor<1,dim,adtype>,nstate> diffusive_phys_flux;
1391  //Compute the physical dissipative flux
1392  diffusive_phys_flux = pde_physics.dissipative_flux(soln_state, aux_soln_state, filtered_soln_state, filtered_aux_soln_state, current_cell_index);
1393 
1394  // Manufactured source
1395  std::array<adtype,nstate> manufactured_source;
1397  dealii::Point<dim,adtype> vol_flux_node;
1398  for(int idim=0; idim<dim; idim++){
1399  vol_flux_node[idim] = metric_oper.flux_nodes_vol[idim][iquad];
1400  }
1401  //compute the manufactured source
1402  manufactured_source = pde_physics.source_term (vol_flux_node, soln_state, this->current_time, current_cell_index);
1403  }
1404 
1405  // Physical source
1406  std::array<adtype,nstate> physical_source;
1407  if(pde_physics.has_nonzero_physical_source) {
1408  dealii::Point<dim,adtype> vol_flux_node;
1409  for(int idim=0; idim<dim; idim++){
1410  vol_flux_node[idim] = metric_oper.flux_nodes_vol[idim][iquad];
1411  }
1412  //compute the physical source
1413  physical_source = pde_physics.physical_source_term (vol_flux_node, soln_state, aux_soln_state, current_cell_index);
1414  }
1415 
1416  //Write the values in a way that we can use sum-factorization on.
1417  for(int istate=0; istate<nstate; istate++){
1418  dealii::Tensor<1,dim,adtype> conv_ref_flux;
1419  dealii::Tensor<1,dim,adtype> diffusive_ref_flux;
1420  //Trnasform to reference fluxes
1421  if (this->all_parameters->use_split_form || this->all_parameters->use_curvilinear_split_form){
1422  //Do Nothing.
1423  //I am leaving this block here so the diligent reader
1424  //remembers that, for entropy stable schemes, we construct
1425  //a REFERENCE two-point flux at construction, where the physical
1426  //to reference transformation was done by splitting the metric cofactor.
1427  }
1428  else{
1429  //transform the conservative convective physical flux to reference space
1430  metric_oper.transform_physical_to_reference(
1431  conv_phys_flux[istate],
1432  metric_cofactor,
1433  conv_ref_flux);
1434  }
1435  //transform the dissipative flux to reference space
1436  metric_oper.transform_physical_to_reference(
1437  diffusive_phys_flux[istate],
1438  metric_cofactor,
1439  diffusive_ref_flux);
1440 
1441  //Write the data in a way that we can use sum-factorization on.
1442  //Since sum-factorization improves the speed for matrix-vector multiplications,
1443  //We need the values to have their inner elements be vectors.
1444  for(int idim=0; idim<dim; idim++){
1445  //allocate
1446  if(iquad == 0){
1447  conv_ref_flux_at_q[istate][idim].resize(n_quad_pts);
1448  diffusive_ref_flux_at_q[istate][idim].resize(n_quad_pts);
1449  }
1450  //write data
1451  if (this->all_parameters->use_split_form || this->all_parameters->use_curvilinear_split_form){
1452  //Do nothing because written in a Hadamard product sum-factorized form above.
1453  }
1454  else{
1455  conv_ref_flux_at_q[istate][idim][iquad] = conv_ref_flux[idim];
1456  }
1457 
1458  diffusive_ref_flux_at_q[istate][idim][iquad] = diffusive_ref_flux[idim];
1459  }
1461  if(iquad == 0){
1462  source_at_q[istate].resize(n_quad_pts);
1463  }
1464  source_at_q[istate][iquad] = manufactured_source[istate];
1465  }
1466  if(pde_physics.has_nonzero_physical_source) {
1467  if(iquad == 0){
1468  physical_source_at_q[istate].resize(n_quad_pts);
1469  }
1470  physical_source_at_q[istate][iquad] = physical_source[istate];
1471  }
1472  }
1473  }
1474 
1475  // Get a flux basis reference gradient operator in a sum-factorized Hadamard product sparse form. Then apply the divergence.
1476  std::array<dealii::FullMatrix<real>,dim> flux_basis_stiffness_skew_symm_oper_sparse;
1477  if (this->all_parameters->use_split_form || this->all_parameters->use_curvilinear_split_form){
1478  for(int idim=0; idim<dim; idim++){
1479  flux_basis_stiffness_skew_symm_oper_sparse[idim].reinit(n_quad_pts, n_quad_pts_1D);
1480  }
1481  flux_basis.sum_factorized_Hadamard_basis_assembly(n_quad_pts_1D, n_quad_pts_1D,
1482  Hadamard_rows_sparsity, Hadamard_columns_sparsity,
1483  flux_basis_stiffness.oneD_skew_symm_vol_oper,
1484  oneD_vol_quad_weights,
1485  flux_basis_stiffness_skew_symm_oper_sparse);
1486  }
1487 
1488  //For each state we:
1489  // 1. Compute reference divergence.
1490  // 2. Then compute and write the rhs for the given state.
1491  for(int istate=0; istate<nstate; istate++){
1492 
1493  //Compute reference divergence of the reference fluxes.
1494  std::vector<adtype> conv_flux_divergence(n_quad_pts);
1495  std::vector<adtype> diffusive_flux_divergence(n_quad_pts);
1496 
1497  if (this->all_parameters->use_split_form || this->all_parameters->use_curvilinear_split_form){
1498  //2pt flux Hadamard Product, and then multiply by vector of ones scaled by 1.
1499  // Same as the volume term in Eq. (15) in Chan, Jesse. "Skew-symmetric entropy stable modal discontinuous Galerkin formulations." Journal of Scientific Computing 81.1 (2019): 459-485. but,
1500  // where we use the reference skew-symmetric stiffness operator of the flux basis for the Q operator and the reference two-point flux as to make use of Alex's Hadamard product
1501  // sum-factorization type algorithm that exploits the structure of the flux basis in the reference space to have O(n^{d+1}).
1502 
1503  for(int ref_dim=0; ref_dim<dim; ref_dim++){
1504  std::vector<adtype> divergence_ref_flux_Hadamard_product(n_quad_pts * n_quad_pts_1D);
1505  flux_basis.Hadamard_product_AD_vector(flux_basis_stiffness_skew_symm_oper_sparse[ref_dim], conv_ref_2pt_flux_at_q[istate][ref_dim], divergence_ref_flux_Hadamard_product);
1506  //Hadamard product times the vector of ones.
1507  for(unsigned int iquad=0; iquad<n_quad_pts; iquad++){
1508  if(ref_dim == 0){
1509  conv_flux_divergence[iquad] = 0.0;
1510  }
1511  for(unsigned int iquad_1D=0; iquad_1D<n_quad_pts_1D; iquad_1D++){
1512  conv_flux_divergence[iquad] += divergence_ref_flux_Hadamard_product[iquad * n_quad_pts_1D + iquad_1D];
1513  }
1514  }
1515  }
1516 
1517  }
1518  else{
1519  //Reference divergence of the reference convective flux.
1520  flux_basis.divergence_matrix_vector_mult_1D(conv_ref_flux_at_q[istate], conv_flux_divergence,
1521  flux_basis.oneD_vol_operator,
1522  flux_basis.oneD_grad_operator);
1523  }
1524  //Reference divergence of the reference diffusive flux.
1525  flux_basis.divergence_matrix_vector_mult_1D(diffusive_ref_flux_at_q[istate], diffusive_flux_divergence,
1526  flux_basis.oneD_vol_operator,
1527  flux_basis.oneD_grad_operator);
1528 
1529 
1530  // Strong form
1531  // The right-hand side sends all the term to the side of the source term
1532  // Therefore,
1533  // \divergence ( Fconv + Fdiss ) = source
1534  // has the right-hand side
1535  // rhs = - \divergence( Fconv + Fdiss ) + source
1536  // Since we have done an integration by parts, the volume term resulting from the divergence of Fconv and Fdiss
1537  // is negative. Therefore, negative of negative means we add that volume term to the right-hand-side
1538  std::vector<adtype> rhs(n_shape_fns);
1539 
1540  // Convective
1541  if (this->all_parameters->use_split_form || this->all_parameters->use_curvilinear_split_form){
1542  std::vector<real> ones(n_quad_pts, 1.0);
1543  soln_basis.inner_product_1D(conv_flux_divergence, ones, rhs, soln_basis.oneD_vol_operator, false, -1.0);
1544  }
1545  else {
1546  soln_basis.inner_product_1D(conv_flux_divergence, vol_quad_weights, rhs, soln_basis.oneD_vol_operator, false, -1.0);
1547  }
1548 
1549  // Diffusive
1550  // Note that for diffusion, the negative is defined in the physics. Since we used the auxiliary
1551  // variable, put a negative here.
1552  soln_basis.inner_product_1D(diffusive_flux_divergence, vol_quad_weights, rhs, soln_basis.oneD_vol_operator, true, -1.0);
1553 
1554  // Manufactured source
1556  std::vector<adtype> JxWxsource(n_quad_pts);
1557  for(unsigned int iquad=0; iquad<n_quad_pts; iquad++){
1558  JxWxsource[iquad] = vol_quad_weights[iquad] * metric_oper.det_Jac_vol[iquad]*source_at_q[istate][iquad];
1559  }
1560  std::vector<real> ones(n_quad_pts, 1.0);
1561  soln_basis.inner_product_1D(JxWxsource, ones, rhs, soln_basis.oneD_vol_operator, true, 1.0);
1562  }
1563 
1564  // Physical source
1565  if(pde_physics.has_nonzero_physical_source) {
1566  std::vector<adtype> JxWxphys_source(n_quad_pts);
1567  for(unsigned int iquad=0; iquad<n_quad_pts; iquad++){
1568  JxWxphys_source[iquad] = vol_quad_weights[iquad] * metric_oper.det_Jac_vol[iquad] * physical_source_at_q[istate][iquad];
1569  }
1570  std::vector<real> ones(n_quad_pts, 1.0);
1571  soln_basis.inner_product_1D(JxWxphys_source, ones, rhs, soln_basis.oneD_vol_operator, true, 1.0);
1572  }
1573 
1574  for(unsigned int ishape=0; ishape<n_shape_fns; ishape++){
1575  local_rhs_int_cell[istate*n_shape_fns + ishape] += rhs[ishape];
1576  }
1577 
1578  }
1579 }
1580 
1581 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
1582 template <typename adtype>
1584  typename dealii::DoFHandler<dim>::active_cell_iterator current_cell,
1585  const unsigned int iface,
1586  const dealii::types::global_dof_index current_cell_index,
1587  std::vector<bool> face_orientation,
1588  const std::array<std::vector<adtype>,nstate> &soln_coeff,
1589  const std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> &aux_soln_coeff,
1590  const unsigned int boundary_id,
1591  const unsigned int poly_degree,
1592  const real penalty,
1595  OPERATOR::vol_projection_operator<dim,2*dim> &soln_basis_projection_oper,
1600  std::vector<adtype> &local_rhs_cell)
1601 {
1602  // Get opposite face index
1603  const int opposite_iface = (iface == 0) ? 1 : (
1604  (iface == 1) ? 0 : (
1605  (iface == 2) ? 3 : (
1606  (iface == 3) ? 2 : (
1607  (iface == 4) ? 5 : (
1608  (iface == 5) ? 4 : -1)))));
1609  if(opposite_iface == -1) {
1610  pcout << "ERROR: Invalid iface, opposite_iface is -1. Aborting..."<<std::endl;
1611  std::abort();
1612  }
1613  std::vector<bool> opposite_face_orientation = {current_cell->face_orientation(opposite_iface), current_cell->face_rotation(opposite_iface), current_cell->face_flip(opposite_iface)};
1614 
1615  // Find the neighbor cell opposite to the boundary of interest
1616  // Used for the wall model.
1617  // A dummy cell and face index are returned if the wall model is not being used.
1618  const auto neighbor_cell = (this->using_wall_model) ? current_cell->neighbor(opposite_iface) : current_cell;
1619  const unsigned int neighbor_iface = (this->using_wall_model) ? current_cell->neighbor_face_no(opposite_iface) : 0;
1620  const int i_fele_n = neighbor_cell->active_fe_index();//, i_quad_n = i_fele_n, i_mapp_n = 0;
1621  const unsigned int n_dofs_neigh_cell = this->fe_collection[i_fele_n].n_dofs_per_cell();
1622  // Obtain the mapping from local dof indices to global dof indices for neighbor cell
1623  std::vector<dealii::types::global_dof_index> neighbor_dofs_indices;
1624  neighbor_dofs_indices.resize(n_dofs_neigh_cell);
1625  neighbor_cell->get_dof_indices (neighbor_dofs_indices);
1626  std::vector<bool> neighbor_face_orientation = {neighbor_cell->face_orientation(neighbor_iface), neighbor_cell->face_rotation(neighbor_iface), neighbor_cell->face_flip(neighbor_iface)};
1627 
1628  AssertDimension (n_dofs_neigh_cell, neighbor_dofs_indices.size());
1629 
1630  const unsigned int n_face_quad_pts = this->face_quadrature_collection[poly_degree].size();
1631  const unsigned int n_quad_pts_vol = this->volume_quadrature_collection[poly_degree].size();
1632  const unsigned int n_quad_pts_1D = this->oneD_quadrature_collection[poly_degree].size();
1633  const unsigned int n_dofs = this->fe_collection[poly_degree].dofs_per_cell;
1634  const unsigned int n_shape_fns = n_dofs / nstate;
1635  const std::vector<double> &face_quad_weights = this->face_quadrature_collection[poly_degree].get_weights();
1636 
1637  // Fetch the modal soln coefficients and the modal auxiliary soln coefficients
1638  // We immediately separate them by state as to be able to use sum-factorization
1639  // in the interpolation operator. If we left it by n_dofs_cell, then the matrix-vector
1640  // mult would sum the states at the quadrature point.
1641  std::array<std::vector<adtype>,nstate> neighbor_soln_coeff; //NOTE: This is NOT being automtically differentiated.
1642  for (unsigned int idof = 0; idof < n_dofs; ++idof) {
1643  const unsigned int istate = this->fe_collection[poly_degree].system_to_component_index(idof).first;
1644  const unsigned int ishape = this->fe_collection[poly_degree].system_to_component_index(idof).second;
1645  // allocate
1646  if(ishape == 0){
1647  neighbor_soln_coeff[istate].resize(n_shape_fns);
1648  }
1649  // solve
1650  neighbor_soln_coeff[istate][ishape] = this->solution(neighbor_dofs_indices[idof]);
1651  }
1652 
1653  // Interpolate the modal coefficients to the volume cubature nodes.
1654  std::array<std::vector<adtype>,nstate> soln_at_vol_q;
1655  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> aux_soln_at_vol_q;
1656  // Interpolate modal soln coefficients to the facet.
1657  std::array<std::vector<adtype>,nstate> soln_at_surf_q;
1658  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> aux_soln_at_surf_q;
1659  // Opposite surface solution for wall model
1660  std::array<std::vector<adtype>,nstate> soln_at_opposite_surf_q;
1661  for(int istate=0; istate<nstate; ++istate){
1662  //allocate
1663  soln_at_vol_q[istate].resize(n_quad_pts_vol);
1664  //solve soln at volume cubature nodes
1665  soln_basis.matrix_vector_mult_1D(soln_coeff[istate], soln_at_vol_q[istate],
1666  soln_basis.oneD_vol_operator);
1667 
1668  //allocate
1669  soln_at_surf_q[istate].resize(n_face_quad_pts);
1670  //solve soln at facet cubature nodes
1671  soln_basis.matrix_vector_mult_surface_1D(face_orientation,
1672  iface,
1673  soln_coeff[istate], soln_at_surf_q[istate],
1674  soln_basis.oneD_surf_operator,
1675  soln_basis.oneD_vol_operator);
1676  if(this->using_wall_model && (boundary_id == 1001)) {
1677  //allocate
1678  soln_at_opposite_surf_q[istate].resize(n_face_quad_pts);
1679  //solve soln at facet cubature nodes
1680  if(this->wall_model_input_from_second_element) {
1681  soln_basis.matrix_vector_mult_surface_1D(neighbor_face_orientation, neighbor_iface,
1682  neighbor_soln_coeff[istate], soln_at_opposite_surf_q[istate],
1683  soln_basis.oneD_surf_operator,
1684  soln_basis.oneD_vol_operator);
1685  } else {
1686  soln_basis.matrix_vector_mult_surface_1D(opposite_face_orientation, opposite_iface,
1687  soln_coeff[istate], soln_at_opposite_surf_q[istate],
1688  soln_basis.oneD_surf_operator,
1689  soln_basis.oneD_vol_operator);
1690  }
1691  }
1692 
1693  for(int idim=0; idim<dim; idim++){
1694  //alocate
1695  aux_soln_at_vol_q[istate][idim].resize(n_quad_pts_vol);
1696  //solve auxiliary soln at volume cubature nodes
1697  soln_basis.matrix_vector_mult_1D(aux_soln_coeff[istate][idim], aux_soln_at_vol_q[istate][idim],
1698  soln_basis.oneD_vol_operator);
1699 
1700  //allocate
1701  aux_soln_at_surf_q[istate][idim].resize(n_face_quad_pts);
1702  //solve auxiliary soln at facet cubature nodes
1703  soln_basis.matrix_vector_mult_surface_1D(face_orientation,
1704  iface,
1705  aux_soln_coeff[istate][idim], aux_soln_at_surf_q[istate][idim],
1706  soln_basis.oneD_surf_operator,
1707  soln_basis.oneD_vol_operator);
1708  }
1709  }
1710 
1711  // -- Solution at legendre poly
1712  // -- (a) Interpolate the modal coefficients to the volume cubature nodes.
1713  std::array<std::vector<adtype>,nstate> legendre_soln_at_vol_q;
1714  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> legendre_aux_soln_at_vol_q; // legendre auxiliary sol at flux nodes
1715  // -- (b) Interpolate modal soln coefficients to the facet.
1716  std::array<std::vector<adtype>,nstate> legendre_soln_at_surf_q;
1717  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> legendre_aux_soln_at_surf_q; // legendre auxiliary sol at flux nodes
1718  if(this->do_compute_filtered_solution) {
1719  // NOTE: This only pertains to advanced SGS models for LES
1720  //==================================================
1721  // GET THE PRIMITIVE SOLUTION
1722  //==================================================
1723  std::array<std::vector<adtype>,nstate> primitive_soln_at_vol_q;
1724  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> primitive_aux_soln_at_vol_q;
1725  std::array<std::vector<adtype>,nstate> primitive_soln_at_surf_q;
1726  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> primitive_aux_soln_at_surf_q;
1727  // Resize the primitive soln arrays
1728  for(int istate=0; istate<nstate; istate++){
1729  primitive_soln_at_vol_q[istate].resize(n_quad_pts_vol);
1730  primitive_soln_at_surf_q[istate].resize(n_face_quad_pts);
1731  for(int idim=0; idim<dim; idim++){
1732  primitive_aux_soln_at_vol_q[istate][idim].resize(n_quad_pts_vol);
1733  primitive_aux_soln_at_surf_q[istate][idim].resize(n_face_quad_pts);
1734  }
1735  }
1736  // Compute the primitive soln at all iquad and fill arrays
1737  // -- volume
1738  for (unsigned int iquad=0; iquad<n_quad_pts_vol; ++iquad) {
1739  // extract conservative soln state
1740  std::array<adtype,nstate> soln_state;
1741  std::array<dealii::Tensor<1,dim,adtype>,nstate> aux_soln_state;
1742  for(int istate=0; istate<nstate; istate++){
1743  soln_state[istate] = soln_at_vol_q[istate][iquad];
1744  for(int idim=0; idim<dim; idim++){
1745  aux_soln_state[istate][idim] = aux_soln_at_vol_q[istate][idim][iquad];
1746  }
1747  }
1748  // compute primitive soln state from conservative
1749  std::array<adtype,nstate> primitive_soln_state = pde_physics.convert_conservative_to_primitive(soln_state);
1750  std::array<dealii::Tensor<1,dim,adtype>,nstate> primitive_aux_soln_state = pde_physics.convert_conservative_gradient_to_primitive_gradient(soln_state,aux_soln_state);
1751  // store primitive soln at quadrature point
1752  for(int istate=0; istate<nstate; istate++){
1753  primitive_soln_at_vol_q[istate][iquad] = primitive_soln_state[istate];
1754  for(int idim=0; idim<dim; idim++){
1755  primitive_aux_soln_at_vol_q[istate][idim][iquad] = primitive_aux_soln_state[istate][idim];
1756  }
1757  }
1758  }
1759  // -- surface
1760  for(unsigned int iquad_face=0; iquad_face<n_face_quad_pts; iquad_face++){
1761  // extract conservative soln state
1762  std::array<adtype,nstate> soln_state;
1763  std::array<dealii::Tensor<1,dim,adtype>,nstate> aux_soln_state;
1764  for(int istate=0; istate<nstate; istate++){
1765  soln_state[istate] = soln_at_surf_q[istate][iquad_face];
1766  for(int idim=0; idim<dim; idim++){
1767  aux_soln_state[istate][idim] = aux_soln_at_surf_q[istate][idim][iquad_face];
1768  }
1769  }
1770  // compute primitive soln state from conservative
1771  std::array<adtype,nstate> primitive_soln_state = pde_physics.convert_conservative_to_primitive(soln_state);
1772  std::array<dealii::Tensor<1,dim,adtype>,nstate> primitive_aux_soln_state = pde_physics.convert_conservative_gradient_to_primitive_gradient(soln_state,aux_soln_state);
1773  // store primitive soln at quadrature point
1774  for(int istate=0; istate<nstate; istate++){
1775  primitive_soln_at_surf_q[istate][iquad_face] = primitive_soln_state[istate];
1776  for(int idim=0; idim<dim; idim++){
1777  primitive_aux_soln_at_surf_q[istate][idim][iquad_face] = primitive_aux_soln_state[istate][idim];
1778  }
1779  }
1780  }
1781 
1782  //==================================================
1783  // PROJECT TO LEGENDRE BASIS AND MODALLY FILTER
1784  //==================================================
1785  // -- Primitive solution at legendre poly
1786  std::array<std::vector<adtype>,nstate> primitive_legendre_soln_at_vol_q;
1787  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> primitive_legendre_aux_soln_at_vol_q;
1788  std::array<std::vector<adtype>,nstate> primitive_legendre_soln_at_surf_q;
1789  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> primitive_legendre_aux_soln_at_surf_q;
1790 
1791  // Details: this projects to Legendre basis, truncates, then interpolates back to quad nodes.
1792  // -- Constructor for tensor product polynomials based on Polynomials::Legendre interpolation.
1793  dealii::FE_DGQLegendre<1,1> legendre_poly_1D(poly_degree);
1794  // -- Projection operator for legendre basis
1795  OPERATOR::vol_projection_operator<dim,2*dim> legendre_soln_basis_projection_oper(1, poly_degree, this->max_grid_degree);
1796  legendre_soln_basis_projection_oper.build_1D_volume_operator(legendre_poly_1D, this->oneD_quadrature_collection[poly_degree]);
1797  // -- Legendre basis functions
1798  OPERATOR::basis_functions<dim,2*dim> legendre_soln_basis(1, poly_degree, this->max_grid_degree);
1799  legendre_soln_basis.build_1D_volume_operator(legendre_poly_1D, this->oneD_quadrature_collection[poly_degree]);
1800  legendre_soln_basis.build_1D_surface_operator(legendre_poly_1D, this->oneD_face_quadrature);
1801  const unsigned int p_min_filtered = this->poly_degree_max_large_scales + 1;
1802  for(int istate=0; istate<nstate; istate++){
1803  //==================================================
1804  // Solution
1805  //==================================================
1806  // -- (1) Project to Legendre basis
1807  std::vector<adtype> legendre_soln_coeff(n_shape_fns);
1808  legendre_soln_basis_projection_oper.matrix_vector_mult_1D(primitive_soln_at_vol_q[istate], legendre_soln_coeff,
1809  legendre_soln_basis_projection_oper.oneD_vol_operator);
1810  // -- (2) Truncate modes for high-pass filter (i.e. DG-VMS like)
1811  if(this->apply_modal_high_pass_filter_on_filtered_solution && (istate!=0 && istate!=(nstate-1))) {
1812  for(unsigned int ishape=0; ishape<n_shape_fns; ishape++){
1813  if(ishape < p_min_filtered){
1814  legendre_soln_coeff[ishape] = 0.0;
1815  }
1816  }
1817  }
1818  // -- (3) Interpolate filtered solution back to quadrature points
1819  primitive_legendre_soln_at_vol_q[istate].resize(n_quad_pts_vol);
1820  legendre_soln_basis.matrix_vector_mult_1D(legendre_soln_coeff, primitive_legendre_soln_at_vol_q[istate],
1821  legendre_soln_basis.oneD_vol_operator);
1822  primitive_legendre_soln_at_surf_q[istate].resize(n_face_quad_pts);
1823  legendre_soln_basis.matrix_vector_mult_surface_1D(face_orientation,
1824  iface,
1825  legendre_soln_coeff, primitive_legendre_soln_at_surf_q[istate],
1826  legendre_soln_basis.oneD_surf_operator,
1827  legendre_soln_basis.oneD_vol_operator);
1828  //==================================================
1829 
1830  //==================================================
1831  // Auxiliary Solution (gradients)
1832  //==================================================
1833  dealii::Tensor<1,dim,std::vector<adtype>> legendre_aux_soln_coeff;
1834  for(int idim=0; idim<dim; idim++){
1835  // -- (1) Project to Legendre basis
1836  legendre_aux_soln_coeff[idim].resize(n_shape_fns);
1837  if(this->use_auxiliary_eq){
1838  legendre_soln_basis_projection_oper.matrix_vector_mult_1D(primitive_aux_soln_at_vol_q[istate][idim], legendre_aux_soln_coeff[idim],
1839  legendre_soln_basis_projection_oper.oneD_vol_operator);
1840  // -- (2) Truncate modes for high-pass filter (i.e. DG-VMS like)
1841  if(this->apply_modal_high_pass_filter_on_filtered_solution && (istate!=0 && istate!=(nstate-1))) {
1842  for(unsigned int ishape=0; ishape<n_shape_fns; ishape++){
1843  if(ishape < p_min_filtered){
1844  legendre_aux_soln_coeff[idim][ishape] = 0.0;
1845  }
1846  }
1847  }
1848  }
1849  else {
1850  for(unsigned int ishape=0; ishape<n_shape_fns; ishape++){
1851  legendre_aux_soln_coeff[idim][ishape] = 0.0;
1852  }
1853  }
1854  // -- (3) Interpolate filtered solution back to quadrature points
1855  primitive_legendre_aux_soln_at_vol_q[istate][idim].resize(n_quad_pts_vol);
1856  legendre_soln_basis.matrix_vector_mult_1D(legendre_aux_soln_coeff[idim], primitive_legendre_aux_soln_at_vol_q[istate][idim],
1857  legendre_soln_basis.oneD_vol_operator);
1858  primitive_legendre_aux_soln_at_surf_q[istate][idim].resize(n_face_quad_pts);
1859  legendre_soln_basis.matrix_vector_mult_surface_1D(face_orientation,
1860  iface,
1861  legendre_aux_soln_coeff[idim], primitive_legendre_aux_soln_at_surf_q[istate][idim],
1862  legendre_soln_basis.oneD_surf_operator,
1863  legendre_soln_basis.oneD_vol_operator);
1864  }
1865  //==================================================
1866  }
1867  //=======================================================
1868  // CONVERT PRIMITIVE LEGENDRE SOLUTION TO CONSERVATIVE
1869  //=======================================================
1870  // Resize the conservative soln arrays
1871  for(int istate=0; istate<nstate; istate++){
1872  legendre_soln_at_vol_q[istate].resize(n_quad_pts_vol);
1873  legendre_soln_at_surf_q[istate].resize(n_face_quad_pts);
1874  for(int idim=0; idim<dim; idim++){
1875  legendre_aux_soln_at_vol_q[istate][idim].resize(n_quad_pts_vol);
1876  legendre_aux_soln_at_surf_q[istate][idim].resize(n_face_quad_pts);
1877  }
1878  }
1879  // Compute the primitive soln at all iquad and fill arrays
1880  // -- volume
1881  for (unsigned int iquad=0; iquad<n_quad_pts_vol; ++iquad) {
1882  // extract conservative soln state
1883  std::array<adtype,nstate> primitive_legendre_soln_state;
1884  std::array<dealii::Tensor<1,dim,adtype>,nstate> primitive_legendre_aux_soln_state;
1885  for(int istate=0; istate<nstate; istate++){
1886  primitive_legendre_soln_state[istate] = primitive_legendre_soln_at_vol_q[istate][iquad];
1887  for(int idim=0; idim<dim; idim++){
1888  primitive_legendre_aux_soln_state[istate][idim] = primitive_legendre_aux_soln_at_vol_q[istate][idim][iquad];
1889  }
1890  }
1891  // compute conservative soln state from primitive
1892  std::array<adtype,nstate> legendre_soln_state = pde_physics.convert_primitive_to_conservative(primitive_legendre_soln_state);
1893  std::array<dealii::Tensor<1,dim,adtype>,nstate> legendre_aux_soln_state = pde_physics.convert_primitive_gradient_to_conservative_gradient(primitive_legendre_soln_state,primitive_legendre_aux_soln_state);
1894  // store conservative soln at quadrature point
1895  for(int istate=0; istate<nstate; istate++){
1896  legendre_soln_at_vol_q[istate][iquad] = legendre_soln_state[istate];
1897  for(int idim=0; idim<dim; idim++){
1898  legendre_aux_soln_at_vol_q[istate][idim][iquad] = legendre_aux_soln_state[istate][idim];
1899  }
1900  }
1901  }
1902  // -- surface
1903  for (unsigned int iquad_face=0; iquad_face<n_face_quad_pts; iquad_face++) {
1904  // extract conservative soln state
1905  std::array<adtype,nstate> primitive_legendre_soln_state;
1906  std::array<dealii::Tensor<1,dim,adtype>,nstate> primitive_legendre_aux_soln_state;
1907  for(int istate=0; istate<nstate; istate++){
1908  primitive_legendre_soln_state[istate] = primitive_legendre_soln_at_surf_q[istate][iquad_face];
1909  for(int idim=0; idim<dim; idim++){
1910  primitive_legendre_aux_soln_state[istate][idim] = primitive_legendre_aux_soln_at_surf_q[istate][idim][iquad_face];
1911  }
1912  }
1913  // compute conservative soln state from primitive
1914  std::array<adtype,nstate> legendre_soln_state = pde_physics.convert_primitive_to_conservative(primitive_legendre_soln_state);
1915  std::array<dealii::Tensor<1,dim,adtype>,nstate> legendre_aux_soln_state = pde_physics.convert_primitive_gradient_to_conservative_gradient(primitive_legendre_soln_state,primitive_legendre_aux_soln_state);
1916  // store conservative soln at quadrature point
1917  for(int istate=0; istate<nstate; istate++){
1918  legendre_soln_at_surf_q[istate][iquad_face] = legendre_soln_state[istate];
1919  for(int idim=0; idim<dim; idim++){
1920  legendre_aux_soln_at_surf_q[istate][idim][iquad_face] = legendre_aux_soln_state[istate][idim];
1921  }
1922  }
1923  }
1924  }
1925 
1926  // Get volume reference fluxes and interpolate them to the facet.
1927  // Compute reference volume fluxes in both interior and exterior cells.
1928 
1929  // First we do interior.
1930  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> conv_ref_flux_at_vol_q;
1931  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> diffusive_ref_flux_at_vol_q;
1932  for (unsigned int iquad=0; iquad<n_quad_pts_vol; ++iquad) {
1933  // Copy Metric Cofactor in a way can use for transforming Tensor Blocks to reference space
1934  // The way it is stored in metric_operators is to use sum-factorization in each direction,
1935  // but here it is cleaner to apply a reference transformation in each Tensor block returned by physics.
1936  dealii::Tensor<2,dim,adtype> metric_cofactor_vol;
1937  for(int idim=0; idim<dim; idim++){
1938  for(int jdim=0; jdim<dim; jdim++){
1939  metric_cofactor_vol[idim][jdim] = metric_oper.metric_cofactor_vol[idim][jdim][iquad];
1940  }
1941  }
1942  std::array<adtype,nstate> soln_state;
1943  std::array<dealii::Tensor<1,dim,adtype>,nstate> aux_soln_state;
1944  std::array<adtype,nstate> filtered_soln_state;
1945  std::array<dealii::Tensor<1,dim,adtype>,nstate> filtered_aux_soln_state;
1946  for(int istate=0; istate<nstate; istate++){
1947  soln_state[istate] = soln_at_vol_q[istate][iquad];
1948  if(this->do_compute_filtered_solution) filtered_soln_state[istate] = legendre_soln_at_vol_q[istate][iquad];
1949  for(int idim=0; idim<dim; idim++){
1950  aux_soln_state[istate][idim] = aux_soln_at_vol_q[istate][idim][iquad];
1951  if(this->do_compute_filtered_solution) filtered_aux_soln_state[istate][idim] = legendre_aux_soln_at_vol_q[istate][idim][iquad];
1952  }
1953  }
1954 
1955  // Evaluate physical convective flux
1956  std::array<dealii::Tensor<1,dim,adtype>,nstate> conv_phys_flux;
1957  if(!this->all_parameters->use_split_form && !this->all_parameters->use_curvilinear_split_form){
1958  conv_phys_flux = pde_physics.convective_flux (soln_state);
1959  }
1960 
1961  // Compute the physical dissipative flux
1962  std::array<dealii::Tensor<1,dim,adtype>,nstate> diffusive_phys_flux;
1963  diffusive_phys_flux = pde_physics.dissipative_flux(soln_state, aux_soln_state, filtered_soln_state, filtered_aux_soln_state, current_cell_index);
1964 
1965  // Write the values in a way that we can use sum-factorization on.
1966  for(int istate=0; istate<nstate; istate++){
1967  dealii::Tensor<1,dim,adtype> conv_ref_flux;
1968  dealii::Tensor<1,dim,adtype> diffusive_ref_flux;
1969  // transform the conservative convective physical flux to reference space
1970  if(!this->all_parameters->use_split_form && !this->all_parameters->use_curvilinear_split_form){
1971  metric_oper.transform_physical_to_reference(
1972  conv_phys_flux[istate],
1973  metric_cofactor_vol,
1974  conv_ref_flux);
1975  }
1976  // transform the dissipative flux to reference space
1977  metric_oper.transform_physical_to_reference(
1978  diffusive_phys_flux[istate],
1979  metric_cofactor_vol,
1980  diffusive_ref_flux);
1981 
1982  // Write the data in a way that we can use sum-factorization on.
1983  // Since sum-factorization improves the speed for matrix-vector multiplications,
1984  // We need the values to have their inner elements be vectors.
1985  for(int idim=0; idim<dim; idim++){
1986  //allocate
1987  if(iquad == 0){
1988  conv_ref_flux_at_vol_q[istate][idim].resize(n_quad_pts_vol);
1989  diffusive_ref_flux_at_vol_q[istate][idim].resize(n_quad_pts_vol);
1990  }
1991  //write data
1992  if(!this->all_parameters->use_split_form && !this->all_parameters->use_curvilinear_split_form){
1993  conv_ref_flux_at_vol_q[istate][idim][iquad] = conv_ref_flux[idim];
1994  }
1995 
1996  diffusive_ref_flux_at_vol_q[istate][idim][iquad] = diffusive_ref_flux[idim];
1997  }
1998  }
1999  }
2000 
2001  // Interpolate the volume reference fluxes to the facet.
2002  // And do the dot product with the UNIT REFERENCE normal.
2003  // Since we are computing a dot product with the unit reference normal,
2004  // we exploit the fact that the unit reference normal has a value of 0 in all reference directions except
2005  // the outward reference normal dircetion.
2006  const dealii::Tensor<1,dim,double> unit_ref_normal_int = dealii::GeometryInfo<dim>::unit_normal_vector[iface];
2007  const int dim_not_zero = iface / 2;//reference direction of face integer division
2008 
2009  std::array<std::vector<adtype>,nstate> conv_int_vol_ref_flux_interp_to_face_dot_ref_normal;
2010  std::array<std::vector<adtype>,nstate> diffusive_int_vol_ref_flux_interp_to_face_dot_ref_normal;
2011  for(int istate=0; istate<nstate; istate++){
2012  //allocate
2013  conv_int_vol_ref_flux_interp_to_face_dot_ref_normal[istate].resize(n_face_quad_pts);
2014  diffusive_int_vol_ref_flux_interp_to_face_dot_ref_normal[istate].resize(n_face_quad_pts);
2015 
2016  //solve
2017  //Note, since the normal is zero in all other reference directions, we only have to interpolate one given reference direction to the facet
2018 
2019  //interpolate reference volume convective flux to the facet, and apply unit reference normal as scaled by 1.0 or -1.0
2020  if(!this->all_parameters->use_split_form && !this->all_parameters->use_curvilinear_split_form){
2021  flux_basis.matrix_vector_mult_surface_1D(face_orientation,
2022  iface,
2023  conv_ref_flux_at_vol_q[istate][dim_not_zero],
2024  conv_int_vol_ref_flux_interp_to_face_dot_ref_normal[istate],
2025  flux_basis.oneD_surf_operator,//the flux basis interpolates from the flux nodes
2026  flux_basis.oneD_vol_operator,
2027  false, unit_ref_normal_int[dim_not_zero]);//don't add to previous value, scale by unit_normal int
2028  }
2029 
2030  //interpolate reference volume dissipative flux to the facet, and apply unit reference normal as scaled by 1.0 or -1.0
2031  flux_basis.matrix_vector_mult_surface_1D(face_orientation,
2032  iface,
2033  diffusive_ref_flux_at_vol_q[istate][dim_not_zero],
2034  diffusive_int_vol_ref_flux_interp_to_face_dot_ref_normal[istate],
2035  flux_basis.oneD_surf_operator,
2036  flux_basis.oneD_vol_operator,
2037  false, unit_ref_normal_int[dim_not_zero]);
2038  }
2039 
2040  //Note that for entropy-dissipation and entropy stability, the conservative variables
2041  //are functions of projected entropy variables. For Euler etc, the transformation is nonlinear
2042  //so careful attention to what is evaluated where and interpolated to where is needed.
2043  //For further information, please see Chan, Jesse. "On discretely entropy conservative and entropy stable discontinuous Galerkin methods." Journal of Computational Physics 362 (2018): 346-374.
2044  //pages 355 (Eq. 57 with text around it) and page 359 (Eq 86 and text below it).
2045 
2046  // First, transform the volume conservative solution at volume cubature nodes to entropy variables.
2047  std::array<std::vector<adtype>,nstate> entropy_var_vol;
2048  for(unsigned int iquad=0; iquad<n_quad_pts_vol; iquad++){
2049  std::array<adtype,nstate> soln_state;
2050  for(int istate=0; istate<nstate; istate++){
2051  soln_state[istate] = soln_at_vol_q[istate][iquad];
2052  }
2053  std::array<adtype,nstate> entropy_var;
2054  entropy_var = pde_physics.compute_entropy_variables(soln_state);
2055  for(int istate=0; istate<nstate; istate++){
2056  if(iquad==0){
2057  entropy_var_vol[istate].resize(n_quad_pts_vol);
2058  }
2059  entropy_var_vol[istate][iquad] = entropy_var[istate];
2060  }
2061  }
2062 
2063  //project it onto the solution basis functions and interpolate it
2064  std::array<std::vector<adtype>,nstate> projected_entropy_var_vol;
2065  std::array<std::vector<adtype>,nstate> projected_entropy_var_surf;
2066  for(int istate=0; istate<nstate; istate++){
2067  // allocate
2068  projected_entropy_var_vol[istate].resize(n_quad_pts_vol);
2069  projected_entropy_var_surf[istate].resize(n_face_quad_pts);
2070 
2071  //interior
2072  std::vector<adtype> entropy_var_coeff(n_shape_fns);
2073  soln_basis_projection_oper.matrix_vector_mult_1D(entropy_var_vol[istate],
2074  entropy_var_coeff,
2075  soln_basis_projection_oper.oneD_vol_operator);
2076  soln_basis.matrix_vector_mult_1D(entropy_var_coeff,
2077  projected_entropy_var_vol[istate],
2078  soln_basis.oneD_vol_operator);
2079  soln_basis.matrix_vector_mult_surface_1D(face_orientation,
2080  iface,
2081  entropy_var_coeff,
2082  projected_entropy_var_surf[istate],
2083  soln_basis.oneD_surf_operator,
2084  soln_basis.oneD_vol_operator);
2085  }
2086 
2087  //get the surface-volume sparsity pattern for a "sum-factorized" Hadamard product only computing terms needed for the operation.
2088  const unsigned int row_size = n_face_quad_pts * n_quad_pts_1D;
2089  const unsigned int col_size = n_face_quad_pts * n_quad_pts_1D;
2090  std::vector<unsigned int> Hadamard_rows_sparsity(row_size);
2091  std::vector<unsigned int> Hadamard_columns_sparsity(col_size);
2092  if(this->all_parameters->use_split_form || this->all_parameters->use_curvilinear_split_form){
2093  flux_basis.sum_factorized_Hadamard_surface_sparsity_pattern(n_face_quad_pts, n_quad_pts_1D, Hadamard_rows_sparsity, Hadamard_columns_sparsity, dim_not_zero);
2094  }
2095 
2096  std::array<std::vector<adtype>,nstate> surf_vol_ref_2pt_flux_interp_surf;
2097  std::array<std::vector<adtype>,nstate> surf_vol_ref_2pt_flux_interp_vol;
2098  if(this->all_parameters->use_split_form || this->all_parameters->use_curvilinear_split_form){
2099  //get surface-volume hybrid 2pt flux from Eq.(15) in Chan, Jesse. "Skew-symmetric entropy stable modal discontinuous Galerkin formulations." Journal of Scientific Computing 81.1 (2019): 459-485.
2100  std::array<std::vector<adtype>,nstate> surface_ref_2pt_flux;
2101  //make use of the sparsity pattern from above to assemble only n^d non-zero entries without ever allocating not computing zeros.
2102  for(int istate=0; istate<nstate; istate++){
2103  surface_ref_2pt_flux[istate].resize(n_face_quad_pts * n_quad_pts_1D);
2104  }
2105  for(unsigned int iquad_face=0; iquad_face<n_face_quad_pts; iquad_face++){
2106  dealii::Tensor<2,dim,adtype> metric_cofactor_surf;
2107  for(int idim=0; idim<dim; idim++){
2108  for(int jdim=0; jdim<dim; jdim++){
2109  metric_cofactor_surf[idim][jdim] = metric_oper.metric_cofactor_surf[idim][jdim][iquad_face];
2110  }
2111  }
2112 
2113  //Compute the conservative values on the facet from the interpolated entorpy variables.
2114  std::array<adtype,nstate> entropy_var_face;
2115  for(int istate=0; istate<nstate; istate++){
2116  entropy_var_face[istate] = projected_entropy_var_surf[istate][iquad_face];
2117  }
2118  std::array<adtype,nstate> soln_state_face;
2119  soln_state_face= pde_physics.compute_conservative_variables_from_entropy_variables (entropy_var_face);
2120 
2121  //only do the n_quad_1D vol points that give non-zero entries from Hadamard product.
2122  for(unsigned int row_index = iquad_face * n_quad_pts_1D, column_index = 0;
2123  column_index < n_quad_pts_1D;
2124  row_index++, column_index++){
2125 
2126  if(Hadamard_rows_sparsity[row_index] != iquad_face){
2127  pcout<<"The boundary Hadamard rows sparsity pattern does not match."<<std::endl;
2128  std::abort();
2129  }
2130 
2131  const unsigned int iquad_vol = Hadamard_columns_sparsity[row_index];//extract flux_quad pt that corresponds to a non-zero entry for Hadamard product.
2132  // Copy Metric Cofactor in a way can use for transforming Tensor Blocks to reference space
2133  // The way it is stored in metric_operators is to use sum-factorization in each direction,
2134  // but here it is cleaner to apply a reference transformation in each Tensor block returned by physics.
2135  dealii::Tensor<2,dim,adtype> metric_cofactor_vol;
2136  for(int idim=0; idim<dim; idim++){
2137  for(int jdim=0; jdim<dim; jdim++){
2138  metric_cofactor_vol[idim][jdim] = metric_oper.metric_cofactor_vol[idim][jdim][iquad_vol];
2139  }
2140  }
2141  std::array<adtype,nstate> entropy_var;
2142  for(int istate=0; istate<nstate; istate++){
2143  entropy_var[istate] = projected_entropy_var_vol[istate][iquad_vol];
2144  }
2145  std::array<adtype,nstate> soln_state;
2146  soln_state = pde_physics.compute_conservative_variables_from_entropy_variables (entropy_var);
2147  //Note that the flux basis is collocated on the volume cubature set so we don't need to evaluate the entropy variables
2148  //on the volume set then transform back to the conservative variables since the flux basis volume
2149  //projection is identity.
2150 
2151  //Compute the physical flux
2152  std::array<dealii::Tensor<1,dim,adtype>,nstate> conv_phys_flux_2pt;
2153  conv_phys_flux_2pt = pde_physics.convective_numerical_split_flux(soln_state, soln_state_face);
2154  for(int istate=0; istate<nstate; istate++){
2155  dealii::Tensor<1,dim,adtype> conv_ref_flux_2pt;
2156  //For each state, transform the physical flux to a reference flux.
2157  dealii::Tensor<2,dim,adtype> metric_cofactor_split;
2158  for(int idim=0; idim<dim; idim++){
2159  for(int jdim=0; jdim<dim; jdim++){
2160  metric_cofactor_split[idim][jdim] = 0.5 * (metric_cofactor_surf[idim][jdim] + metric_cofactor_vol[idim][jdim]);
2161  }
2162  }
2163  metric_oper.transform_physical_to_reference(
2164  conv_phys_flux_2pt[istate],
2165  metric_cofactor_split,
2166  conv_ref_flux_2pt);
2167  //only store the dim not zero in reference space bc dot product with unit ref normal later.
2168  surface_ref_2pt_flux[istate][iquad_face * n_quad_pts_1D + column_index] = conv_ref_flux_2pt[dim_not_zero];
2169  }
2170  }
2171  }
2172  //get the surface basis operator from Hadamard sparsity pattern
2173  //to be applied at n^d operations (on the face so n^{d+1-1}=n^d flops)
2174  //also only allocates n^d terms.
2175  const int iface_1D = iface % 2;//the reference face number
2176  const std::vector<double> &oneD_quad_weights_vol= this->oneD_quadrature_collection[poly_degree].get_weights();
2177  dealii::FullMatrix<real> surf_oper_sparse(n_face_quad_pts, n_quad_pts_1D);
2178  flux_basis.sum_factorized_Hadamard_surface_basis_assembly(n_face_quad_pts, n_quad_pts_1D,
2179  Hadamard_rows_sparsity, Hadamard_columns_sparsity,
2180  flux_basis.oneD_surf_operator[iface_1D],
2181  oneD_quad_weights_vol,
2182  surf_oper_sparse,
2183  dim_not_zero);
2184 
2185  // Apply the surface Hadamard products and multiply with vector of ones for both off diagonal terms in
2186  // Eq.(15) in Chan, Jesse. "Skew-symmetric entropy stable modal discontinuous Galerkin formulations." Journal of Scientific Computing 81.1 (2019): 459-485.
2187  for(int istate=0; istate<nstate; istate++){
2188  //first apply Hadamard product with the structure made above.
2189  std::vector<adtype> surface_ref_2pt_flux_int_Hadamard_with_surf_oper(n_face_quad_pts * n_quad_pts_1D);
2190  flux_basis.Hadamard_product_AD_vector(surf_oper_sparse,
2191  surface_ref_2pt_flux[istate],
2192  surface_ref_2pt_flux_int_Hadamard_with_surf_oper);
2193  //sum with reference unit normal
2194  surf_vol_ref_2pt_flux_interp_surf[istate].resize(n_face_quad_pts);
2195  surf_vol_ref_2pt_flux_interp_vol[istate].resize(n_quad_pts_vol);
2196 
2197  for(unsigned int iface_quad=0; iface_quad<n_face_quad_pts; iface_quad++){
2198  for(unsigned int iquad_int=0; iquad_int<n_quad_pts_1D; iquad_int++){
2199  surf_vol_ref_2pt_flux_interp_surf[istate][iface_quad]
2200  -= surface_ref_2pt_flux_int_Hadamard_with_surf_oper[iface_quad * n_quad_pts_1D + iquad_int]
2201  * unit_ref_normal_int[dim_not_zero];
2202  const unsigned int column_index = iface_quad * n_quad_pts_1D + iquad_int;
2203  surf_vol_ref_2pt_flux_interp_vol[istate][Hadamard_columns_sparsity[column_index]]
2204  += surface_ref_2pt_flux_int_Hadamard_with_surf_oper[iface_quad * n_quad_pts_1D + iquad_int]
2205  * unit_ref_normal_int[dim_not_zero];
2206  }
2207  }
2208  }
2209  }//end of if split form or curvilinear split form
2210 
2211 
2212  //the outward reference normal dircetion.
2213  std::array<std::vector<adtype>,nstate> conv_flux_dot_normal;
2214  std::array<std::vector<adtype>,nstate> diss_flux_dot_normal_diff;
2215  // Get surface numerical fluxes
2216  for (unsigned int iquad=0; iquad<n_face_quad_pts; ++iquad) {
2217  // Copy Metric Cofactor on the facet in a way can use for transforming Tensor Blocks to reference space
2218  // The way it is stored in metric_operators is to use sum-factorization in each direction,
2219  // but here it is cleaner to apply a reference transformation in each Tensor block returned by physics.
2220  // Note that for a conforming mesh, the facet metric cofactor matrix is the same from either interioir or exterior metric terms.
2221  // This is verified for the metric computations in: unit_tests/operator_tests/surface_conforming_test.cpp
2222  dealii::Tensor<2,dim,adtype> metric_cofactor_surf;
2223  for(int idim=0; idim<dim; idim++){
2224  for(int jdim=0; jdim<dim; jdim++){
2225  metric_cofactor_surf[idim][jdim] = metric_oper.metric_cofactor_surf[idim][jdim][iquad];
2226  }
2227  }
2228  //numerical fluxes
2229  dealii::Tensor<1,dim,adtype> unit_phys_normal_int;
2230  metric_oper.transform_reference_to_physical(unit_ref_normal_int,
2231  metric_cofactor_surf,
2232  unit_phys_normal_int);
2233  adtype face_Jac_norm_scaled = 0.0;
2234  for(int idim=0; idim<dim; idim++){
2235  face_Jac_norm_scaled += unit_phys_normal_int[idim] * unit_phys_normal_int[idim];
2236  }
2237  face_Jac_norm_scaled = sqrt(face_Jac_norm_scaled);
2238  unit_phys_normal_int /= face_Jac_norm_scaled;//normalize it.
2239 
2240  //get the projected entropy variables, soln, and
2241  //auxiliary solution on the surface point.
2242  std::array<adtype,nstate> entropy_var_face;
2243  std::array<dealii::Tensor<1,dim,adtype>,nstate> aux_soln_state;
2244  std::array<adtype,nstate> soln_interp_to_face;
2245  std::array<adtype,nstate> soln_state;
2246  std::array<adtype,nstate> opposite_surf_soln_state;
2247  std::array<adtype,nstate> filtered_soln_state;
2248  std::array<dealii::Tensor<1,dim,adtype>,nstate> filtered_aux_soln_state;
2249  for(int istate=0; istate<nstate; istate++){
2250  soln_interp_to_face[istate] = soln_at_surf_q[istate][iquad];
2251  soln_state[istate] = soln_interp_to_face[istate]; // initialize as solution interpolated to face
2252  entropy_var_face[istate] = projected_entropy_var_surf[istate][iquad];
2253  if(this->using_wall_model && (boundary_id == 1001)) opposite_surf_soln_state[istate] = soln_at_opposite_surf_q[istate][iquad];
2254  if(this->do_compute_filtered_solution) filtered_soln_state[istate] = legendre_soln_at_surf_q[istate][iquad];
2255  for(int idim=0; idim<dim; idim++){
2256  aux_soln_state[istate][idim] = aux_soln_at_surf_q[istate][idim][iquad];
2257  if(this->do_compute_filtered_solution) filtered_aux_soln_state[istate][idim] = legendre_aux_soln_at_surf_q[istate][idim][iquad];
2258  }
2259  }
2260 
2261  //extract solution on surface from projected entropy variables if NSFR; conservative DG uses solution interpolated to face (i.e. the initialization)
2262  if((this->all_parameters->use_split_form || this->all_parameters->use_curvilinear_split_form) && this->use_projected_entropy_variables_for_nsfr_boundary_term) {
2263  soln_state = pde_physics.compute_conservative_variables_from_entropy_variables (entropy_var_face);
2264  }
2265 
2266  std::array<adtype,nstate> soln_boundary;
2267  std::array<dealii::Tensor<1,dim,adtype>,nstate> grad_soln_boundary;
2268  dealii::Point<dim,adtype> surf_flux_node;
2269  for(int idim=0; idim<dim; idim++){
2270  surf_flux_node[idim] = metric_oper.flux_nodes_surf[iface][idim][iquad];
2271  }
2272  //I am not sure if BC should be from solution interpolated to face
2273  //or solution from the projected entropy variables.
2274  //Now, it uses projected entropy variables for NSFR, and solution
2275  //interpolated to face for conservative DG.
2276  pde_physics.boundary_face_values (boundary_id, surf_flux_node, unit_phys_normal_int, soln_state, aux_soln_state, filtered_soln_state, filtered_aux_soln_state, soln_boundary, grad_soln_boundary);
2277 
2278  // Convective numerical flux.
2279  std::array<adtype,nstate> conv_num_flux_dot_n_at_q;
2280  conv_num_flux_dot_n_at_q = conv_num_flux.evaluate_flux(soln_state, soln_boundary, unit_phys_normal_int);
2281 
2282  // Dissipative numerical flux
2283  pde_physics.boundary_face_values_viscous_flux (boundary_id, surf_flux_node, unit_phys_normal_int, soln_state, aux_soln_state, filtered_soln_state, filtered_aux_soln_state, soln_boundary, grad_soln_boundary);
2284  std::array<adtype,nstate> diss_auxi_num_flux_dot_n_at_q;
2285  if(this->using_wall_model && (boundary_id == 1001)) {
2286  diss_auxi_num_flux_dot_n_at_q = pde_physics.dissipative_flux_dot_normal(
2287  opposite_surf_soln_state, aux_soln_state,
2288  filtered_soln_state, filtered_aux_soln_state,
2289  true, // on_boundary == true
2290  current_cell_index,
2291  unit_phys_normal_int,
2292  boundary_id);
2293  } else {
2294  diss_auxi_num_flux_dot_n_at_q = diss_num_flux.evaluate_auxiliary_flux(
2295  current_cell_index, current_cell_index,
2296  0.0, 0.0,
2297  soln_interp_to_face, soln_boundary,
2298  aux_soln_state, grad_soln_boundary,
2299  filtered_soln_state, soln_boundary,
2300  filtered_aux_soln_state, grad_soln_boundary,
2301  unit_phys_normal_int, penalty, true, boundary_id);
2302  }
2303 
2304  for(int istate=0; istate<nstate; istate++){
2305  // allocate
2306  if(iquad==0){
2307  conv_flux_dot_normal[istate].resize(n_face_quad_pts);
2308  diss_flux_dot_normal_diff[istate].resize(n_face_quad_pts);
2309  }
2310  // write data
2311  conv_flux_dot_normal[istate][iquad] = face_Jac_norm_scaled * conv_num_flux_dot_n_at_q[istate];
2312  diss_flux_dot_normal_diff[istate][iquad] = face_Jac_norm_scaled * diss_auxi_num_flux_dot_n_at_q[istate]
2313  - diffusive_int_vol_ref_flux_interp_to_face_dot_ref_normal[istate][iquad];
2314  }
2315  }
2316 
2317  //solve rhs
2318  for(int istate=0; istate<nstate; istate++){
2319  std::vector<adtype> rhs(n_shape_fns);
2320  //Convective flux on the facet
2321  if(this->all_parameters->use_split_form || this->all_parameters->use_curvilinear_split_form){
2322  std::vector<real> ones_surf(n_face_quad_pts, 1.0);
2323  soln_basis.inner_product_surface_1D(face_orientation,
2324  iface,
2325  surf_vol_ref_2pt_flux_interp_surf[istate],
2326  ones_surf, rhs,
2327  soln_basis.oneD_surf_operator,
2328  soln_basis.oneD_vol_operator,
2329  false, -1.0);
2330  std::vector<real> ones_vol(n_quad_pts_vol, 1.0);
2331  soln_basis.inner_product_1D(surf_vol_ref_2pt_flux_interp_vol[istate],
2332  ones_vol, rhs,
2333  soln_basis.oneD_vol_operator,
2334  true, -1.0);
2335  }
2336  else{
2337  soln_basis.inner_product_surface_1D(face_orientation,
2338  iface,
2339  conv_int_vol_ref_flux_interp_to_face_dot_ref_normal[istate],
2340  face_quad_weights, rhs,
2341  soln_basis.oneD_surf_operator,
2342  soln_basis.oneD_vol_operator,
2343  false, 1.0);//adding=false, scaled by factor=-1.0 bc subtract it
2344  }
2345  //Convective surface nnumerical flux.
2346  soln_basis.inner_product_surface_1D(face_orientation,
2347  iface,
2348  conv_flux_dot_normal[istate],
2349  face_quad_weights, rhs,
2350  soln_basis.oneD_surf_operator,
2351  soln_basis.oneD_vol_operator,
2352  true, -1.0);//adding=true, scaled by factor=-1.0 bc subtract it
2353  //Dissipative surface numerical flux.
2354  soln_basis.inner_product_surface_1D(face_orientation,
2355  iface,
2356  diss_flux_dot_normal_diff[istate],
2357  face_quad_weights, rhs,
2358  soln_basis.oneD_surf_operator,
2359  soln_basis.oneD_vol_operator,
2360  true, -1.0);//adding=true, scaled by factor=-1.0 bc subtract it
2361 
2362  for(unsigned int ishape=0; ishape<n_shape_fns; ishape++){
2363  local_rhs_cell[istate*n_shape_fns + ishape] += rhs[ishape];
2364  }
2365  }
2366 }
2367 
2368 
2369 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
2370 template <typename adtype>
2372  const unsigned int iface,
2373  const unsigned int neighbor_iface,
2374  const dealii::types::global_dof_index current_cell_index,
2375  const dealii::types::global_dof_index neighbor_cell_index,
2376  std::vector<bool> face_orientation_int,
2377  std::vector<bool> face_orientation_ext,
2378  const std::array<std::vector<adtype>,nstate> &soln_coeff_int,
2379  const std::array<std::vector<adtype>,nstate> &soln_coeff_ext,
2380  const std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> &aux_soln_coeff_int,
2381  const std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> &aux_soln_coeff_ext,
2382  const unsigned int poly_degree_int,
2383  const unsigned int poly_degree_ext,
2384  const real penalty,
2385  OPERATOR::basis_functions<dim,2*dim> &soln_basis_int,
2386  OPERATOR::basis_functions<dim,2*dim> &soln_basis_ext,
2387  OPERATOR::basis_functions<dim,2*dim> &flux_basis_int,
2388  OPERATOR::basis_functions<dim,2*dim> &flux_basis_ext,
2389  OPERATOR::vol_projection_operator<dim,2*dim> &soln_basis_projection_oper_int,
2390  OPERATOR::vol_projection_operator<dim,2*dim> &soln_basis_projection_oper_ext,
2396  std::vector<adtype> &local_rhs_int_cell,
2397  std::vector<adtype> &local_rhs_ext_cell)
2398 {
2399 
2400  const unsigned int n_face_quad_pts = this->face_quadrature_collection[poly_degree_int].size();//assume interior cell does the work
2401 
2402  const unsigned int n_quad_pts_vol_int = this->volume_quadrature_collection[poly_degree_int].size();
2403  const unsigned int n_quad_pts_vol_ext = this->volume_quadrature_collection[poly_degree_ext].size();
2404  const unsigned int n_quad_pts_1D_int = this->oneD_quadrature_collection[poly_degree_int].size();
2405  const unsigned int n_quad_pts_1D_ext = this->oneD_quadrature_collection[poly_degree_ext].size();
2406 
2407  const unsigned int n_dofs_int = this->fe_collection[poly_degree_int].dofs_per_cell;
2408  const unsigned int n_dofs_ext = this->fe_collection[poly_degree_ext].dofs_per_cell;
2409 
2410  const unsigned int n_shape_fns_int = n_dofs_int / nstate;
2411  const unsigned int n_shape_fns_ext = n_dofs_ext / nstate;
2412 
2413  // Interpolate the modal coefficients to the volume cubature nodes.
2414  std::array<std::vector<adtype>,nstate> soln_at_vol_q_int;
2415  std::array<std::vector<adtype>,nstate> soln_at_vol_q_ext;
2416  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> aux_soln_at_vol_q_int;
2417  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> aux_soln_at_vol_q_ext;
2418  // Interpolate modal soln coefficients to the facet.
2419  std::array<std::vector<adtype>,nstate> soln_at_surf_q_int;
2420  std::array<std::vector<adtype>,nstate> soln_at_surf_q_ext;
2421  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> aux_soln_at_surf_q_int;
2422  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> aux_soln_at_surf_q_ext;
2423  for(int istate=0; istate<nstate; ++istate){
2424  // allocate
2425  soln_at_vol_q_int[istate].resize(n_quad_pts_vol_int);
2426  soln_at_vol_q_ext[istate].resize(n_quad_pts_vol_ext);
2427  // solve soln at volume cubature nodes
2428  soln_basis_int.matrix_vector_mult_1D(soln_coeff_int[istate], soln_at_vol_q_int[istate],
2429  soln_basis_int.oneD_vol_operator);
2430  soln_basis_ext.matrix_vector_mult_1D(soln_coeff_ext[istate], soln_at_vol_q_ext[istate],
2431  soln_basis_ext.oneD_vol_operator);
2432 
2433  // allocate
2434  soln_at_surf_q_int[istate].resize(n_face_quad_pts);
2435  soln_at_surf_q_ext[istate].resize(n_face_quad_pts);
2436  // solve soln at facet cubature nodes
2437  soln_basis_int.matrix_vector_mult_surface_1D(face_orientation_int,
2438  iface,
2439  soln_coeff_int[istate], soln_at_surf_q_int[istate],
2440  soln_basis_int.oneD_surf_operator,
2441  soln_basis_int.oneD_vol_operator);
2442  soln_basis_ext.matrix_vector_mult_surface_1D(face_orientation_ext,
2443  neighbor_iface,
2444  soln_coeff_ext[istate], soln_at_surf_q_ext[istate],
2445  soln_basis_ext.oneD_surf_operator,
2446  soln_basis_ext.oneD_vol_operator);
2447 
2448  for(int idim=0; idim<dim; idim++){
2449  // alocate
2450  aux_soln_at_vol_q_int[istate][idim].resize(n_quad_pts_vol_int);
2451  aux_soln_at_vol_q_ext[istate][idim].resize(n_quad_pts_vol_ext);
2452  // solve auxiliary soln at volume cubature nodes
2453  soln_basis_int.matrix_vector_mult_1D(aux_soln_coeff_int[istate][idim], aux_soln_at_vol_q_int[istate][idim],
2454  soln_basis_int.oneD_vol_operator);
2455  soln_basis_ext.matrix_vector_mult_1D(aux_soln_coeff_ext[istate][idim], aux_soln_at_vol_q_ext[istate][idim],
2456  soln_basis_ext.oneD_vol_operator);
2457 
2458  // allocate
2459  aux_soln_at_surf_q_int[istate][idim].resize(n_face_quad_pts);
2460  aux_soln_at_surf_q_ext[istate][idim].resize(n_face_quad_pts);
2461  // solve auxiliary soln at facet cubature nodes
2462  soln_basis_int.matrix_vector_mult_surface_1D(face_orientation_int,
2463  iface,
2464  aux_soln_coeff_int[istate][idim], aux_soln_at_surf_q_int[istate][idim],
2465  soln_basis_int.oneD_surf_operator,
2466  soln_basis_int.oneD_vol_operator);
2467  soln_basis_ext.matrix_vector_mult_surface_1D(face_orientation_ext,
2468  neighbor_iface,
2469  aux_soln_coeff_ext[istate][idim], aux_soln_at_surf_q_ext[istate][idim],
2470  soln_basis_ext.oneD_surf_operator,
2471  soln_basis_ext.oneD_vol_operator);
2472  }
2473  }
2474 
2475  // -- Solution at legendre poly
2476  // -- (a) Interpolate the modal coefficients to the volume cubature nodes.
2477  std::array<std::vector<adtype>,nstate> legendre_soln_at_vol_q_int;
2478  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> legendre_aux_soln_at_vol_q_int; // legendre auxiliary sol at flux nodes
2479  std::array<std::vector<adtype>,nstate> legendre_soln_at_vol_q_ext;
2480  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> legendre_aux_soln_at_vol_q_ext; // legendre auxiliary sol at flux nodes
2481  // -- (b) Interpolate modal soln coefficients to the facet.
2482  std::array<std::vector<adtype>,nstate> legendre_soln_at_surf_q_int;
2483  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> legendre_aux_soln_at_surf_q_int; // legendre auxiliary sol at flux nodes
2484  std::array<std::vector<adtype>,nstate> legendre_soln_at_surf_q_ext;
2485  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> legendre_aux_soln_at_surf_q_ext; // legendre auxiliary sol at flux nodes
2486  if(this->do_compute_filtered_solution) {
2487  // NOTE: This only pertains to advanced SGS models for LES
2488  //==================================================
2489  // GET THE PRIMITIVE SOLUTION
2490  //==================================================
2491  std::array<std::vector<adtype>,nstate> primitive_soln_at_vol_q_int;
2492  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> primitive_aux_soln_at_vol_q_int;
2493  std::array<std::vector<adtype>,nstate> primitive_soln_at_surf_q_int;
2494  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> primitive_aux_soln_at_surf_q_int;
2495  std::array<std::vector<adtype>,nstate> primitive_soln_at_vol_q_ext;
2496  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> primitive_aux_soln_at_vol_q_ext;
2497  std::array<std::vector<adtype>,nstate> primitive_soln_at_surf_q_ext;
2498  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> primitive_aux_soln_at_surf_q_ext;
2499  // Resize the primitive soln arrays
2500  for(int istate=0; istate<nstate; istate++){
2501  primitive_soln_at_vol_q_int[istate].resize(n_quad_pts_vol_int);
2502  primitive_soln_at_surf_q_int[istate].resize(n_face_quad_pts);
2503  primitive_soln_at_vol_q_ext[istate].resize(n_quad_pts_vol_ext);
2504  primitive_soln_at_surf_q_ext[istate].resize(n_face_quad_pts);
2505  for(int idim=0; idim<dim; idim++){
2506  primitive_aux_soln_at_vol_q_int[istate][idim].resize(n_quad_pts_vol_int);
2507  primitive_aux_soln_at_surf_q_int[istate][idim].resize(n_face_quad_pts);
2508  primitive_aux_soln_at_vol_q_ext[istate][idim].resize(n_quad_pts_vol_ext);
2509  primitive_aux_soln_at_surf_q_ext[istate][idim].resize(n_face_quad_pts);
2510  }
2511  }
2512  // Compute the primitive soln at all iquad and fill arrays
2513  // -- volume int
2514  for (unsigned int iquad=0; iquad<n_quad_pts_vol_int; ++iquad) {
2515  // extract conservative soln state
2516  std::array<adtype,nstate> soln_state;
2517  std::array<dealii::Tensor<1,dim,adtype>,nstate> aux_soln_state;
2518  for(int istate=0; istate<nstate; istate++){
2519  soln_state[istate] = soln_at_vol_q_int[istate][iquad];
2520  for(int idim=0; idim<dim; idim++){
2521  aux_soln_state[istate][idim] = aux_soln_at_vol_q_int[istate][idim][iquad];
2522  }
2523  }
2524  // compute primitive soln state from conservative
2525  std::array<adtype,nstate> primitive_soln_state = pde_physics.convert_conservative_to_primitive(soln_state);
2526  std::array<dealii::Tensor<1,dim,adtype>,nstate> primitive_aux_soln_state = pde_physics.convert_conservative_gradient_to_primitive_gradient(soln_state,aux_soln_state);
2527  // store primitive soln at quadrature point
2528  for(int istate=0; istate<nstate; istate++){
2529  primitive_soln_at_vol_q_int[istate][iquad] = primitive_soln_state[istate];
2530  for(int idim=0; idim<dim; idim++){
2531  primitive_aux_soln_at_vol_q_int[istate][idim][iquad] = primitive_aux_soln_state[istate][idim];
2532  }
2533  }
2534  }
2535  // -- volume ext
2536  for (unsigned int iquad=0; iquad<n_quad_pts_vol_ext; ++iquad) {
2537  // extract conservative soln state
2538  std::array<adtype,nstate> soln_state;
2539  std::array<dealii::Tensor<1,dim,adtype>,nstate> aux_soln_state;
2540  for(int istate=0; istate<nstate; istate++){
2541  soln_state[istate] = soln_at_vol_q_ext[istate][iquad];
2542  for(int idim=0; idim<dim; idim++){
2543  aux_soln_state[istate][idim] = aux_soln_at_vol_q_ext[istate][idim][iquad];
2544  }
2545  }
2546  // compute primitive soln state from conservative
2547  std::array<adtype,nstate> primitive_soln_state = pde_physics.convert_conservative_to_primitive(soln_state);
2548  std::array<dealii::Tensor<1,dim,adtype>,nstate> primitive_aux_soln_state = pde_physics.convert_conservative_gradient_to_primitive_gradient(soln_state,aux_soln_state);
2549  // store primitive soln at quadrature point
2550  for(int istate=0; istate<nstate; istate++){
2551  primitive_soln_at_vol_q_ext[istate][iquad] = primitive_soln_state[istate];
2552  for(int idim=0; idim<dim; idim++){
2553  primitive_aux_soln_at_vol_q_ext[istate][idim][iquad] = primitive_aux_soln_state[istate][idim];
2554  }
2555  }
2556  }
2557  // -- surface int
2558  for(unsigned int iquad_face=0; iquad_face<n_face_quad_pts; iquad_face++){
2559  // extract conservative soln state
2560  std::array<adtype,nstate> soln_state;
2561  std::array<dealii::Tensor<1,dim,adtype>,nstate> aux_soln_state;
2562  for(int istate=0; istate<nstate; istate++){
2563  soln_state[istate] = soln_at_surf_q_int[istate][iquad_face];
2564  for(int idim=0; idim<dim; idim++){
2565  aux_soln_state[istate][idim] = aux_soln_at_surf_q_int[istate][idim][iquad_face];
2566  }
2567  }
2568  // compute primitive soln state from conservative
2569  std::array<adtype,nstate> primitive_soln_state = pde_physics.convert_conservative_to_primitive(soln_state);
2570  std::array<dealii::Tensor<1,dim,adtype>,nstate> primitive_aux_soln_state = pde_physics.convert_conservative_gradient_to_primitive_gradient(soln_state,aux_soln_state);
2571  // store primitive soln at quadrature point
2572  for(int istate=0; istate<nstate; istate++){
2573  primitive_soln_at_surf_q_int[istate][iquad_face] = primitive_soln_state[istate];
2574  for(int idim=0; idim<dim; idim++){
2575  primitive_aux_soln_at_surf_q_int[istate][idim][iquad_face] = primitive_aux_soln_state[istate][idim];
2576  }
2577  }
2578  }
2579  // -- surface ext
2580  for(unsigned int iquad_face=0; iquad_face<n_face_quad_pts; iquad_face++){
2581  // extract conservative soln state
2582  std::array<adtype,nstate> soln_state;
2583  std::array<dealii::Tensor<1,dim,adtype>,nstate> aux_soln_state;
2584  for(int istate=0; istate<nstate; istate++){
2585  soln_state[istate] = soln_at_surf_q_ext[istate][iquad_face];
2586  for(int idim=0; idim<dim; idim++){
2587  aux_soln_state[istate][idim] = aux_soln_at_surf_q_ext[istate][idim][iquad_face];
2588  }
2589  }
2590  // compute primitive soln state from conservative
2591  std::array<adtype,nstate> primitive_soln_state = pde_physics.convert_conservative_to_primitive(soln_state);
2592  std::array<dealii::Tensor<1,dim,adtype>,nstate> primitive_aux_soln_state = pde_physics.convert_conservative_gradient_to_primitive_gradient(soln_state,aux_soln_state);
2593  // store primitive soln at quadrature point
2594  for(int istate=0; istate<nstate; istate++){
2595  primitive_soln_at_surf_q_ext[istate][iquad_face] = primitive_soln_state[istate];
2596  for(int idim=0; idim<dim; idim++){
2597  primitive_aux_soln_at_surf_q_ext[istate][idim][iquad_face] = primitive_aux_soln_state[istate][idim];
2598  }
2599  }
2600  }
2601 
2602  //==================================================
2603  // PROJECT TO LEGENDRE BASIS AND MODALLY FILTER
2604  //==================================================
2605  // -- Primitive solution at legendre poly
2606  std::array<std::vector<adtype>,nstate> primitive_legendre_soln_at_vol_q_int;
2607  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> primitive_legendre_aux_soln_at_vol_q_int;
2608  std::array<std::vector<adtype>,nstate> primitive_legendre_soln_at_surf_q_int;
2609  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> primitive_legendre_aux_soln_at_surf_q_int;
2610  std::array<std::vector<adtype>,nstate> primitive_legendre_soln_at_vol_q_ext;
2611  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> primitive_legendre_aux_soln_at_vol_q_ext;
2612  std::array<std::vector<adtype>,nstate> primitive_legendre_soln_at_surf_q_ext;
2613  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> primitive_legendre_aux_soln_at_surf_q_ext;
2614 
2615  // Details: this projects to Legendre basis, truncates, then interpolates back to quad nodes.
2616  // -- Constructor for tensor product polynomials based on Polynomials::Legendre interpolation.
2617  dealii::FE_DGQLegendre<1,1> legendre_poly_1D_int(poly_degree_int);
2618  dealii::FE_DGQLegendre<1,1> legendre_poly_1D_ext(poly_degree_ext);
2619  // -- Projection operator for legendre basis
2620  OPERATOR::vol_projection_operator<dim,2*dim> legendre_soln_basis_projection_oper_int(1, poly_degree_int, this->max_grid_degree);
2621  legendre_soln_basis_projection_oper_int.build_1D_volume_operator(legendre_poly_1D_int, this->oneD_quadrature_collection[poly_degree_int]);
2622  OPERATOR::vol_projection_operator<dim,2*dim> legendre_soln_basis_projection_oper_ext(1, poly_degree_ext, this->max_grid_degree);
2623  legendre_soln_basis_projection_oper_ext.build_1D_volume_operator(legendre_poly_1D_ext, this->oneD_quadrature_collection[poly_degree_ext]);
2624  // -- Legendre basis functions
2625  OPERATOR::basis_functions<dim,2*dim> legendre_soln_basis_int(1, poly_degree_int, this->max_grid_degree);
2626  legendre_soln_basis_int.build_1D_volume_operator(legendre_poly_1D_int, this->oneD_quadrature_collection[poly_degree_int]);
2627  legendre_soln_basis_int.build_1D_surface_operator(legendre_poly_1D_int, this->oneD_face_quadrature);
2628  OPERATOR::basis_functions<dim,2*dim> legendre_soln_basis_ext(1, poly_degree_ext, this->max_grid_degree);
2629  legendre_soln_basis_ext.build_1D_volume_operator(legendre_poly_1D_ext, this->oneD_quadrature_collection[poly_degree_ext]);
2630  legendre_soln_basis_ext.build_1D_surface_operator(legendre_poly_1D_ext, this->oneD_face_quadrature);
2631  const unsigned int p_min_filtered = this->poly_degree_max_large_scales + 1;
2632  for(int istate=0; istate<nstate; istate++){
2633  //==================================================
2634  // Solution
2635  //==================================================
2636  // -- (1) Project to Legendre basis
2637  std::vector<adtype> legendre_soln_coeff_int(n_shape_fns_int);
2638  legendre_soln_basis_projection_oper_int.matrix_vector_mult_1D(primitive_soln_at_vol_q_int[istate], legendre_soln_coeff_int,
2639  legendre_soln_basis_projection_oper_int.oneD_vol_operator);
2640  std::vector<adtype> legendre_soln_coeff_ext(n_shape_fns_ext);
2641  legendre_soln_basis_projection_oper_ext.matrix_vector_mult_1D(primitive_soln_at_vol_q_ext[istate], legendre_soln_coeff_ext,
2642  legendre_soln_basis_projection_oper_ext.oneD_vol_operator);
2643  // -- (2) Truncate modes for high-pass filter (i.e. DG-VMS like)
2644  if(this->apply_modal_high_pass_filter_on_filtered_solution && (istate!=0 && istate!=(nstate-1))) {
2645  for(unsigned int ishape=0; ishape<n_shape_fns_int; ishape++){
2646  if(ishape < p_min_filtered){
2647  legendre_soln_coeff_int[ishape] = 0.0;
2648  }
2649  }
2650  for(unsigned int ishape=0; ishape<n_shape_fns_ext; ishape++){
2651  if(ishape < p_min_filtered){
2652  legendre_soln_coeff_ext[ishape] = 0.0;
2653  }
2654  }
2655  }
2656  // -- (3) Interpolate filtered solution back to quadrature points
2657  primitive_legendre_soln_at_vol_q_int[istate].resize(n_quad_pts_vol_int);
2658  legendre_soln_basis_int.matrix_vector_mult_1D(legendre_soln_coeff_int, primitive_legendre_soln_at_vol_q_int[istate],
2659  legendre_soln_basis_int.oneD_vol_operator);
2660  primitive_legendre_soln_at_surf_q_int[istate].resize(n_face_quad_pts);
2661  legendre_soln_basis_int.matrix_vector_mult_surface_1D(face_orientation_int, iface,
2662  legendre_soln_coeff_int, primitive_legendre_soln_at_surf_q_int[istate],
2663  legendre_soln_basis_int.oneD_surf_operator,
2664  legendre_soln_basis_int.oneD_vol_operator);
2665  primitive_legendre_soln_at_vol_q_ext[istate].resize(n_quad_pts_vol_ext);
2666  legendre_soln_basis_ext.matrix_vector_mult_1D(legendre_soln_coeff_ext, primitive_legendre_soln_at_vol_q_ext[istate],
2667  legendre_soln_basis_ext.oneD_vol_operator);
2668  primitive_legendre_soln_at_surf_q_ext[istate].resize(n_face_quad_pts);
2669  legendre_soln_basis_ext.matrix_vector_mult_surface_1D(face_orientation_ext, neighbor_iface,
2670  legendre_soln_coeff_ext, primitive_legendre_soln_at_surf_q_ext[istate],
2671  legendre_soln_basis_ext.oneD_surf_operator,
2672  legendre_soln_basis_ext.oneD_vol_operator);
2673  //==================================================
2674 
2675  //==================================================
2676  // Auxiliary Solution (gradients)
2677  //==================================================
2678  dealii::Tensor<1,dim,std::vector<adtype>> legendre_aux_soln_coeff_int;
2679  dealii::Tensor<1,dim,std::vector<adtype>> legendre_aux_soln_coeff_ext;
2680  for(int idim=0; idim<dim; idim++){
2681  // -- (1) Project to Legendre basis
2682  legendre_aux_soln_coeff_int[idim].resize(n_shape_fns_int);
2683  legendre_aux_soln_coeff_ext[idim].resize(n_shape_fns_ext);
2684  if(this->use_auxiliary_eq){
2685  legendre_soln_basis_projection_oper_int.matrix_vector_mult_1D(primitive_aux_soln_at_vol_q_int[istate][idim], legendre_aux_soln_coeff_int[idim],
2686  legendre_soln_basis_projection_oper_int.oneD_vol_operator);
2687  legendre_soln_basis_projection_oper_ext.matrix_vector_mult_1D(primitive_aux_soln_at_vol_q_ext[istate][idim], legendre_aux_soln_coeff_ext[idim],
2688  legendre_soln_basis_projection_oper_ext.oneD_vol_operator);
2689  // -- (2) Truncate modes for high-pass filter (i.e. DG-VMS like)
2690  if(this->apply_modal_high_pass_filter_on_filtered_solution && (istate!=0 && istate!=(nstate-1))) {
2691  for(unsigned int ishape=0; ishape<n_shape_fns_int; ishape++){
2692  if(ishape < p_min_filtered){
2693  legendre_aux_soln_coeff_int[idim][ishape] = 0.0;
2694  }
2695  }
2696  for(unsigned int ishape=0; ishape<n_shape_fns_ext; ishape++){
2697  if(ishape < p_min_filtered){
2698  legendre_aux_soln_coeff_ext[idim][ishape] = 0.0;
2699  }
2700  }
2701  }
2702  }
2703  else {
2704  for(unsigned int ishape=0; ishape<n_shape_fns_int; ishape++){
2705  legendre_aux_soln_coeff_int[idim][ishape] = 0.0;
2706  }
2707  for(unsigned int ishape=0; ishape<n_shape_fns_ext; ishape++){
2708  legendre_aux_soln_coeff_ext[idim][ishape] = 0.0;
2709  }
2710  }
2711  // -- (3) Interpolate filtered solution back to quadrature points
2712  primitive_legendre_aux_soln_at_vol_q_int[istate][idim].resize(n_quad_pts_vol_int);
2713  legendre_soln_basis_int.matrix_vector_mult_1D(legendre_aux_soln_coeff_int[idim], primitive_legendre_aux_soln_at_vol_q_int[istate][idim],
2714  legendre_soln_basis_int.oneD_vol_operator);
2715  primitive_legendre_aux_soln_at_surf_q_int[istate][idim].resize(n_face_quad_pts);
2716  legendre_soln_basis_int.matrix_vector_mult_surface_1D(face_orientation_int, iface,
2717  legendre_aux_soln_coeff_int[idim], primitive_legendre_aux_soln_at_surf_q_int[istate][idim],
2718  legendre_soln_basis_int.oneD_surf_operator,
2719  legendre_soln_basis_int.oneD_vol_operator);
2720  primitive_legendre_aux_soln_at_vol_q_ext[istate][idim].resize(n_quad_pts_vol_ext);
2721  legendre_soln_basis_ext.matrix_vector_mult_1D(legendre_aux_soln_coeff_ext[idim], primitive_legendre_aux_soln_at_vol_q_ext[istate][idim],
2722  legendre_soln_basis_ext.oneD_vol_operator);
2723  primitive_legendre_aux_soln_at_surf_q_ext[istate][idim].resize(n_face_quad_pts);
2724  legendre_soln_basis_ext.matrix_vector_mult_surface_1D(face_orientation_ext, neighbor_iface,
2725  legendre_aux_soln_coeff_ext[idim], primitive_legendre_aux_soln_at_surf_q_ext[istate][idim],
2726  legendre_soln_basis_ext.oneD_surf_operator,
2727  legendre_soln_basis_ext.oneD_vol_operator);
2728  }
2729  //==================================================
2730  }
2731  //=======================================================
2732  // CONVERT PRIMITIVE LEGENDRE SOLUTION TO CONSERVATIVE
2733  //=======================================================
2734  // Resize the conservative soln arrays
2735  for(int istate=0; istate<nstate; istate++){
2736  legendre_soln_at_vol_q_int[istate].resize(n_quad_pts_vol_int);
2737  legendre_soln_at_surf_q_int[istate].resize(n_face_quad_pts);
2738  legendre_soln_at_vol_q_ext[istate].resize(n_quad_pts_vol_ext);
2739  legendre_soln_at_surf_q_ext[istate].resize(n_face_quad_pts);
2740  for(int idim=0; idim<dim; idim++){
2741  legendre_aux_soln_at_vol_q_int[istate][idim].resize(n_quad_pts_vol_int);
2742  legendre_aux_soln_at_surf_q_int[istate][idim].resize(n_face_quad_pts);
2743  legendre_aux_soln_at_vol_q_ext[istate][idim].resize(n_quad_pts_vol_ext);
2744  legendre_aux_soln_at_surf_q_ext[istate][idim].resize(n_face_quad_pts);
2745  }
2746  }
2747  // Compute the primitive soln at all iquad and fill arrays
2748  // -- volume int
2749  for (unsigned int iquad=0; iquad<n_quad_pts_vol_int; ++iquad) {
2750  // extract conservative soln state
2751  std::array<adtype,nstate> primitive_legendre_soln_state;
2752  std::array<dealii::Tensor<1,dim,adtype>,nstate> primitive_legendre_aux_soln_state;
2753  for(int istate=0; istate<nstate; istate++){
2754  primitive_legendre_soln_state[istate] = primitive_legendre_soln_at_vol_q_int[istate][iquad];
2755  for(int idim=0; idim<dim; idim++){
2756  primitive_legendre_aux_soln_state[istate][idim] = primitive_legendre_aux_soln_at_vol_q_int[istate][idim][iquad];
2757  }
2758  }
2759  // compute conservative soln state from primitive
2760  std::array<adtype,nstate> legendre_soln_state = pde_physics.convert_primitive_to_conservative(primitive_legendre_soln_state);
2761  std::array<dealii::Tensor<1,dim,adtype>,nstate> legendre_aux_soln_state = pde_physics.convert_primitive_gradient_to_conservative_gradient(primitive_legendre_soln_state,primitive_legendre_aux_soln_state);
2762  // store conservative soln at quadrature point
2763  for(int istate=0; istate<nstate; istate++){
2764  legendre_soln_at_vol_q_int[istate][iquad] = legendre_soln_state[istate];
2765  for(int idim=0; idim<dim; idim++){
2766  legendre_aux_soln_at_vol_q_int[istate][idim][iquad] = legendre_aux_soln_state[istate][idim];
2767  }
2768  }
2769  }
2770  // -- volume ext
2771  for (unsigned int iquad=0; iquad<n_quad_pts_vol_ext; ++iquad) {
2772  // extract conservative soln state
2773  std::array<adtype,nstate> primitive_legendre_soln_state;
2774  std::array<dealii::Tensor<1,dim,adtype>,nstate> primitive_legendre_aux_soln_state;
2775  for(int istate=0; istate<nstate; istate++){
2776  primitive_legendre_soln_state[istate] = primitive_legendre_soln_at_vol_q_ext[istate][iquad];
2777  for(int idim=0; idim<dim; idim++){
2778  primitive_legendre_aux_soln_state[istate][idim] = primitive_legendre_aux_soln_at_vol_q_ext[istate][idim][iquad];
2779  }
2780  }
2781  // compute conservative soln state from primitive
2782  std::array<adtype,nstate> legendre_soln_state = pde_physics.convert_primitive_to_conservative(primitive_legendre_soln_state);
2783  std::array<dealii::Tensor<1,dim,adtype>,nstate> legendre_aux_soln_state = pde_physics.convert_primitive_gradient_to_conservative_gradient(primitive_legendre_soln_state,primitive_legendre_aux_soln_state);
2784  // store conservative soln at quadrature point
2785  for(int istate=0; istate<nstate; istate++){
2786  legendre_soln_at_vol_q_ext[istate][iquad] = legendre_soln_state[istate];
2787  for(int idim=0; idim<dim; idim++){
2788  legendre_aux_soln_at_vol_q_ext[istate][idim][iquad] = legendre_aux_soln_state[istate][idim];
2789  }
2790  }
2791  }
2792  // -- surface int
2793  for (unsigned int iquad_face=0; iquad_face<n_face_quad_pts; iquad_face++) {
2794  // extract conservative soln state
2795  std::array<adtype,nstate> primitive_legendre_soln_state;
2796  std::array<dealii::Tensor<1,dim,adtype>,nstate> primitive_legendre_aux_soln_state;
2797  for(int istate=0; istate<nstate; istate++){
2798  primitive_legendre_soln_state[istate] = primitive_legendre_soln_at_surf_q_int[istate][iquad_face];
2799  for(int idim=0; idim<dim; idim++){
2800  primitive_legendre_aux_soln_state[istate][idim] = primitive_legendre_aux_soln_at_surf_q_int[istate][idim][iquad_face];
2801  }
2802  }
2803  // compute conservative soln state from primitive
2804  std::array<adtype,nstate> legendre_soln_state = pde_physics.convert_primitive_to_conservative(primitive_legendre_soln_state);
2805  std::array<dealii::Tensor<1,dim,adtype>,nstate> legendre_aux_soln_state = pde_physics.convert_primitive_gradient_to_conservative_gradient(primitive_legendre_soln_state,primitive_legendre_aux_soln_state);
2806  // store conservative soln at quadrature point
2807  for(int istate=0; istate<nstate; istate++){
2808  legendre_soln_at_surf_q_int[istate][iquad_face] = legendre_soln_state[istate];
2809  for(int idim=0; idim<dim; idim++){
2810  legendre_aux_soln_at_surf_q_int[istate][idim][iquad_face] = legendre_aux_soln_state[istate][idim];
2811  }
2812  }
2813  }
2814  // -- surface ext
2815  for (unsigned int iquad_face=0; iquad_face<n_face_quad_pts; iquad_face++) {
2816  // extract conservative soln state
2817  std::array<adtype,nstate> primitive_legendre_soln_state;
2818  std::array<dealii::Tensor<1,dim,adtype>,nstate> primitive_legendre_aux_soln_state;
2819  for(int istate=0; istate<nstate; istate++){
2820  primitive_legendre_soln_state[istate] = primitive_legendre_soln_at_surf_q_ext[istate][iquad_face];
2821  for(int idim=0; idim<dim; idim++){
2822  primitive_legendre_aux_soln_state[istate][idim] = primitive_legendre_aux_soln_at_surf_q_ext[istate][idim][iquad_face];
2823  }
2824  }
2825  // compute conservative soln state from primitive
2826  std::array<adtype,nstate> legendre_soln_state = pde_physics.convert_primitive_to_conservative(primitive_legendre_soln_state);
2827  std::array<dealii::Tensor<1,dim,adtype>,nstate> legendre_aux_soln_state = pde_physics.convert_primitive_gradient_to_conservative_gradient(primitive_legendre_soln_state,primitive_legendre_aux_soln_state);
2828  // store conservative soln at quadrature point
2829  for(int istate=0; istate<nstate; istate++){
2830  legendre_soln_at_surf_q_ext[istate][iquad_face] = legendre_soln_state[istate];
2831  for(int idim=0; idim<dim; idim++){
2832  legendre_aux_soln_at_surf_q_ext[istate][idim][iquad_face] = legendre_aux_soln_state[istate][idim];
2833  }
2834  }
2835  }
2836  }
2837 
2838 
2839  // Get volume reference fluxes and interpolate them to the facet.
2840  // Compute reference volume fluxes in both interior and exterior cells.
2841 
2842  // First we do interior.
2843  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> conv_ref_flux_at_vol_q_int;
2844  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> diffusive_ref_flux_at_vol_q_int;
2845  for (unsigned int iquad=0; iquad<n_quad_pts_vol_int; ++iquad) {
2846  // Copy Metric Cofactor in a way can use for transforming Tensor Blocks to reference space
2847  // The way it is stored in metric_operators is to use sum-factorization in each direction,
2848  // but here it is cleaner to apply a reference transformation in each Tensor block returned by physics.
2849  dealii::Tensor<2,dim,adtype> metric_cofactor_vol_int;
2850  for(int idim=0; idim<dim; idim++){
2851  for(int jdim=0; jdim<dim; jdim++){
2852  metric_cofactor_vol_int[idim][jdim] = metric_oper_int.metric_cofactor_vol[idim][jdim][iquad];
2853  }
2854  }
2855  std::array<adtype,nstate> soln_state;
2856  std::array<dealii::Tensor<1,dim,adtype>,nstate> aux_soln_state;
2857  std::array<adtype,nstate> filtered_soln_state;
2858  std::array<dealii::Tensor<1,dim,adtype>,nstate> filtered_aux_soln_state;
2859  for(int istate=0; istate<nstate; istate++){
2860  soln_state[istate] = soln_at_vol_q_int[istate][iquad];
2861  if(this->do_compute_filtered_solution) filtered_soln_state[istate] = legendre_soln_at_vol_q_int[istate][iquad];
2862  for(int idim=0; idim<dim; idim++){
2863  aux_soln_state[istate][idim] = aux_soln_at_vol_q_int[istate][idim][iquad];
2864  if(this->do_compute_filtered_solution) filtered_aux_soln_state[istate][idim] = legendre_aux_soln_at_vol_q_int[istate][idim][iquad];
2865  }
2866  }
2867 
2868  // Evaluate physical convective flux
2869  std::array<dealii::Tensor<1,dim,adtype>,nstate> conv_phys_flux;
2870  //Only for conservtive DG do we interpolate volume fluxes to the facet
2871  if(!this->all_parameters->use_split_form && !this->all_parameters->use_curvilinear_split_form){
2872  conv_phys_flux = pde_physics.convective_flux (soln_state);
2873  }
2874 
2875  // Compute the physical dissipative flux
2876  std::array<dealii::Tensor<1,dim,adtype>,nstate> diffusive_phys_flux;
2877  diffusive_phys_flux = pde_physics.dissipative_flux(soln_state, aux_soln_state, filtered_soln_state, filtered_aux_soln_state, current_cell_index);
2878 
2879  // Write the values in a way that we can use sum-factorization on.
2880  for(int istate=0; istate<nstate; istate++){
2881  dealii::Tensor<1,dim,adtype> conv_ref_flux;
2882  dealii::Tensor<1,dim,adtype> diffusive_ref_flux;
2883  // transform the conservative convective physical flux to reference space
2884  if(!this->all_parameters->use_split_form && !this->all_parameters->use_curvilinear_split_form){
2885  metric_oper_int.transform_physical_to_reference(
2886  conv_phys_flux[istate],
2887  metric_cofactor_vol_int,
2888  conv_ref_flux);
2889  }
2890  // transform the dissipative flux to reference space
2891  metric_oper_int.transform_physical_to_reference(
2892  diffusive_phys_flux[istate],
2893  metric_cofactor_vol_int,
2894  diffusive_ref_flux);
2895 
2896  // Write the data in a way that we can use sum-factorization on.
2897  // Since sum-factorization improves the speed for matrix-vector multiplications,
2898  // We need the values to have their inner elements be vectors.
2899  for(int idim=0; idim<dim; idim++){
2900  // allocate
2901  if(iquad == 0){
2902  conv_ref_flux_at_vol_q_int[istate][idim].resize(n_quad_pts_vol_int);
2903  diffusive_ref_flux_at_vol_q_int[istate][idim].resize(n_quad_pts_vol_int);
2904  }
2905  // write data
2906  if(!this->all_parameters->use_split_form && !this->all_parameters->use_curvilinear_split_form){
2907  conv_ref_flux_at_vol_q_int[istate][idim][iquad] = conv_ref_flux[idim];
2908  }
2909  diffusive_ref_flux_at_vol_q_int[istate][idim][iquad] = diffusive_ref_flux[idim];
2910  }
2911  }
2912  }
2913 
2914  // Next we do exterior volume reference fluxes.
2915  // Note we split the quad integrals because the interior and exterior could be of different poly basis
2916  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> conv_ref_flux_at_vol_q_ext;
2917  std::array<dealii::Tensor<1,dim,std::vector<adtype>>,nstate> diffusive_ref_flux_at_vol_q_ext;
2918  for (unsigned int iquad=0; iquad<n_quad_pts_vol_ext; ++iquad) {
2919 
2920  // Extract exterior volume metric cofactor matrix at given volume cubature node.
2921  dealii::Tensor<2,dim,adtype> metric_cofactor_vol_ext;
2922  for(int idim=0; idim<dim; idim++){
2923  for(int jdim=0; jdim<dim; jdim++){
2924  metric_cofactor_vol_ext[idim][jdim] = metric_oper_ext.metric_cofactor_vol[idim][jdim][iquad];
2925  }
2926  }
2927 
2928  std::array<adtype,nstate> soln_state;
2929  std::array<dealii::Tensor<1,dim,adtype>,nstate> aux_soln_state;
2930  std::array<adtype,nstate> filtered_soln_state;
2931  std::array<dealii::Tensor<1,dim,adtype>,nstate> filtered_aux_soln_state;
2932  for(int istate=0; istate<nstate; istate++){
2933  soln_state[istate] = soln_at_vol_q_ext[istate][iquad];
2934  if(this->do_compute_filtered_solution) filtered_soln_state[istate] = legendre_soln_at_vol_q_ext[istate][iquad];
2935  for(int idim=0; idim<dim; idim++){
2936  aux_soln_state[istate][idim] = aux_soln_at_vol_q_ext[istate][idim][iquad];
2937  if(this->do_compute_filtered_solution) filtered_aux_soln_state[istate][idim] = legendre_aux_soln_at_vol_q_ext[istate][idim][iquad];
2938  }
2939  }
2940 
2941  // Evaluate physical convective flux
2942  std::array<dealii::Tensor<1,dim,adtype>,nstate> conv_phys_flux;
2943  if(!this->all_parameters->use_split_form && !this->all_parameters->use_curvilinear_split_form){
2944  conv_phys_flux = pde_physics.convective_flux (soln_state);
2945  }
2946 
2947  // Compute the physical dissipative flux
2948  std::array<dealii::Tensor<1,dim,adtype>,nstate> diffusive_phys_flux;
2949  diffusive_phys_flux = pde_physics.dissipative_flux(soln_state, aux_soln_state, filtered_soln_state, filtered_aux_soln_state, neighbor_cell_index);
2950 
2951  // Write the values in a way that we can use sum-factorization on.
2952  for(int istate=0; istate<nstate; istate++){
2953  dealii::Tensor<1,dim,adtype> conv_ref_flux;
2954  dealii::Tensor<1,dim,adtype> diffusive_ref_flux;
2955  // transform the conservative convective physical flux to reference space
2956  if(!this->all_parameters->use_split_form && !this->all_parameters->use_curvilinear_split_form){
2957  metric_oper_ext.transform_physical_to_reference(
2958  conv_phys_flux[istate],
2959  metric_cofactor_vol_ext,
2960  conv_ref_flux);
2961  }
2962  // transform the dissipative flux to reference space
2963  metric_oper_ext.transform_physical_to_reference(
2964  diffusive_phys_flux[istate],
2965  metric_cofactor_vol_ext,
2966  diffusive_ref_flux);
2967 
2968  // Write the data in a way that we can use sum-factorization on.
2969  // Since sum-factorization improves the speed for matrix-vector multiplications,
2970  // We need the values to have their inner elements be vectors.
2971  for(int idim=0; idim<dim; idim++){
2972  // allocate
2973  if(iquad == 0){
2974  conv_ref_flux_at_vol_q_ext[istate][idim].resize(n_quad_pts_vol_ext);
2975  diffusive_ref_flux_at_vol_q_ext[istate][idim].resize(n_quad_pts_vol_ext);
2976  }
2977  // write data
2978  if(!this->all_parameters->use_split_form && !this->all_parameters->use_curvilinear_split_form){
2979  conv_ref_flux_at_vol_q_ext[istate][idim][iquad] = conv_ref_flux[idim];
2980  }
2981  diffusive_ref_flux_at_vol_q_ext[istate][idim][iquad] = diffusive_ref_flux[idim];
2982  }
2983  }
2984  }
2985 
2986  // Interpolate the volume reference fluxes to the facet.
2987  // And do the dot product with the UNIT REFERENCE normal.
2988  // Since we are computing a dot product with the unit reference normal,
2989  // we exploit the fact that the unit reference normal has a value of 0 in all reference directions except
2990  // the outward reference normal dircetion.
2991  const dealii::Tensor<1,dim,double> unit_ref_normal_int = dealii::GeometryInfo<dim>::unit_normal_vector[iface];
2992  const dealii::Tensor<1,dim,double> unit_ref_normal_ext = dealii::GeometryInfo<dim>::unit_normal_vector[neighbor_iface];
2993  // Extract the reference direction that is outward facing on the facet.
2994  const int dim_not_zero_int = iface / 2;//reference direction of face integer division
2995  const int dim_not_zero_ext = neighbor_iface / 2;//reference direction of face integer division
2996 
2997  std::array<std::vector<adtype>,nstate> conv_int_vol_ref_flux_interp_to_face_dot_ref_normal;
2998  std::array<std::vector<adtype>,nstate> conv_ext_vol_ref_flux_interp_to_face_dot_ref_normal;
2999  std::array<std::vector<adtype>,nstate> diffusive_int_vol_ref_flux_interp_to_face_dot_ref_normal;
3000  std::array<std::vector<adtype>,nstate> diffusive_ext_vol_ref_flux_interp_to_face_dot_ref_normal;
3001  for(int istate=0; istate<nstate; istate++){
3002  //allocate
3003  conv_int_vol_ref_flux_interp_to_face_dot_ref_normal[istate].resize(n_face_quad_pts);
3004  conv_ext_vol_ref_flux_interp_to_face_dot_ref_normal[istate].resize(n_face_quad_pts);
3005  diffusive_int_vol_ref_flux_interp_to_face_dot_ref_normal[istate].resize(n_face_quad_pts);
3006  diffusive_ext_vol_ref_flux_interp_to_face_dot_ref_normal[istate].resize(n_face_quad_pts);
3007 
3008  // solve
3009  // Note, since the normal is zero in all other reference directions, we only have to interpolate one given reference direction to the facet
3010 
3011  // interpolate reference volume convective flux to the facet, and apply unit reference normal as scaled by 1.0 or -1.0
3012  if(!this->all_parameters->use_split_form && !this->all_parameters->use_curvilinear_split_form){
3013  flux_basis_int.matrix_vector_mult_surface_1D(face_orientation_int,
3014  iface,
3015  conv_ref_flux_at_vol_q_int[istate][dim_not_zero_int],
3016  conv_int_vol_ref_flux_interp_to_face_dot_ref_normal[istate],
3017  flux_basis_int.oneD_surf_operator,//the flux basis interpolates from the flux nodes
3018  flux_basis_int.oneD_vol_operator,
3019  false, unit_ref_normal_int[dim_not_zero_int]);//don't add to previous value, scale by unit_normal int
3020  flux_basis_ext.matrix_vector_mult_surface_1D(face_orientation_ext,
3021  neighbor_iface,
3022  conv_ref_flux_at_vol_q_ext[istate][dim_not_zero_ext],
3023  conv_ext_vol_ref_flux_interp_to_face_dot_ref_normal[istate],
3024  flux_basis_ext.oneD_surf_operator,
3025  flux_basis_ext.oneD_vol_operator,
3026  false, unit_ref_normal_ext[dim_not_zero_ext]);//don't add to previous value, unit_normal ext is -unit normal int
3027  }
3028 
3029  // interpolate reference volume dissipative flux to the facet, and apply unit reference normal as scaled by 1.0 or -1.0
3030  flux_basis_int.matrix_vector_mult_surface_1D(face_orientation_int,
3031  iface,
3032  diffusive_ref_flux_at_vol_q_int[istate][dim_not_zero_int],
3033  diffusive_int_vol_ref_flux_interp_to_face_dot_ref_normal[istate],
3034  flux_basis_int.oneD_surf_operator,
3035  flux_basis_int.oneD_vol_operator,
3036  false, unit_ref_normal_int[dim_not_zero_int]);
3037  flux_basis_ext.matrix_vector_mult_surface_1D(face_orientation_ext,
3038  neighbor_iface,
3039  diffusive_ref_flux_at_vol_q_ext[istate][dim_not_zero_ext],
3040  diffusive_ext_vol_ref_flux_interp_to_face_dot_ref_normal[istate],
3041  flux_basis_ext.oneD_surf_operator,
3042  flux_basis_ext.oneD_vol_operator,
3043  false, unit_ref_normal_ext[dim_not_zero_ext]);
3044  }
3045 
3046 
3047  //Note that for entropy-dissipation and entropy stability, the conservative variables
3048  //are functions of projected entropy variables. For Euler etc, the transformation is nonlinear
3049  //so careful attention to what is evaluated where and interpolated to where is needed.
3050  //For further information, please see Chan, Jesse. "On discretely entropy conservative and entropy stable discontinuous Galerkin methods." Journal of Computational Physics 362 (2018): 346-374.
3051  //pages 355 (Eq. 57 with text around it) and page 359 (Eq 86 and text below it).
3052 
3053  // First, transform the volume conservative solution at volume cubature nodes to entropy variables.
3054  std::array<std::vector<adtype>,nstate> entropy_var_vol_int;
3055  for(unsigned int iquad=0; iquad<n_quad_pts_vol_int; iquad++){
3056  std::array<adtype,nstate> soln_state;
3057  for(int istate=0; istate<nstate; istate++){
3058  soln_state[istate] = soln_at_vol_q_int[istate][iquad];
3059  }
3060  std::array<adtype,nstate> entropy_var;
3061  entropy_var = pde_physics.compute_entropy_variables(soln_state);
3062  for(int istate=0; istate<nstate; istate++){
3063  if(iquad==0){
3064  entropy_var_vol_int[istate].resize(n_quad_pts_vol_int);
3065  }
3066  entropy_var_vol_int[istate][iquad] = entropy_var[istate];
3067  }
3068  }
3069  std::array<std::vector<adtype>,nstate> entropy_var_vol_ext;
3070  for(unsigned int iquad=0; iquad<n_quad_pts_vol_ext; iquad++){
3071  std::array<adtype,nstate> soln_state;
3072  for(int istate=0; istate<nstate; istate++){
3073  soln_state[istate] = soln_at_vol_q_ext[istate][iquad];
3074  }
3075  std::array<adtype,nstate> entropy_var;
3076  entropy_var = pde_physics.compute_entropy_variables(soln_state);
3077  for(int istate=0; istate<nstate; istate++){
3078  if(iquad==0){
3079  entropy_var_vol_ext[istate].resize(n_quad_pts_vol_ext);
3080  }
3081  entropy_var_vol_ext[istate][iquad] = entropy_var[istate];
3082  }
3083  }
3084 
3085  //project it onto the solution basis functions and interpolate it
3086  std::array<std::vector<adtype>,nstate> projected_entropy_var_vol_int;
3087  std::array<std::vector<adtype>,nstate> projected_entropy_var_vol_ext;
3088  std::array<std::vector<adtype>,nstate> projected_entropy_var_surf_int;
3089  std::array<std::vector<adtype>,nstate> projected_entropy_var_surf_ext;
3090  std::array<std::vector<adtype>,nstate> projected_entropy_var_surf_int_corrected; //To be corrected for face orientation. Needed for numerical flux when using split form
3091  std::array<std::vector<adtype>,nstate> projected_entropy_var_surf_ext_corrected; //To be corrected for face orientation. Needed for numerical flux when using split form
3092  for(int istate=0; istate<nstate; istate++){
3093  // allocate
3094  projected_entropy_var_vol_int[istate].resize(n_quad_pts_vol_int);
3095  projected_entropy_var_vol_ext[istate].resize(n_quad_pts_vol_ext);
3096  projected_entropy_var_surf_int[istate].resize(n_face_quad_pts);
3097  projected_entropy_var_surf_ext[istate].resize(n_face_quad_pts);
3098  projected_entropy_var_surf_int_corrected[istate].resize(n_face_quad_pts);
3099  projected_entropy_var_surf_ext_corrected[istate].resize(n_face_quad_pts);
3100 
3101  //interior
3102  std::vector<adtype> entropy_var_coeff_int(n_shape_fns_int);
3103  soln_basis_projection_oper_int.matrix_vector_mult_1D(entropy_var_vol_int[istate],
3104  entropy_var_coeff_int,
3105  soln_basis_projection_oper_int.oneD_vol_operator);
3106  soln_basis_int.matrix_vector_mult_1D(entropy_var_coeff_int,
3107  projected_entropy_var_vol_int[istate],
3108  soln_basis_int.oneD_vol_operator);
3109  soln_basis_int.matrix_vector_mult_surface_1D({true,false,false},
3110  iface,
3111  entropy_var_coeff_int,
3112  projected_entropy_var_surf_int[istate],
3113  soln_basis_int.oneD_surf_operator,
3114  soln_basis_int.oneD_vol_operator);
3115 
3116  soln_basis_int.matrix_vector_mult_surface_1D(face_orientation_int,
3117  iface,
3118  entropy_var_coeff_int,
3119  projected_entropy_var_surf_int_corrected[istate],
3120  soln_basis_int.oneD_surf_operator,
3121  soln_basis_int.oneD_vol_operator);
3122 
3123  //exterior
3124  std::vector<adtype> entropy_var_coeff_ext(n_shape_fns_ext);
3125  soln_basis_projection_oper_ext.matrix_vector_mult_1D(entropy_var_vol_ext[istate],
3126  entropy_var_coeff_ext,
3127  soln_basis_projection_oper_ext.oneD_vol_operator);
3128 
3129  soln_basis_ext.matrix_vector_mult_1D(entropy_var_coeff_ext,
3130  projected_entropy_var_vol_ext[istate],
3131  soln_basis_ext.oneD_vol_operator);
3132  soln_basis_ext.matrix_vector_mult_surface_1D({true,false,false},
3133  neighbor_iface,
3134  entropy_var_coeff_ext,
3135  projected_entropy_var_surf_ext[istate],
3136  soln_basis_ext.oneD_surf_operator,
3137  soln_basis_ext.oneD_vol_operator);
3138 
3139  soln_basis_int.matrix_vector_mult_surface_1D(face_orientation_ext,
3140  neighbor_iface,
3141  entropy_var_coeff_ext,
3142  projected_entropy_var_surf_ext_corrected[istate],
3143  soln_basis_int.oneD_surf_operator,
3144  soln_basis_int.oneD_vol_operator);
3145  }
3146 
3147  //get the surface-volume sparsity pattern for a "sum-factorized" Hadamard product only computing terms needed for the operation.
3148  const unsigned int row_size_int = n_face_quad_pts * n_quad_pts_1D_int;
3149  const unsigned int col_size_int = n_face_quad_pts * n_quad_pts_1D_int;
3150  std::vector<unsigned int> Hadamard_rows_sparsity_int(row_size_int);
3151  std::vector<unsigned int> Hadamard_columns_sparsity_int(col_size_int);
3152  const unsigned int row_size_ext = n_face_quad_pts * n_quad_pts_1D_ext;
3153  const unsigned int col_size_ext = n_face_quad_pts * n_quad_pts_1D_ext;
3154  std::vector<unsigned int> Hadamard_rows_sparsity_ext(row_size_ext);
3155  std::vector<unsigned int> Hadamard_columns_sparsity_ext(col_size_ext);
3156  if(this->all_parameters->use_split_form || this->all_parameters->use_curvilinear_split_form){
3157  flux_basis_int.sum_factorized_Hadamard_surface_sparsity_pattern(n_face_quad_pts, n_quad_pts_1D_int, Hadamard_rows_sparsity_int, Hadamard_columns_sparsity_int, dim_not_zero_int);
3158  flux_basis_ext.sum_factorized_Hadamard_surface_sparsity_pattern(n_face_quad_pts, n_quad_pts_1D_ext, Hadamard_rows_sparsity_ext, Hadamard_columns_sparsity_ext, dim_not_zero_ext);
3159  }
3160 
3161  std::array<std::vector<adtype>,nstate> surf_vol_ref_2pt_flux_interp_surf_int;
3162  std::array<std::vector<adtype>,nstate> surf_vol_ref_2pt_flux_interp_surf_ext;
3163  std::array<std::vector<adtype>,nstate> surf_vol_ref_2pt_flux_interp_vol_int;
3164  std::array<std::vector<adtype>,nstate> surf_vol_ref_2pt_flux_interp_vol_ext;
3165  if(this->all_parameters->use_split_form || this->all_parameters->use_curvilinear_split_form){
3166  //get surface-volume hybrid 2pt flux from Eq.(15) in Chan, Jesse. "Skew-symmetric entropy stable modal discontinuous Galerkin formulations." Journal of Scientific Computing 81.1 (2019): 459-485.
3167  std::array<std::vector<adtype>,nstate> surface_ref_2pt_flux_int;
3168  std::array<std::vector<adtype>,nstate> surface_ref_2pt_flux_ext;
3169  //make use of the sparsity pattern from above to assemble only n^d non-zero entries without ever allocating not computing zeros.
3170  for(int istate=0; istate<nstate; istate++){
3171  surface_ref_2pt_flux_int[istate].resize(n_face_quad_pts * n_quad_pts_1D_int);
3172  surface_ref_2pt_flux_ext[istate].resize(n_face_quad_pts * n_quad_pts_1D_ext);
3173  }
3174  for(unsigned int iquad_face=0; iquad_face<n_face_quad_pts; iquad_face++){
3175  dealii::Tensor<2,dim,adtype> metric_cofactor_surf;
3176  for(int idim=0; idim<dim; idim++){
3177  for(int jdim=0; jdim<dim; jdim++){
3178  metric_cofactor_surf[idim][jdim] = metric_oper_int.metric_cofactor_surf[idim][jdim][iquad_face];
3179  }
3180  }
3181 
3182  //Compute the conservative values on the facet from the interpolated entorpy variables.
3183  std::array<adtype,nstate> entropy_var_face_int;
3184  std::array<adtype,nstate> entropy_var_face_ext;
3185  for(int istate=0; istate<nstate; istate++){
3186  entropy_var_face_int[istate] = projected_entropy_var_surf_int[istate][iquad_face];
3187  entropy_var_face_ext[istate] = projected_entropy_var_surf_ext[istate][iquad_face];
3188  }
3189  std::array<adtype,nstate> soln_state_face_int;
3190  soln_state_face_int = pde_physics.compute_conservative_variables_from_entropy_variables (entropy_var_face_int);
3191  std::array<adtype,nstate> soln_state_face_ext;
3192  soln_state_face_ext = pde_physics.compute_conservative_variables_from_entropy_variables (entropy_var_face_ext);
3193 
3194  //only do the n_quad_1D vol points that give non-zero entries from Hadamard product.
3195  for(unsigned int row_index = iquad_face * n_quad_pts_1D_int, column_index = 0;
3196  column_index < n_quad_pts_1D_int;
3197  row_index++, column_index++){
3198 
3199  if(Hadamard_rows_sparsity_int[row_index] != iquad_face){
3200  pcout<<"The interior Hadamard rows sparsity pattern does not match."<<std::endl;
3201  std::abort();
3202  }
3203 
3204  const unsigned int iquad_vol = Hadamard_columns_sparsity_int[row_index];//extract flux_quad pt that corresponds to a non-zero entry for Hadamard product.
3205  // Copy Metric Cofactor in a way can use for transforming Tensor Blocks to reference space
3206  // The way it is stored in metric_operators is to use sum-factorization in each direction,
3207  // but here it is cleaner to apply a reference transformation in each Tensor block returned by physics.
3208  dealii::Tensor<2,dim,adtype> metric_cofactor_vol_int;
3209  for(int idim=0; idim<dim; idim++){
3210  for(int jdim=0; jdim<dim; jdim++){
3211  metric_cofactor_vol_int[idim][jdim] = metric_oper_int.metric_cofactor_vol[idim][jdim][iquad_vol];
3212  }
3213  }
3214  std::array<adtype,nstate> entropy_var;
3215  for(int istate=0; istate<nstate; istate++){
3216  entropy_var[istate] = projected_entropy_var_vol_int[istate][iquad_vol];
3217  }
3218  std::array<adtype,nstate> soln_state;
3219  soln_state = pde_physics.compute_conservative_variables_from_entropy_variables (entropy_var);
3220  //Note that the flux basis is collocated on the volume cubature set so we don't need to evaluate the entropy variables
3221  //on the volume set then transform back to the conservative variables since the flux basis volume
3222  //projection is identity.
3223 
3224  //Compute the physical flux
3225  std::array<dealii::Tensor<1,dim,adtype>,nstate> conv_phys_flux_2pt;
3226  conv_phys_flux_2pt = pde_physics.convective_numerical_split_flux(soln_state, soln_state_face_int);
3227  for(int istate=0; istate<nstate; istate++){
3228  dealii::Tensor<1,dim,adtype> conv_ref_flux_2pt;
3229  //For each state, transform the physical flux to a reference flux.
3230  //For each state, transform the physical flux to a reference flux.
3231  dealii::Tensor<2,dim,adtype> metric_cofactor_split;
3232  for(int idim=0; idim<dim; idim++){
3233  for(int jdim=0; jdim<dim; jdim++){
3234  metric_cofactor_split[idim][jdim] = 0.5 * (metric_cofactor_surf[idim][jdim] + metric_cofactor_vol_int[idim][jdim]);
3235  }
3236  }
3237  metric_oper_int.transform_physical_to_reference(
3238  conv_phys_flux_2pt[istate],
3239  metric_cofactor_split,
3240  conv_ref_flux_2pt);
3241  //only store the dim not zero in reference space bc dot product with unit ref normal later.
3242  surface_ref_2pt_flux_int[istate][iquad_face * n_quad_pts_1D_int + column_index] = conv_ref_flux_2pt[dim_not_zero_int];
3243  }
3244  }
3245  for(unsigned int row_index = iquad_face * n_quad_pts_1D_ext, column_index = 0;
3246  column_index < n_quad_pts_1D_ext;
3247  row_index++, column_index++){
3248 
3249  if(Hadamard_rows_sparsity_ext[row_index] != iquad_face){
3250  pcout<<"The exterior Hadamard rows sparsity pattern does not match."<<std::endl;
3251  std::abort();
3252  }
3253 
3254  const unsigned int iquad_vol = Hadamard_columns_sparsity_ext[row_index];//extract flux_quad pt that corresponds to a non-zero entry for Hadamard product.
3255  // Copy Metric Cofactor in a way can use for transforming Tensor Blocks to reference space
3256  // The way it is stored in metric_operators is to use sum-factorization in each direction,
3257  // but here it is cleaner to apply a reference transformation in each Tensor block returned by physics.
3258  dealii::Tensor<2,dim,adtype> metric_cofactor_vol_ext;
3259  for(int idim=0; idim<dim; idim++){
3260  for(int jdim=0; jdim<dim; jdim++){
3261  metric_cofactor_vol_ext[idim][jdim] = metric_oper_ext.metric_cofactor_vol[idim][jdim][iquad_vol];
3262  }
3263  }
3264  std::array<adtype,nstate> entropy_var;
3265  for(int istate=0; istate<nstate; istate++){
3266  entropy_var[istate] = projected_entropy_var_vol_ext[istate][iquad_vol];
3267  }
3268  std::array<adtype,nstate> soln_state;
3269  soln_state = pde_physics.compute_conservative_variables_from_entropy_variables (entropy_var);
3270  //Compute the physical flux
3271  std::array<dealii::Tensor<1,dim,adtype>,nstate> conv_phys_flux_2pt;
3272  conv_phys_flux_2pt = pde_physics.convective_numerical_split_flux(soln_state, soln_state_face_ext);
3273  for(int istate=0; istate<nstate; istate++){
3274  dealii::Tensor<1,dim,adtype> conv_ref_flux_2pt;
3275  //For each state, transform the physical flux to a reference flux.
3276  dealii::Tensor<2,dim,adtype> metric_cofactor_split;
3277  for(int idim=0; idim<dim; idim++){
3278  for(int jdim=0; jdim<dim; jdim++){
3279  metric_cofactor_split[idim][jdim] = 0.5 * (metric_cofactor_surf[idim][jdim] + metric_cofactor_vol_ext[idim][jdim]);
3280  }
3281  }
3282  metric_oper_ext.transform_physical_to_reference(
3283  conv_phys_flux_2pt[istate],
3284  metric_cofactor_split,
3285  conv_ref_flux_2pt);
3286  //only store the dim not zero in reference space bc dot product with unit ref normal later.
3287  surface_ref_2pt_flux_ext[istate][iquad_face * n_quad_pts_1D_ext + column_index] = conv_ref_flux_2pt[dim_not_zero_ext];
3288  }
3289  }
3290  }
3291 
3292  //get the surface basis operator from Hadamard sparsity pattern
3293  //to be applied at n^d operations (on the face so n^{d+1-1}=n^d flops)
3294  //also only allocates n^d terms.
3295  const int iface_1D = iface % 2;//the reference face number
3296  const std::vector<double> &oneD_quad_weights_vol_int = this->oneD_quadrature_collection[poly_degree_int].get_weights();
3297  dealii::FullMatrix<real> surf_oper_sparse_int(n_face_quad_pts, n_quad_pts_1D_int);
3298  flux_basis_int.sum_factorized_Hadamard_surface_basis_assembly(n_face_quad_pts, n_quad_pts_1D_int,
3299  Hadamard_rows_sparsity_int, Hadamard_columns_sparsity_int,
3300  flux_basis_int.oneD_surf_operator[iface_1D],
3301  oneD_quad_weights_vol_int,
3302  surf_oper_sparse_int,
3303  dim_not_zero_int);
3304  const int neighbor_iface_1D = neighbor_iface % 2;//the reference neighbour face number
3305  const std::vector<double> &oneD_quad_weights_vol_ext = this->oneD_quadrature_collection[poly_degree_ext].get_weights();
3306  dealii::FullMatrix<real> surf_oper_sparse_ext(n_face_quad_pts, n_quad_pts_1D_ext);
3307  flux_basis_ext.sum_factorized_Hadamard_surface_basis_assembly(n_face_quad_pts, n_quad_pts_1D_ext,
3308  Hadamard_rows_sparsity_ext, Hadamard_columns_sparsity_ext,
3309  flux_basis_ext.oneD_surf_operator[neighbor_iface_1D],
3310  oneD_quad_weights_vol_ext,
3311  surf_oper_sparse_ext,
3312  dim_not_zero_ext);
3313 
3314  // Apply the surface Hadamard products and multiply with vector of ones for both off diagonal terms in
3315  // Eq.(15) in Chan, Jesse. "Skew-symmetric entropy stable modal discontinuous Galerkin formulations." Journal of Scientific Computing 81.1 (2019): 459-485.
3316  for(int istate=0; istate<nstate; istate++){
3317  //first apply Hadamard product with the structure made above.
3318  std::vector<adtype> surface_ref_2pt_flux_int_Hadamard_with_surf_oper(n_face_quad_pts * n_quad_pts_1D_int);
3319  flux_basis_int.Hadamard_product_AD_vector(surf_oper_sparse_int,
3320  surface_ref_2pt_flux_int[istate],
3321  surface_ref_2pt_flux_int_Hadamard_with_surf_oper);
3322  std::vector<adtype> surface_ref_2pt_flux_ext_Hadamard_with_surf_oper(n_face_quad_pts * n_quad_pts_1D_ext);
3323  flux_basis_ext.Hadamard_product_AD_vector(surf_oper_sparse_ext,
3324  surface_ref_2pt_flux_ext[istate],
3325  surface_ref_2pt_flux_ext_Hadamard_with_surf_oper);
3326  //sum with reference unit normal
3327  surf_vol_ref_2pt_flux_interp_surf_int[istate].resize(n_face_quad_pts);
3328  surf_vol_ref_2pt_flux_interp_surf_ext[istate].resize(n_face_quad_pts);
3329  surf_vol_ref_2pt_flux_interp_vol_int[istate].resize(n_quad_pts_vol_int);
3330  surf_vol_ref_2pt_flux_interp_vol_ext[istate].resize(n_quad_pts_vol_ext);
3331 
3332  for(unsigned int iface_quad=0; iface_quad<n_face_quad_pts; iface_quad++){
3333  for(unsigned int iquad_int=0; iquad_int<n_quad_pts_1D_int; iquad_int++){
3334  surf_vol_ref_2pt_flux_interp_surf_int[istate][iface_quad]
3335  -= surface_ref_2pt_flux_int_Hadamard_with_surf_oper[iface_quad * n_quad_pts_1D_int + iquad_int]
3336  * unit_ref_normal_int[dim_not_zero_int];
3337  const unsigned int column_index = iface_quad * n_quad_pts_1D_int + iquad_int;
3338  surf_vol_ref_2pt_flux_interp_vol_int[istate][Hadamard_columns_sparsity_int[column_index]]
3339  += surface_ref_2pt_flux_int_Hadamard_with_surf_oper[iface_quad * n_quad_pts_1D_int + iquad_int]
3340  * unit_ref_normal_int[dim_not_zero_int];
3341  }
3342  for(unsigned int iquad_ext=0; iquad_ext<n_quad_pts_1D_ext; iquad_ext++){
3343  surf_vol_ref_2pt_flux_interp_surf_ext[istate][iface_quad]
3344  -= surface_ref_2pt_flux_ext_Hadamard_with_surf_oper[iface_quad * n_quad_pts_1D_ext + iquad_ext]
3345  * (unit_ref_normal_ext[dim_not_zero_ext]);
3346  const unsigned int column_index = iface_quad * n_quad_pts_1D_ext + iquad_ext;
3347  surf_vol_ref_2pt_flux_interp_vol_ext[istate][Hadamard_columns_sparsity_ext[column_index]]
3348  += surface_ref_2pt_flux_ext_Hadamard_with_surf_oper[iface_quad * n_quad_pts_1D_ext + iquad_ext]
3349  * (unit_ref_normal_ext[dim_not_zero_ext]);
3350  }
3351  }
3352  }
3353  }//end of if split form or curvilinear split form
3354 
3355 
3356 
3357  // Evaluate reference numerical fluxes.
3358 
3359  std::array<std::vector<adtype>,nstate> conv_num_flux_dot_n;
3360  std::array<std::vector<adtype>,nstate> diss_auxi_num_flux_dot_n;
3361  for (unsigned int iquad=0; iquad<n_face_quad_pts; ++iquad) {
3362  // Copy Metric Cofactor on the facet in a way can use for transforming Tensor Blocks to reference space
3363  // The way it is stored in metric_operators is to use sum-factorization in each direction,
3364  // but here it is cleaner to apply a reference transformation in each Tensor block returned by physics.
3365  // Note that for a conforming mesh, the facet metric cofactor matrix is the same from either interioir or exterior metric terms.
3366  // This is verified for the metric computations in: unit_tests/operator_tests/surface_conforming_test.cpp
3367  dealii::Tensor<2,dim,adtype> metric_cofactor_surf;
3368  for(int idim=0; idim<dim; idim++){
3369  for(int jdim=0; jdim<dim; jdim++){
3370  metric_cofactor_surf[idim][jdim] = metric_oper_int.metric_cofactor_surf[idim][jdim][iquad];
3371  }
3372  }
3373 
3374  std::array<adtype,nstate> entropy_var_face_int;
3375  std::array<adtype,nstate> entropy_var_face_ext;
3376  std::array<dealii::Tensor<1,dim,adtype>,nstate> aux_soln_state_int;
3377  std::array<dealii::Tensor<1,dim,adtype>,nstate> aux_soln_state_ext;
3378  std::array<adtype,nstate> soln_interp_to_face_int;
3379  std::array<adtype,nstate> soln_interp_to_face_ext;
3380  std::array<dealii::Tensor<1,dim,adtype>,nstate> filtered_aux_soln_state_int;
3381  std::array<dealii::Tensor<1,dim,adtype>,nstate> filtered_aux_soln_state_ext;
3382  std::array<adtype,nstate> filtered_soln_interp_to_face_int;
3383  std::array<adtype,nstate> filtered_soln_interp_to_face_ext;
3384  for(int istate=0; istate<nstate; istate++){
3385  soln_interp_to_face_int[istate] = soln_at_surf_q_int[istate][iquad];
3386  soln_interp_to_face_ext[istate] = soln_at_surf_q_ext[istate][iquad];
3387  if(this->do_compute_filtered_solution) filtered_soln_interp_to_face_int[istate] = legendre_soln_at_surf_q_int[istate][iquad];
3388  if(this->do_compute_filtered_solution) filtered_soln_interp_to_face_ext[istate] = legendre_soln_at_surf_q_ext[istate][iquad];
3389  entropy_var_face_int[istate] = projected_entropy_var_surf_int_corrected[istate][iquad];
3390  entropy_var_face_ext[istate] = projected_entropy_var_surf_ext_corrected[istate][iquad];
3391  for(int idim=0; idim<dim; idim++){
3392  aux_soln_state_int[istate][idim] = aux_soln_at_surf_q_int[istate][idim][iquad];
3393  aux_soln_state_ext[istate][idim] = aux_soln_at_surf_q_ext[istate][idim][iquad];
3394  if(this->do_compute_filtered_solution) filtered_aux_soln_state_int[istate][idim] = legendre_aux_soln_at_surf_q_int[istate][idim][iquad];
3395  if(this->do_compute_filtered_solution) filtered_aux_soln_state_ext[istate][idim] = legendre_aux_soln_at_surf_q_ext[istate][idim][iquad];
3396  }
3397  }
3398 
3399  std::array<adtype,nstate> soln_state_int;
3400  soln_state_int = pde_physics.compute_conservative_variables_from_entropy_variables (entropy_var_face_int);
3401  std::array<adtype,nstate> soln_state_ext;
3402  soln_state_ext = pde_physics.compute_conservative_variables_from_entropy_variables (entropy_var_face_ext);
3403 
3404 
3405  if(!this->all_parameters->use_split_form && !this->all_parameters->use_curvilinear_split_form){
3406  for(int istate=0; istate<nstate; istate++){
3407  soln_state_int[istate] = soln_at_surf_q_int[istate][iquad];
3408  soln_state_ext[istate] = soln_at_surf_q_ext[istate][iquad];
3409  }
3410  }
3411 
3412  // numerical fluxes
3413  dealii::Tensor<1,dim,adtype> unit_phys_normal_int;
3414  metric_oper_int.transform_reference_to_physical(unit_ref_normal_int,
3415  metric_cofactor_surf,
3416  unit_phys_normal_int);
3417  adtype face_Jac_norm_scaled = 0.0;
3418  for(int idim=0; idim<dim; idim++){
3419  face_Jac_norm_scaled += unit_phys_normal_int[idim] * unit_phys_normal_int[idim];
3420  }
3421  face_Jac_norm_scaled = sqrt(face_Jac_norm_scaled);
3422  unit_phys_normal_int /= face_Jac_norm_scaled;//normalize it.
3423  // Note that the facet determinant of metric jacobian is the above norm multiplied by the determinant of the metric Jacobian evaluated on the facet.
3424  // Since the determinant of the metric Jacobian evaluated on the face cancels off, we can just scale the numerical flux by the norm.
3425  std::array<adtype,nstate> conv_num_flux_dot_n_at_q;
3426  std::array<adtype,nstate> diss_auxi_num_flux_dot_n_at_q;
3427  // Convective numerical flux.
3428  conv_num_flux_dot_n_at_q = conv_num_flux.evaluate_flux(soln_state_int, soln_state_ext, unit_phys_normal_int);
3429  // dissipative numerical flux
3430  diss_auxi_num_flux_dot_n_at_q = diss_num_flux.evaluate_auxiliary_flux(
3431  current_cell_index, neighbor_cell_index,
3432  0.0, 0.0,
3433  soln_interp_to_face_int, soln_interp_to_face_ext,
3434  aux_soln_state_int, aux_soln_state_ext,
3435  filtered_soln_interp_to_face_int, filtered_soln_interp_to_face_ext,
3436  filtered_aux_soln_state_int, filtered_aux_soln_state_ext,
3437  unit_phys_normal_int, penalty, false);
3438 
3439  // Write the values in a way that we can use sum-factorization on.
3440  for(int istate=0; istate<nstate; istate++){
3441  // Write the data in a way that we can use sum-factorization on.
3442  // Since sum-factorization improves the speed for matrix-vector multiplications,
3443  // We need the values to have their inner elements be vectors of n_face_quad_pts.
3444 
3445  // allocate
3446  if(iquad == 0){
3447  conv_num_flux_dot_n[istate].resize(n_face_quad_pts);
3448  diss_auxi_num_flux_dot_n[istate].resize(n_face_quad_pts);
3449  }
3450 
3451  // write data
3452  conv_num_flux_dot_n[istate][iquad] = face_Jac_norm_scaled * conv_num_flux_dot_n_at_q[istate];
3453  diss_auxi_num_flux_dot_n[istate][iquad] = face_Jac_norm_scaled * diss_auxi_num_flux_dot_n_at_q[istate];
3454  }
3455  }
3456 
3457  // Compute RHS
3458  const std::vector<double> &surf_quad_weights = this->face_quadrature_collection[poly_degree_int].get_weights();
3459  for(int istate=0; istate<nstate; istate++){
3460  // interior RHS
3461  std::vector<adtype> rhs_int(n_shape_fns_int);
3462 
3463  // convective flux
3464  if(this->all_parameters->use_split_form || this->all_parameters->use_curvilinear_split_form){
3465  std::vector<real> ones_surf(n_face_quad_pts, 1.0);
3466  soln_basis_int.inner_product_surface_1D({true,false,false},
3467  iface,
3468  surf_vol_ref_2pt_flux_interp_surf_int[istate],
3469  ones_surf, rhs_int,
3470  soln_basis_int.oneD_surf_operator,
3471  soln_basis_int.oneD_vol_operator,
3472  false, -1.0);
3473  std::vector<real> ones_vol(n_quad_pts_vol_int, 1.0);
3474  soln_basis_int.inner_product_1D(surf_vol_ref_2pt_flux_interp_vol_int[istate],
3475  ones_vol, rhs_int,
3476  soln_basis_int.oneD_vol_operator,
3477  true, -1.0);
3478  }
3479  else
3480  {
3481  soln_basis_int.inner_product_surface_1D(face_orientation_int,
3482  iface,
3483  conv_int_vol_ref_flux_interp_to_face_dot_ref_normal[istate],
3484  surf_quad_weights, rhs_int,
3485  soln_basis_int.oneD_surf_operator,
3486  soln_basis_int.oneD_vol_operator,
3487  false, 1.0);
3488  }
3489  // dissipative flux
3490  soln_basis_int.inner_product_surface_1D(face_orientation_int,
3491  iface,
3492  diffusive_int_vol_ref_flux_interp_to_face_dot_ref_normal[istate],
3493  surf_quad_weights, rhs_int,
3494  soln_basis_int.oneD_surf_operator,
3495  soln_basis_int.oneD_vol_operator,
3496  true, 1.0);//adding=true, subtract the negative so add it
3497  // convective numerical flux
3498  soln_basis_int.inner_product_surface_1D(face_orientation_int,
3499  iface,
3500  conv_num_flux_dot_n[istate],
3501  surf_quad_weights, rhs_int,
3502  soln_basis_int.oneD_surf_operator,
3503  soln_basis_int.oneD_vol_operator,
3504  true, -1.0);//adding=true, scaled by factor=-1.0 bc subtract it
3505  // dissipative numerical flux
3506  soln_basis_int.inner_product_surface_1D(face_orientation_int,
3507  iface,
3508  diss_auxi_num_flux_dot_n[istate],
3509  surf_quad_weights, rhs_int,
3510  soln_basis_int.oneD_surf_operator,
3511  soln_basis_int.oneD_vol_operator,
3512  true, -1.0);//adding=true, scaled by factor=-1.0 bc subtract it
3513 
3514 
3515  for(unsigned int ishape=0; ishape<n_shape_fns_int; ishape++){
3516  local_rhs_int_cell[istate*n_shape_fns_int + ishape] += rhs_int[ishape];
3517  }
3518 
3519  // exterior RHS
3520  std::vector<adtype> rhs_ext(n_shape_fns_ext);
3521 
3522  // convective flux
3523  if(this->all_parameters->use_split_form || this->all_parameters->use_curvilinear_split_form){
3524  std::vector<real> ones_surf(n_face_quad_pts, 1.0);
3525  soln_basis_ext.inner_product_surface_1D({true,false,false},
3526  neighbor_iface,
3527  surf_vol_ref_2pt_flux_interp_surf_ext[istate],
3528  ones_surf, rhs_ext,
3529  soln_basis_ext.oneD_surf_operator,
3530  soln_basis_ext.oneD_vol_operator,
3531  false, -1.0);//the negative sign is bc the surface Hadamard function computes it on the otherside.
3532  //to satisfy the unit test that checks consistency with Jesse Chan's formulation.
3533  std::vector<real> ones_vol(n_quad_pts_vol_ext, 1.0);
3534  soln_basis_ext.inner_product_1D(surf_vol_ref_2pt_flux_interp_vol_ext[istate],
3535  ones_vol, rhs_ext,
3536  soln_basis_ext.oneD_vol_operator,
3537  true, -1.0);
3538  }
3539  else
3540  {
3541  soln_basis_ext.inner_product_surface_1D(face_orientation_ext,
3542  neighbor_iface,
3543  conv_ext_vol_ref_flux_interp_to_face_dot_ref_normal[istate],
3544  surf_quad_weights, rhs_ext,
3545  soln_basis_ext.oneD_surf_operator,
3546  soln_basis_ext.oneD_vol_operator,
3547  false, 1.0);//adding false
3548  }
3549  // dissipative flux
3550  soln_basis_ext.inner_product_surface_1D(face_orientation_ext,
3551  neighbor_iface,
3552  diffusive_ext_vol_ref_flux_interp_to_face_dot_ref_normal[istate],
3553  surf_quad_weights, rhs_ext,
3554  soln_basis_ext.oneD_surf_operator,
3555  soln_basis_ext.oneD_vol_operator,
3556  true, 1.0);//adding=true
3557  // convective numerical flux
3558  soln_basis_ext.inner_product_surface_1D(face_orientation_ext,
3559  neighbor_iface,
3560  conv_num_flux_dot_n[istate],
3561  surf_quad_weights, rhs_ext,
3562  soln_basis_ext.oneD_surf_operator,
3563  soln_basis_ext.oneD_vol_operator,
3564  true, 1.0);//adding=true, scaled by factor=1.0 because negative numerical flux and subtract it
3565  // dissipative numerical flux
3566  soln_basis_ext.inner_product_surface_1D(face_orientation_ext,
3567  neighbor_iface,
3568  diss_auxi_num_flux_dot_n[istate],
3569  surf_quad_weights, rhs_ext,
3570  soln_basis_ext.oneD_surf_operator,
3571  soln_basis_ext.oneD_vol_operator,
3572  true, 1.0);//adding=true, scaled by factor=1.0 because negative numerical flux and subtract it
3573 
3574 
3575  for(unsigned int ishape=0; ishape<n_shape_fns_ext; ishape++){
3576  local_rhs_ext_cell[istate*n_shape_fns_ext + ishape] += rhs_ext[ishape];
3577  }
3578  }
3579 }
3580 
3581 /*******************************************************
3582  *
3583  * EXPLICIT
3584  *
3585  *******************************************************/
3586 
3587 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
3589  typename dealii::DoFHandler<dim>::active_cell_iterator /*cell*/,
3590  const dealii::types::global_dof_index /*current_cell_index*/,
3591  const dealii::FEValues<dim,dim> &/*fe_values_vol*/,
3592  const std::vector<dealii::types::global_dof_index> &/*cell_dofs_indices*/,
3593  const std::vector<dealii::types::global_dof_index> &/*metric_dof_indices*/,
3594  const unsigned int /*poly_degree*/,
3595  const unsigned int /*grid_degree*/,
3596  dealii::Vector<real> &/*local_rhs_int_cell*/,
3597  const dealii::FEValues<dim,dim> &/*fe_values_lagrange*/)
3598 {
3599  //do nothing
3600 }
3601 
3602 
3603 template <int dim, int nspecies, int nstate, typename real, typename MeshType>
3605 {
3606  if(compute_d2R){
3607  this->dual.reinit(this->locally_owned_dofs, this->ghost_dofs, this->mpi_communicator);
3608  }
3609 }
3610 
3611 #if PHILIP_SPECIES==1
3612  // using default MeshType = Triangulation
3613 // 1D: dealii::Triangulation<dim>;
3614 // Otherwise: dealii::parallel::distributed::Triangulation<dim>;
3621 
3628 
3629 #if PHILIP_DIM!=1
3636 #endif
3637 
3638 #define POSSIBLE_NSTATE (1)(2)(3)(4)(5)(6)
3639 
3640 // Define a macro to instantiate MyTemplate for a specific index
3641 #define INSTANTIATE_DISTRIBUTED(r, data, index) \
3642  template void DGStrong <PHILIP_DIM, PHILIP_SPECIES, index, double, dealii::parallel::distributed::Triangulation<PHILIP_DIM>>::assemble_face_term_auxiliary_equation<double>(const unsigned int iface, const unsigned int neighbor_iface, const dealii::types::global_dof_index current_cell_index, const dealii::types::global_dof_index neighbor_cell_index, std::vector<bool> face_orientation_int, std::vector<bool> face_orientation_ext, const std::array<std::vector<double>,index> &soln_coeff_int, const std::array<std::vector<double>,index> &soln_coeff_ext, const unsigned int poly_degree_int,const unsigned int poly_degree_ext, OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_int,OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_ext, OPERATOR::metric_operators<double,PHILIP_DIM,2*PHILIP_DIM> &metric_oper_int, const Physics::PhysicsBase<PHILIP_DIM, PHILIP_SPECIES, index, double> &pde_physics, const NumericalFlux::NumericalFluxDissipative<PHILIP_DIM, PHILIP_SPECIES, index, double> &diss_num_flux, dealii::Tensor<1,PHILIP_DIM,std::vector<double>> &local_auxiliary_RHS_int, dealii::Tensor<1,PHILIP_DIM,std::vector<double>> &local_auxiliary_RHS_ext);\
3643  template void DGStrong <PHILIP_DIM, PHILIP_SPECIES, index, double, dealii::parallel::distributed::Triangulation<PHILIP_DIM>>::assemble_face_term_auxiliary_equation<codi_JacobianComputationType>(const unsigned int iface, const unsigned int neighbor_iface, const dealii::types::global_dof_index current_cell_index, const dealii::types::global_dof_index neighbor_cell_index, std::vector<bool> face_orientation_int, std::vector<bool> face_orientation_ext, const std::array<std::vector<codi_JacobianComputationType>,index> &soln_coeff_int, const std::array<std::vector<codi_JacobianComputationType>,index> &soln_coeff_ext, const unsigned int poly_degree_int,const unsigned int poly_degree_ext, OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_int,OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_ext, OPERATOR::metric_operators<codi_JacobianComputationType,PHILIP_DIM,2*PHILIP_DIM> &metric_oper_int, const Physics::PhysicsBase<PHILIP_DIM, PHILIP_SPECIES, index, codi_JacobianComputationType> &pde_physics, const NumericalFlux::NumericalFluxDissipative<PHILIP_DIM, PHILIP_SPECIES, index, codi_JacobianComputationType> &diss_num_flux, dealii::Tensor<1,PHILIP_DIM,std::vector<codi_JacobianComputationType>> &local_auxiliary_RHS_int, dealii::Tensor<1,PHILIP_DIM,std::vector<codi_JacobianComputationType>> &local_auxiliary_RHS_ext);\
3644  template void DGStrong <PHILIP_DIM, PHILIP_SPECIES, index, double, dealii::parallel::distributed::Triangulation<PHILIP_DIM>>::assemble_face_term_auxiliary_equation<codi_HessianComputationType>(const unsigned int iface, const unsigned int neighbor_iface, const dealii::types::global_dof_index current_cell_index, const dealii::types::global_dof_index neighbor_cell_index, std::vector<bool> face_orientation_int, std::vector<bool> face_orientation_ext, const std::array<std::vector<codi_HessianComputationType>,index> &soln_coeff_int, const std::array<std::vector<codi_HessianComputationType>,index> &soln_coeff_ext, const unsigned int poly_degree_int,const unsigned int poly_degree_ext, OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_int,OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_ext, OPERATOR::metric_operators<codi_HessianComputationType,PHILIP_DIM,2*PHILIP_DIM> &metric_oper_int, const Physics::PhysicsBase<PHILIP_DIM, PHILIP_SPECIES, index, codi_HessianComputationType> &pde_physics, const NumericalFlux::NumericalFluxDissipative<PHILIP_DIM, PHILIP_SPECIES, index, codi_HessianComputationType> &diss_num_flux, dealii::Tensor<1,PHILIP_DIM,std::vector<codi_HessianComputationType>> &local_auxiliary_RHS_int, dealii::Tensor<1,PHILIP_DIM,std::vector<codi_HessianComputationType>> &local_auxiliary_RHS_ext);
3645 
3646 
3647 #define INSTANTIATE_SHARED(r, data, index) \
3648  template void DGStrong <PHILIP_DIM, PHILIP_SPECIES, index, double, dealii::parallel::shared::Triangulation<PHILIP_DIM>>::assemble_face_term_auxiliary_equation<double>(const unsigned int iface, const unsigned int neighbor_iface, const dealii::types::global_dof_index current_cell_index, const dealii::types::global_dof_index neighbor_cell_index, std::vector<bool> face_orientation_int, std::vector<bool> face_orientation_ext, const std::array<std::vector<double>,index> &soln_coeff_int, const std::array<std::vector<double>,index> &soln_coeff_ext, const unsigned int poly_degree_int,const unsigned int poly_degree_ext, OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_int,OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_ext, OPERATOR::metric_operators<double,PHILIP_DIM,2*PHILIP_DIM> &metric_oper_int, const Physics::PhysicsBase<PHILIP_DIM, PHILIP_SPECIES, index, double> &pde_physics, const NumericalFlux::NumericalFluxDissipative<PHILIP_DIM, PHILIP_SPECIES, index, double> &diss_num_flux, dealii::Tensor<1,PHILIP_DIM,std::vector<double>> &local_auxiliary_RHS_int, dealii::Tensor<1,PHILIP_DIM,std::vector<double>> &local_auxiliary_RHS_ext);\
3649  template void DGStrong <PHILIP_DIM, PHILIP_SPECIES, index, double, dealii::parallel::shared::Triangulation<PHILIP_DIM>>::assemble_face_term_auxiliary_equation<codi_JacobianComputationType>(const unsigned int iface, const unsigned int neighbor_iface, const dealii::types::global_dof_index current_cell_index, const dealii::types::global_dof_index neighbor_cell_index, std::vector<bool> face_orientation_int, std::vector<bool> face_orientation_ext, const std::array<std::vector<codi_JacobianComputationType>,index> &soln_coeff_int, const std::array<std::vector<codi_JacobianComputationType>,index> &soln_coeff_ext, const unsigned int poly_degree_int,const unsigned int poly_degree_ext, OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_int,OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_ext, OPERATOR::metric_operators<codi_JacobianComputationType,PHILIP_DIM,2*PHILIP_DIM> &metric_oper_int, const Physics::PhysicsBase<PHILIP_DIM, PHILIP_SPECIES, index, codi_JacobianComputationType> &pde_physics, const NumericalFlux::NumericalFluxDissipative<PHILIP_DIM, PHILIP_SPECIES, index, codi_JacobianComputationType> &diss_num_flux, dealii::Tensor<1,PHILIP_DIM,std::vector<codi_JacobianComputationType>> &local_auxiliary_RHS_int, dealii::Tensor<1,PHILIP_DIM,std::vector<codi_JacobianComputationType>> &local_auxiliary_RHS_ext);\
3650  template void DGStrong <PHILIP_DIM, PHILIP_SPECIES, index, double, dealii::parallel::shared::Triangulation<PHILIP_DIM>>::assemble_face_term_auxiliary_equation<codi_HessianComputationType>(const unsigned int iface, const unsigned int neighbor_iface, const dealii::types::global_dof_index current_cell_index, const dealii::types::global_dof_index neighbor_cell_index, std::vector<bool> face_orientation_int, std::vector<bool> face_orientation_ext, const std::array<std::vector<codi_HessianComputationType>,index> &soln_coeff_int, const std::array<std::vector<codi_HessianComputationType>,index> &soln_coeff_ext, const unsigned int poly_degree_int,const unsigned int poly_degree_ext, OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_int,OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_ext, OPERATOR::metric_operators<codi_HessianComputationType,PHILIP_DIM,2*PHILIP_DIM> &metric_oper_int, const Physics::PhysicsBase<PHILIP_DIM, PHILIP_SPECIES, index, codi_HessianComputationType> &pde_physics, const NumericalFlux::NumericalFluxDissipative<PHILIP_DIM, PHILIP_SPECIES, index, codi_HessianComputationType> &diss_num_flux, dealii::Tensor<1,PHILIP_DIM,std::vector<codi_HessianComputationType>> &local_auxiliary_RHS_int, dealii::Tensor<1,PHILIP_DIM,std::vector<codi_HessianComputationType>> &local_auxiliary_RHS_ext);
3651 
3652 
3653 #define INSTANTIATE_TRIA(r, data, index) \
3654  template void DGStrong <PHILIP_DIM, PHILIP_SPECIES, index, double, dealii::Triangulation<PHILIP_DIM>>::assemble_face_term_auxiliary_equation<double>(const unsigned int iface, const unsigned int neighbor_iface, const dealii::types::global_dof_index current_cell_index, const dealii::types::global_dof_index neighbor_cell_index, std::vector<bool> face_orientation_int, std::vector<bool> face_orientation_ext, const std::array<std::vector<double>,index> &soln_coeff_int, const std::array<std::vector<double>,index> &soln_coeff_ext, const unsigned int poly_degree_int,const unsigned int poly_degree_ext, OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_int,OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_ext, OPERATOR::metric_operators<double,PHILIP_DIM,2*PHILIP_DIM> &metric_oper_int, const Physics::PhysicsBase<PHILIP_DIM, PHILIP_SPECIES, index, double> &pde_physics, const NumericalFlux::NumericalFluxDissipative<PHILIP_DIM, PHILIP_SPECIES, index, double> &diss_num_flux, dealii::Tensor<1,PHILIP_DIM,std::vector<double>> &local_auxiliary_RHS_int, dealii::Tensor<1,PHILIP_DIM,std::vector<double>> &local_auxiliary_RHS_ext);\
3655  template void DGStrong <PHILIP_DIM, PHILIP_SPECIES, index, double, dealii::Triangulation<PHILIP_DIM>>::assemble_face_term_auxiliary_equation<codi_JacobianComputationType>(const unsigned int iface, const unsigned int neighbor_iface, const dealii::types::global_dof_index current_cell_index, const dealii::types::global_dof_index neighbor_cell_index, std::vector<bool> face_orientation_int, std::vector<bool> face_orientation_ext, const std::array<std::vector<codi_JacobianComputationType>,index> &soln_coeff_int, const std::array<std::vector<codi_JacobianComputationType>,index> &soln_coeff_ext, const unsigned int poly_degree_int,const unsigned int poly_degree_ext, OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_int,OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_ext, OPERATOR::metric_operators<codi_JacobianComputationType,PHILIP_DIM,2*PHILIP_DIM> &metric_oper_int, const Physics::PhysicsBase<PHILIP_DIM, PHILIP_SPECIES, index, codi_JacobianComputationType> &pde_physics, const NumericalFlux::NumericalFluxDissipative<PHILIP_DIM, PHILIP_SPECIES, index, codi_JacobianComputationType> &diss_num_flux, dealii::Tensor<1,PHILIP_DIM,std::vector<codi_JacobianComputationType>> &local_auxiliary_RHS_int, dealii::Tensor<1,PHILIP_DIM,std::vector<codi_JacobianComputationType>> &local_auxiliary_RHS_ext);\
3656  template void DGStrong <PHILIP_DIM, PHILIP_SPECIES, index, double, dealii::Triangulation<PHILIP_DIM>>::assemble_face_term_auxiliary_equation<codi_HessianComputationType>(const unsigned int iface, const unsigned int neighbor_iface, const dealii::types::global_dof_index current_cell_index, const dealii::types::global_dof_index neighbor_cell_index, std::vector<bool> face_orientation_int, std::vector<bool> face_orientation_ext, const std::array<std::vector<codi_HessianComputationType>,index> &soln_coeff_int, const std::array<std::vector<codi_HessianComputationType>,index> &soln_coeff_ext, const unsigned int poly_degree_int,const unsigned int poly_degree_ext, OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_int,OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_ext, OPERATOR::metric_operators<codi_HessianComputationType,PHILIP_DIM,2*PHILIP_DIM> &metric_oper_int, const Physics::PhysicsBase<PHILIP_DIM, PHILIP_SPECIES, index, codi_HessianComputationType> &pde_physics, const NumericalFlux::NumericalFluxDissipative<PHILIP_DIM, PHILIP_SPECIES, index, codi_HessianComputationType> &diss_num_flux, dealii::Tensor<1,PHILIP_DIM,std::vector<codi_HessianComputationType>> &local_auxiliary_RHS_int, dealii::Tensor<1,PHILIP_DIM,std::vector<codi_HessianComputationType>> &local_auxiliary_RHS_ext);
3657 
3658 
3659 #if PHILIP_DIM!=1
3660 BOOST_PP_SEQ_FOR_EACH(INSTANTIATE_DISTRIBUTED, _, POSSIBLE_NSTATE)
3661 #endif
3662 
3663 BOOST_PP_SEQ_FOR_EACH(INSTANTIATE_SHARED, _, POSSIBLE_NSTATE)
3664 
3665 BOOST_PP_SEQ_FOR_EACH(INSTANTIATE_TRIA, _, POSSIBLE_NSTATE)
3666 #else
3667  #define NSTATE PHILIP_DIM+PHILIP_SPECIES+1
3668  #if PHILIP_DIM != 1
3670  template void DGStrong <PHILIP_DIM, PHILIP_SPECIES, NSTATE, double, dealii::parallel::distributed::Triangulation<PHILIP_DIM>>::assemble_face_term_auxiliary_equation<double>(const unsigned int iface, const unsigned int neighbor_iface, const dealii::types::global_dof_index current_cell_index, const dealii::types::global_dof_index neighbor_cell_index, std::vector<bool> face_orientation_int, std::vector<bool> face_orientation_ext, const std::array<std::vector<double>,NSTATE> &soln_coeff_int, const std::array<std::vector<double>,NSTATE> &soln_coeff_ext, const unsigned int poly_degree_int,const unsigned int poly_degree_ext, OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_int,OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_ext, OPERATOR::metric_operators<double,PHILIP_DIM,2*PHILIP_DIM> &metric_oper_int, const Physics::PhysicsBase<PHILIP_DIM, PHILIP_SPECIES, NSTATE, double> &pde_physics, const NumericalFlux::NumericalFluxDissipative<PHILIP_DIM, PHILIP_SPECIES, NSTATE, double> &diss_num_flux, dealii::Tensor<1,PHILIP_DIM,std::vector<double>> &local_auxiliary_RHS_int, dealii::Tensor<1,PHILIP_DIM,std::vector<double>> &local_auxiliary_RHS_ext);
3671  #endif
3673  template void DGStrong <PHILIP_DIM, PHILIP_SPECIES, NSTATE, double, dealii::parallel::shared::Triangulation<PHILIP_DIM>>::assemble_face_term_auxiliary_equation<double>(const unsigned int iface, const unsigned int neighbor_iface, const dealii::types::global_dof_index current_cell_index, const dealii::types::global_dof_index neighbor_cell_index, std::vector<bool> face_orientation_int, std::vector<bool> face_orientation_ext, const std::array<std::vector<double>,NSTATE> &soln_coeff_int, const std::array<std::vector<double>,NSTATE> &soln_coeff_ext, const unsigned int poly_degree_int,const unsigned int poly_degree_ext, OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_int,OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_ext, OPERATOR::metric_operators<double,PHILIP_DIM,2*PHILIP_DIM> &metric_oper_int, const Physics::PhysicsBase<PHILIP_DIM, PHILIP_SPECIES, NSTATE, double> &pde_physics, const NumericalFlux::NumericalFluxDissipative<PHILIP_DIM, PHILIP_SPECIES, NSTATE, double> &diss_num_flux, dealii::Tensor<1,PHILIP_DIM,std::vector<double>> &local_auxiliary_RHS_int, dealii::Tensor<1,PHILIP_DIM,std::vector<double>> &local_auxiliary_RHS_ext);
3675  template void DGStrong <PHILIP_DIM, PHILIP_SPECIES, NSTATE, double, dealii::Triangulation<PHILIP_DIM>>::assemble_face_term_auxiliary_equation<double>(const unsigned int iface, const unsigned int neighbor_iface, const dealii::types::global_dof_index current_cell_index, const dealii::types::global_dof_index neighbor_cell_index, std::vector<bool> face_orientation_int, std::vector<bool> face_orientation_ext, const std::array<std::vector<double>,NSTATE> &soln_coeff_int, const std::array<std::vector<double>,NSTATE> &soln_coeff_ext, const unsigned int poly_degree_int,const unsigned int poly_degree_ext, OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_int,OPERATOR::basis_functions<PHILIP_DIM,2*PHILIP_DIM> &soln_basis_ext, OPERATOR::metric_operators<double,PHILIP_DIM,2*PHILIP_DIM> &metric_oper_int, const Physics::PhysicsBase<PHILIP_DIM, PHILIP_SPECIES, NSTATE, double> &pde_physics, const NumericalFlux::NumericalFluxDissipative<PHILIP_DIM, PHILIP_SPECIES, NSTATE, double> &diss_num_flux, dealii::Tensor<1,PHILIP_DIM,std::vector<double>> &local_auxiliary_RHS_int, dealii::Tensor<1,PHILIP_DIM,std::vector<double>> &local_auxiliary_RHS_ext);
3676 #endif
3677 } // PHiLiP namespace
void assemble_face_term_strong(const unsigned int iface, const unsigned int neighbor_iface, const dealii::types::global_dof_index current_cell_index, const dealii::types::global_dof_index neighbor_cell_index, std::vector< bool > face_orientation_int, std::vector< bool > face_orientation_ext, const std::array< std::vector< adtype >, nstate > &soln_coeff_int, const std::array< std::vector< adtype >, nstate > &soln_coeff_ext, const std::array< dealii::Tensor< 1, dim, std::vector< adtype >>, nstate > &aux_soln_coeff_int, const std::array< dealii::Tensor< 1, dim, std::vector< adtype >>, nstate > &aux_soln_coeff_ext, const unsigned int poly_degree_int, const unsigned int poly_degree_ext, const real penalty, OPERATOR::basis_functions< dim, 2 *dim > &soln_basis_int, OPERATOR::basis_functions< dim, 2 *dim > &soln_basis_ext, OPERATOR::basis_functions< dim, 2 *dim > &flux_basis_int, OPERATOR::basis_functions< dim, 2 *dim > &flux_basis_ext, OPERATOR::vol_projection_operator< dim, 2 *dim > &soln_basis_projection_oper_int, OPERATOR::vol_projection_operator< dim, 2 *dim > &soln_basis_projection_oper_ext, OPERATOR::metric_operators< adtype, dim, 2 *dim > &metric_oper_int, OPERATOR::metric_operators< adtype, dim, 2 *dim > &metric_oper_ext, Physics::PhysicsBase< dim, nspecies, nstate, adtype > &pde_physics, const NumericalFlux::NumericalFluxConvective< dim, nspecies, nstate, adtype > &conv_num_flux, const NumericalFlux::NumericalFluxDissipative< dim, nspecies, nstate, adtype > &diss_num_flux, std::vector< adtype > &local_rhs_int_cell, std::vector< adtype > &local_rhs_ext_cell)
Strong form primary equation&#39;s facet right-hand-side.
Definition: strong_dg.cpp:2371
void reinit_operators_for_cell_residual_loop(const unsigned int poly_degree_int, const unsigned int poly_degree_ext, const unsigned int grid_degree, OPERATOR::basis_functions< dim, 2 *dim > &soln_basis_int, OPERATOR::basis_functions< dim, 2 *dim > &soln_basis_ext, OPERATOR::basis_functions< dim, 2 *dim > &flux_basis_int, OPERATOR::basis_functions< dim, 2 *dim > &flux_basis_ext, OPERATOR::local_basis_stiffness< dim, 2 *dim > &flux_basis_stiffness, OPERATOR::vol_projection_operator< dim, 2 *dim > &soln_basis_projection_oper_int, OPERATOR::vol_projection_operator< dim, 2 *dim > &soln_basis_projection_oper_ext, OPERATOR::mapping_shape_functions< dim, 2 *dim > &mapping_basis)
Builds needed operators for cell residual loop.
Definition: dg_base.cpp:2553
dealii::Tensor< 2, dim, std::vector< real > > metric_cofactor_vol
The volume metric cofactor matrix.
Definition: operators.h:1210
PartialDifferentialEquation pde_type
Store the PDE type to be solved.
const dealii::hp::FECollection< dim > fe_collection_lagrange
Lagrange basis used in strong form.
Definition: dg_base.hpp:1137
void build_facet_metric_operators(const unsigned int iface, 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 facet metric operators.
Definition: operators.cpp:2501
virtual std::array< real, nstate > physical_source_term(const dealii::Point< dim, real > &pos, const std::array< real, nstate > &solution, const std::array< dealii::Tensor< 1, dim, real >, nstate > &solution_gradient, const dealii::types::global_dof_index cell_index) const
Physical source term that does require differentiation.
Definition: physics.cpp:252
void assemble_face_term_auxiliary_equation(const unsigned int iface, const unsigned int neighbor_iface, const dealii::types::global_dof_index current_cell_index, const dealii::types::global_dof_index neighbor_cell_index, std::vector< bool > face_orientation_int, std::vector< bool > face_orientation_ext, const std::array< std::vector< adtype >, nstate > &soln_coeff_int, const std::array< std::vector< adtype >, nstate > &soln_coeff_ext, const unsigned int poly_degree_int, const unsigned int poly_degree_ext, OPERATOR::basis_functions< dim, 2 *dim > &soln_basis_int, OPERATOR::basis_functions< dim, 2 *dim > &soln_basis_ext, OPERATOR::metric_operators< adtype, dim, 2 *dim > &metric_oper_int, const Physics::PhysicsBase< dim, nspecies, nstate, adtype > &pde_physics, const NumericalFlux::NumericalFluxDissipative< dim, nspecies, nstate, adtype > &diss_num_flux, dealii::Tensor< 1, dim, std::vector< adtype >> &local_auxiliary_RHS_int, dealii::Tensor< 1, dim, std::vector< adtype >> &local_auxiliary_RHS_ext)
Evaluate the facet RHS for the auxiliary equation.
Definition: strong_dg.cpp:872
void apply_inverse_global_mass_matrix(const dealii::LinearAlgebra::distributed::Vector< double > &input_vector, dealii::LinearAlgebra::distributed::Vector< double > &output_vector, const bool use_auxiliary_eq=false)
Applies the inverse of the local metric dependent mass matrices when the global is not stored...
Definition: dg_base.cpp:4089
void assemble_volume_term_and_build_operators_ad_templated(typename dealii::DoFHandler< dim >::active_cell_iterator cell, const dealii::types::global_dof_index current_cell_index, const std::vector< adtype > &soln_coeffs, const dealii::Tensor< 1, dim, std::vector< adtype >> &aux_soln_coeffs, const std::vector< adtype > &metric_coeffs, const std::vector< real > &local_dual, const std::vector< dealii::types::global_dof_index > &soln_dofs_indices, const std::vector< dealii::types::global_dof_index > &metric_dofs_indices, const unsigned int poly_degree, const unsigned int grid_degree, Physics::PhysicsBase< dim, nspecies, nstate, adtype > &physics, OPERATOR::basis_functions< dim, 2 *dim > &soln_basis, OPERATOR::basis_functions< dim, 2 *dim > &flux_basis, OPERATOR::local_basis_stiffness< dim, 2 *dim > &flux_basis_stiffness, OPERATOR::vol_projection_operator< dim, 2 *dim > &soln_basis_projection_oper_int, OPERATOR::vol_projection_operator< dim, 2 *dim > &soln_basis_projection_oper_ext, OPERATOR::metric_operators< adtype, dim, 2 *dim > &metric_oper, OPERATOR::mapping_shape_functions< dim, 2 *dim > &mapping_basis, std::array< std::vector< adtype >, dim > &mapping_support_points, dealii::hp::FEValues< dim, dim > &, dealii::hp::FEValues< dim, dim > &, const dealii::FESystem< dim, dim > &, std::vector< adtype > &rhs, dealii::Tensor< 1, dim, std::vector< adtype >> &local_auxiliary_RHS, const bool compute_auxiliary_right_hand_side, adtype &dual_dot_residual)
Builds the necessary operators and assembles volume residual for either primary or auxiliary...
Definition: strong_dg.cpp:91
virtual std::array< real, nstate > evaluate_solution_flux(const std::array< real, nstate > &soln_int, const std::array< real, nstate > &soln_ext, const dealii::Tensor< 1, dim, real > &normal_int) const =0
Solution flux at the interface.
void assemble_volume_term_explicit(typename dealii::DoFHandler< dim >::active_cell_iterator cell, const dealii::types::global_dof_index current_cell_index, const dealii::FEValues< dim, dim > &fe_values_volume, const std::vector< dealii::types::global_dof_index > &current_dofs_indices, const std::vector< dealii::types::global_dof_index > &metric_dof_indices, const unsigned int poly_degree, const unsigned int grid_degree, dealii::Vector< real > &current_cell_rhs, const dealii::FEValues< dim, dim > &fe_values_lagrange)
Evaluate the integral over the cell volume.
Definition: strong_dg.cpp:3588
dealii::LinearAlgebra::distributed::Vector< double > artificial_dissipation_c0
Artificial dissipation coefficients.
Definition: dg_base.hpp:1196
void build_volume_metric_operators(const unsigned int poly_degree, const unsigned int grid_degree, const std::vector< adtype > &metric_coeffs, OPERATOR::metric_operators< adtype, dim, 2 *dim > &metric_oper, OPERATOR::mapping_shape_functions< dim, 2 *dim > &mapping_basis, std::array< std::vector< adtype >, dim > &mapping_support_points)
< Parallel std::cout that only outputs on mpi_rank==0
Definition: strong_dg.cpp:52
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
std::array< real, nstate > evaluate_flux(const std::array< real, nstate > &soln_int, const std::array< real, nstate > &soln_ext, const dealii::Tensor< 1, dim, real > &normal1) const
Returns the convective numerical flux at an interface.
virtual std::array< real, nstate > dissipative_flux_dot_normal(const std::array< real, nstate > &solution, const std::array< dealii::Tensor< 1, dim, real >, nstate > &solution_gradient, const std::array< real, nstate > &filtered_solution, const std::array< dealii::Tensor< 1, dim, real >, nstate > &filtered_solution_gradient, const bool on_boundary, const dealii::types::global_dof_index cell_index, const dealii::Tensor< 1, dim, real > &normal, const int boundary_type)
Dissipative fluxes dot normal vector.
Definition: physics.cpp:105
Base class from which Advection, Diffusion, ConvectionDiffusion, and Euler is derived.
Definition: physics.h:34
const dealii::FE_Q< dim > fe_q_artificial_dissipation
Continuous distribution of artificial dissipation.
Definition: dg_base.hpp:1190
virtual std::array< dealii::Tensor< 1, dim, real >, nstate > convective_flux(const std::array< real, nstate > &solution) const =0
Convective fluxes that will be differentiated once in space.
dealii::ConditionalOStream pcout
Parallel std::cout that only outputs on mpi_rank==0.
Definition: dg_base.hpp:1259
dealii::IndexSet ghost_dofs
Locally relevant ghost degrees of freedom.
Definition: dg_base.hpp:399
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
const dealii::UpdateFlags neighbor_face_update_flags
Update flags needed at neighbor&#39; face points.
Definition: dg_base.hpp:1219
PartialDifferentialEquation
Possible Partial Differential Equations to solve.
dealii::hp::QCollection< dim-1 > face_quadrature_collection
Quadrature used to evaluate face integrals.
Definition: dg_base.hpp:1133
Base class of numerical flux associated with dissipation.
Files for the baseline physics.
Definition: ADTypes.hpp:10
const bool has_nonzero_physical_source
Flag to signal that physical source term is non-zero.
Definition: physics.h:62
dealii::DoFHandler< dim > dof_handler_artificial_dissipation
Degrees of freedom handler for C0 artificial dissipation.
Definition: dg_base.hpp:1193
ManufacturedSolutionParam manufactured_solution_param
Associated manufactured solution parameters.
bool use_invariant_curl_form
Flag to use invariant curl form for metric cofactor operator.
dealii::QGauss< 0 > oneD_face_quadrature
1D surface quadrature is always one single point for all poly degrees.
Definition: dg_base.hpp:1157
std::shared_ptr< HighOrderGrid< dim, real, MeshType > > high_order_grid
High order grid that will provide the MappingFEField.
Definition: dg_base.hpp:1178
dealii::Tensor< 2, dim, std::vector< real > > metric_cofactor_surf
The facet metric cofactor matrix, for ONE face.
Definition: operators.h:1213
const int nstate
Number of state variables.
Definition: dg_base.hpp:96
void sum_factorized_Hadamard_sparsity_pattern(const unsigned int rows_size, const unsigned int columns_size, std::vector< std::array< unsigned int, dim >> &rows, std::vector< std::array< unsigned int, dim >> &columns)
Computes the rows and columns vectors with non-zero indices for sum-factorized Hadamard products...
Definition: operators.cpp:949
virtual std::array< real, nstate > source_term(const dealii::Point< dim, real > &pos, const std::array< real, nstate > &solution, const real current_time, const dealii::types::global_dof_index cell_index) const =0
Artificial dissipative fluxes that will be differentiated ONCE in space.
dealii::hp::QCollection< dim > volume_quadrature_collection
Finite Element Collection to represent the high-order grid.
Definition: dg_base.hpp:1131
void sum_factorized_Hadamard_surface_sparsity_pattern(const unsigned int rows_size, const unsigned int columns_size, std::vector< unsigned int > &rows, std::vector< unsigned int > &columns, const int dim_not_zero)
Computes the rows and columns vectors with non-zero indices for surface sum-factorized Hadamard produ...
Definition: operators.cpp:1067
virtual std::array< real, nstate > evaluate_auxiliary_flux(const dealii::types::global_dof_index current_cell_index, const dealii::types::global_dof_index neighbor_cell_index_, const real artificial_diss_coeff_int, const real artificial_diss_coeff_ext_, const std::array< real, nstate > &soln_int, const std::array< real, nstate > &soln_ext, const std::array< dealii::Tensor< 1, dim, real >, nstate > &soln_grad_int, const std::array< dealii::Tensor< 1, dim, real >, nstate > &soln_grad_ext_, const std::array< real, nstate > &filtered_soln_int, const std::array< real, nstate > &filtered_soln_ext, const std::array< dealii::Tensor< 1, dim, real >, nstate > &filtered_soln_grad_int, const std::array< dealii::Tensor< 1, dim, real >, nstate > &filtered_soln_grad_ext_, const dealii::Tensor< 1, dim, real > &normal_int, const real &penalty, const bool on_boundary, const int boundary_type=0) const =0
Auxiliary flux at the interface.
void divergence_matrix_vector_mult_1D(const dealii::Tensor< 1, dim, std::vector< real >> &input_vect, std::vector< real > &output_vect, const dealii::FullMatrix< double > &basis, const dealii::FullMatrix< double > &gradient_basis)
Computes the divergence using sum-factorization where the basis are the same in each direction...
Definition: operators.cpp:481
void matrix_vector_mult_surface_1D(const std::vector< bool > face_orientation, const unsigned int face_number, const std::vector< real > &input_vect, std::vector< real > &output_vect, const std::array< dealii::FullMatrix< double >, 2 > &basis_surf, const dealii::FullMatrix< double > &basis_vol, const bool adding=false, const double factor=1.0)
Apply sum-factorization matrix vector multiplication on a surface.
Definition: operators.cpp:414
Main parameter class that contains the various other sub-parameter classes.
void assemble_auxiliary_residual(const bool compute_dRdW, const bool compute_dRdX, const bool compute_d2R)
Flag for using projected entropy variables for NSFR boundary term.
Definition: strong_dg.cpp:498
void transform_reference_to_physical(const dealii::Tensor< 1, dim, real > &ref, const dealii::Tensor< 2, dim, real > &metric_cofactor, dealii::Tensor< 1, dim, real > &phys)
Given a reference tensor, return the physical tensor.
Definition: operators.cpp:2368
unsigned int current_degree
Stores the degree of the current poly degree.
Definition: operators.h:416
virtual std::array< dealii::Tensor< 1, dim, real >, nstate > convert_primitive_gradient_to_conservative_gradient(const std::array< real, nstate > &primitive_soln, const std::array< dealii::Tensor< 1, dim, real >, nstate > &primitive_soln_gradient) const =0
const bool using_wall_model
Flag for using wall model.
Definition: strong_dg.hpp:34
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
ManufacturedConvergenceStudyParam manufactured_convergence_study_param
Contains parameters for manufactured convergence study.
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
virtual std::array< real, nstate > convert_conservative_to_primitive(const std::array< real, nstate > &conservative_soln) const =0
Convert conservative variables to primitive variables.
DGStrong class templated on the number of state variables.
Definition: strong_dg.hpp:16
ODESolverParam ode_solver_param
Contains parameters for ODE solver.
bool use_auxiliary_eq
Flag for using the auxiliary equation.
Definition: dg_base.hpp:1305
dealii::Vector< double > cell_volume
Time it takes for the maximum wavespeed to cross the cell domain.
Definition: dg_base.hpp:454
void Hadamard_product_AD_vector(const dealii::FullMatrix< double > &input_mat1, const std::vector< real > &input_mat2, std::vector< real > &output_mat)
Computes a single Hadamard product for AD type.
Definition: operators.cpp:931
const Parameters::AllParameters *const all_parameters
Pointer to all parameters.
Definition: dg_base.hpp:91
virtual std::array< real, nstate > compute_entropy_variables(const std::array< real, nstate > &conservative_soln) const =0
Computes the entropy variables.
virtual void boundary_face_values_viscous_flux(const int, const dealii::Point< dim, real > &, const dealii::Tensor< 1, dim, real > &, const std::array< real, nstate > &, const std::array< dealii::Tensor< 1, dim, real >, nstate > &, const std::array< real, nstate > &, const std::array< dealii::Tensor< 1, dim, real >, nstate > &, std::array< real, nstate > &, std::array< dealii::Tensor< 1, dim, real >, nstate > &) const
Evaluates boundary values and gradients on the other side of the face for the viscous flux...
Definition: physics.cpp:230
MPI_Comm mpi_communicator
MPI communicator.
Definition: dg_base.hpp:1258
void sum_factorized_Hadamard_basis_assembly(const unsigned int rows_size_1D, const unsigned int columns_size_1D, const std::vector< std::array< unsigned int, dim >> &rows, const std::vector< std::array< unsigned int, dim >> &columns, const dealii::FullMatrix< double > &basis, const std::vector< double > &weights, std::array< dealii::FullMatrix< double >, dim > &basis_sparse)
Constructs the basis operator storing all non-zero entries for a "sum-factorized" Hadamard product...
Definition: operators.cpp:1014
dealii::IndexSet locally_owned_dofs
Locally own degrees of freedom.
Definition: dg_base.hpp:398
void assemble_volume_term_auxiliary_equation(const std::array< std::vector< adtype >, nstate > &soln_coeff, const unsigned int poly_degree, OPERATOR::basis_functions< dim, 2 *dim > &soln_basis, OPERATOR::basis_functions< dim, 2 *dim > &flux_basis, OPERATOR::metric_operators< adtype, dim, 2 *dim > &metric_oper, dealii::Tensor< 1, dim, std::vector< adtype >> &local_auxiliary_RHS)
Evaluate the volume RHS for the auxiliary equation.
Definition: strong_dg.cpp:667
Base metric operators class that stores functions used in both the volume and on surface.
Definition: operators.h:1131
std::array< dealii::LinearAlgebra::distributed::Vector< double >, dim > auxiliary_solution
The auxiliary equations&#39; solution.
Definition: dg_base.hpp:419
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
Base class of numerical flux associated with convection.
dealii::Vector< double > max_dt_cell
Time it takes for the maximum wavespeed to cross the cell domain.
Definition: dg_base.hpp:461
bool use_manufactured_source_term
Uses non-zero source term based on the manufactured solution and the PDE.
virtual std::array< real, nstate > compute_conservative_variables_from_entropy_variables(const std::array< real, nstate > &entropy_var) const =0
Computes the conservative variables from the entropy variables.
bool use_split_form
Flag to use split form.
virtual std::array< dealii::Tensor< 1, dim, real >, nstate > convert_conservative_gradient_to_primitive_gradient(const std::array< real, nstate > &conservative_soln, const std::array< dealii::Tensor< 1, dim, real >, nstate > &conservative_soln_gradient) const =0
void assemble_boundary_term_and_build_operators_ad_templated(typename dealii::DoFHandler< dim >::active_cell_iterator cell, const dealii::types::global_dof_index current_cell_index, const std::vector< adtype > &soln_coeffs, const dealii::Tensor< 1, dim, std::vector< adtype >> &aux_soln_coeffs, const std::vector< adtype > &metric_coeffs, const std::vector< real > &local_dual, const unsigned int face_number, const unsigned int boundary_id, Physics::PhysicsBase< dim, nspecies, nstate, adtype > &physics, const NumericalFlux::NumericalFluxConvective< dim, nspecies, nstate, adtype > &conv_num_flux, const NumericalFlux::NumericalFluxDissipative< dim, nspecies, nstate, adtype > &diss_num_flux, const unsigned int poly_degree, const unsigned int grid_degree, OPERATOR::basis_functions< dim, 2 *dim > &soln_basis, OPERATOR::basis_functions< dim, 2 *dim > &flux_basis, OPERATOR::vol_projection_operator< dim, 2 *dim > &soln_basis_projection_oper_int, OPERATOR::metric_operators< adtype, dim, 2 *dim > &metric_oper, OPERATOR::mapping_shape_functions< dim, 2 *dim > &mapping_basis, std::array< std::vector< adtype >, dim > &mapping_support_points, dealii::hp::FEFaceValues< dim, dim > &, const dealii::FESystem< dim, dim > &, const real penalty, std::vector< adtype > &rhs, dealii::Tensor< 1, dim, std::vector< adtype >> &local_auxiliary_RHS, const bool compute_auxiliary_right_hand_side, adtype &dual_dot_residual)
Builds the necessary operators and assembles boundary residual for either primary or auxiliary...
Definition: strong_dg.cpp:192
std::array< dealii::FullMatrix< double >, 2 > oneD_surf_operator
Stores the one dimensional surface operator.
Definition: operators.h:385
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
DGStrong(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.
Definition: strong_dg.cpp:35
virtual void boundary_face_values(const int, const dealii::Point< dim, real > &, const dealii::Tensor< 1, dim, real > &, const std::array< real, nstate > &, const std::array< dealii::Tensor< 1, dim, real >, nstate > &, const std::array< real, nstate > &, const std::array< dealii::Tensor< 1, dim, real >, nstate > &, std::array< real, nstate > &, std::array< dealii::Tensor< 1, dim, real >, nstate > &) const
Evaluates boundary values and gradients on the other side of the face for the convective flux...
Definition: physics.cpp:208
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
void inner_product_1D(const std::vector< real > &input_vect, const std::vector< double > &weight_vect, std::vector< real > &output_vect, const dealii::FullMatrix< double > &basis_x, const bool adding=false, const double factor=1.0)
Apply the inner product operation using the 1D operator in each direction.
Definition: operators.cpp:640
void transform_physical_to_reference(const dealii::Tensor< 1, dim, real > &phys, const dealii::Tensor< 2, dim, real > &metric_cofactor, dealii::Tensor< 1, dim, real > &ref)
Given a physical tensor, return the reference tensor.
Definition: operators.cpp:2354
void assemble_boundary_term_strong(typename dealii::DoFHandler< dim >::active_cell_iterator current_cell, const unsigned int iface, const dealii::types::global_dof_index current_cell_index, std::vector< bool > face_orientation, const std::array< std::vector< adtype >, nstate > &soln_coeff, const std::array< dealii::Tensor< 1, dim, std::vector< adtype >>, nstate > &aux_soln_coeff, const unsigned int boundary_id, const unsigned int poly_degree, const real penalty, OPERATOR::basis_functions< dim, 2 *dim > &soln_basis, OPERATOR::basis_functions< dim, 2 *dim > &flux_basis, OPERATOR::vol_projection_operator< dim, 2 *dim > &soln_basis_projection_oper, OPERATOR::metric_operators< adtype, dim, 2 *dim > &metric_oper, Physics::PhysicsBase< dim, nspecies, nstate, adtype > &pde_physics, const NumericalFlux::NumericalFluxConvective< dim, nspecies, nstate, adtype > &conv_num_flux, const NumericalFlux::NumericalFluxDissipative< dim, nspecies, nstate, adtype > &diss_num_flux, std::vector< adtype > &local_rhs_cell)
Strong form primary equation&#39;s boundary right-hand-side.
Definition: strong_dg.cpp:1583
dealii::LinearAlgebra::distributed::Vector< double > right_hand_side
Residual of the current solution.
Definition: dg_base.hpp:396
dealii::hp::QCollection< 1 > oneD_quadrature_collection
1D quadrature to generate Lagrange polynomials for the sake of flux interpolation.
Definition: dg_base.hpp:1155
const dealii::UpdateFlags volume_update_flags
Update flags needed at volume points.
Definition: dg_base.hpp:1212
dealii::FullMatrix< double > oneD_grad_operator
Stores the one dimensional gradient operator.
Definition: operators.h:388
std::array< dealii::LinearAlgebra::distributed::Vector< double >, dim > auxiliary_right_hand_side
The auxiliary equations&#39; right hand sides.
Definition: dg_base.hpp:416
void inner_product_surface_1D(const std::vector< bool > face_orientation, const unsigned int face_number, const std::vector< real > &input_vect, const std::vector< double > &weight_vect, std::vector< real > &output_vect, const std::array< dealii::FullMatrix< double >, 2 > &basis_surf, const dealii::FullMatrix< double > &basis_vol, const bool adding=false, const double factor=1.0)
Apply sum-factorization inner product on a surface.
Definition: operators.cpp:445
dealii::LinearAlgebra::distributed::Vector< double > solution
Current modal coefficients of the solution.
Definition: dg_base.hpp:409
Abstract class templated on the number of state variables.
dealii::LinearAlgebra::distributed::Vector< real > dual
Current optimization dual variables corresponding to the residual constraints also known as the adjoi...
Definition: dg_base.hpp:483
bool use_inverse_mass_on_the_fly
Flag to use inverse mass matrix on-the-fly for explicit solves.
ODESolverEnum ode_solver_type
ODE solver type.
void assemble_face_term_and_build_operators_ad_templated(typename dealii::DoFHandler< dim >::active_cell_iterator cell, typename dealii::DoFHandler< dim >::active_cell_iterator neighbor_cell, const dealii::types::global_dof_index current_cell_index, const dealii::types::global_dof_index neighbor_cell_index, const unsigned int iface, const unsigned int neighbor_iface, const std::vector< adtype > &soln_coeffs_int, const std::vector< adtype > &soln_coeffs_ext, const dealii::Tensor< 1, dim, std::vector< adtype >> &aux_soln_coeffs_int, const dealii::Tensor< 1, dim, std::vector< adtype >> &aux_soln_coeffs_ext, const std::vector< adtype > &metric_coeff_int, const std::vector< adtype > &metric_coeff_ext, const std::vector< double > &dual_int, const std::vector< double > &dual_ext, const unsigned int poly_degree_int, const unsigned int poly_degree_ext, const unsigned int grid_degree_int, const unsigned int grid_degree_ext, OPERATOR::basis_functions< dim, 2 *dim > &soln_basis_int, OPERATOR::basis_functions< dim, 2 *dim > &soln_basis_ext, OPERATOR::basis_functions< dim, 2 *dim > &flux_basis_int, OPERATOR::basis_functions< dim, 2 *dim > &flux_basis_ext, OPERATOR::local_basis_stiffness< dim, 2 *dim > &flux_basis_stiffness, OPERATOR::vol_projection_operator< dim, 2 *dim > &soln_basis_projection_oper_int, OPERATOR::vol_projection_operator< dim, 2 *dim > &soln_basis_projection_oper_ext, OPERATOR::metric_operators< adtype, dim, 2 *dim > &metric_oper_int, OPERATOR::metric_operators< adtype, dim, 2 *dim > &metric_oper_ext, OPERATOR::mapping_shape_functions< dim, 2 *dim > &mapping_basis, std::array< std::vector< adtype >, dim > &mapping_support_points, Physics::PhysicsBase< dim, nspecies, nstate, adtype > &physics, const NumericalFlux::NumericalFluxConvective< dim, nspecies, nstate, adtype > &conv_num_flux, const NumericalFlux::NumericalFluxDissipative< dim, nspecies, nstate, adtype > &diss_num_flux, dealii::hp::FEFaceValues< dim, dim > &, dealii::hp::FEFaceValues< dim, dim > &, dealii::hp::FESubfaceValues< dim, dim > &, const dealii::FESystem< dim, dim > &, const dealii::FESystem< dim, dim > &, const real penalty, std::vector< adtype > &rhs_int, std::vector< adtype > &rhs_ext, dealii::Tensor< 1, dim, std::vector< adtype >> &aux_rhs_int, dealii::Tensor< 1, dim, std::vector< adtype >> &aux_rhs_ext, const bool compute_auxiliary_right_hand_side, adtype &dual_dot_residual, const bool, const unsigned int)
Calls the function to assemble face residual.
Definition: strong_dg.cpp:296
const unsigned int max_degree
Maximum degree used for p-refi1nement.
Definition: dg_base.hpp:104
real current_time
The current time set in set_current_time()
Definition: dg_base.hpp:1188
dealii::FullMatrix< double > oneD_skew_symm_vol_oper
Skew-symmetric volume operator .
Definition: operators.h:519
std::array< dealii::Tensor< 1, dim, std::vector< real > >, n_faces > flux_nodes_surf
Stores the physical facet flux nodes.
Definition: operators.h:1228
const dealii::UpdateFlags face_update_flags
Update flags needed at face points.
Definition: dg_base.hpp:1215
bool add_artificial_dissipation
Flag to add artificial dissipation from Persson&#39;s shock capturing paper.
void sum_factorized_Hadamard_surface_basis_assembly(const unsigned int rows_size, const unsigned int columns_size_1D, const std::vector< unsigned int > &rows, const std::vector< unsigned int > &columns, const dealii::FullMatrix< double > &basis, const std::vector< double > &weights, dealii::FullMatrix< double > &basis_sparse, const int dim_not_zero)
Constructs the basis operator storing all non-zero entries for a "sum-factorized" surface Hadamard p...
Definition: operators.cpp:1135
const unsigned int max_grid_degree
Maximum grid degree used for hp-refi1nement.
Definition: dg_base.hpp:109
const unsigned int poly_degree_max_large_scales
For filtered solution; lower bound of high pass filter.
Definition: strong_dg.hpp:33
void assemble_volume_term_strong(typename dealii::DoFHandler< dim >::active_cell_iterator cell, const dealii::types::global_dof_index current_cell_index, const std::array< std::vector< adtype >, nstate > &soln_coeff, const std::array< dealii::Tensor< 1, dim, std::vector< adtype >>, nstate > &aux_soln_coeff, const unsigned int poly_degree, OPERATOR::basis_functions< dim, 2 *dim > &soln_basis, OPERATOR::basis_functions< dim, 2 *dim > &flux_basis, OPERATOR::local_basis_stiffness< dim, 2 *dim > &flux_basis_stiffness, OPERATOR::vol_projection_operator< dim, 2 *dim > &soln_basis_projection_oper, OPERATOR::metric_operators< adtype, dim, 2 *dim > &metric_oper, Physics::PhysicsBase< dim, nspecies, nstate, adtype > &pde_physics, std::vector< adtype > &local_rhs_int_cell)
Strong form primary equation&#39;s volume right-hand-side.
Definition: strong_dg.cpp:1017
void assemble_boundary_term_auxiliary_equation(const unsigned int iface, const dealii::types::global_dof_index current_cell_index, std::vector< bool > face_orientation, const std::array< std::vector< adtype >, nstate > &soln_coeff, const unsigned int poly_degree, const unsigned int boundary_id, OPERATOR::basis_functions< dim, 2 *dim > &soln_basis, OPERATOR::metric_operators< adtype, dim, 2 *dim > &metric_oper, const Physics::PhysicsBase< dim, nspecies, nstate, adtype > &pde_physics, const NumericalFlux::NumericalFluxDissipative< dim, nspecies, nstate, adtype > &diss_num_flux, dealii::Tensor< 1, dim, std::vector< adtype >> &local_auxiliary_RHS)
Evaluate the boundary RHS for the auxiliary equation.
Definition: strong_dg.cpp:728
std::shared_ptr< Triangulation > triangulation
Mesh.
Definition: dg_base.hpp:160
void allocate_dual_vector(const bool compute_d2R)
Allocate the dual vector for optimization.
Definition: strong_dg.cpp:3604
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
ArtificialDissipationParam artificial_dissipation_param
Contains parameters for artificial dissipation.
virtual std::array< real, nstate > convert_primitive_to_conservative(const std::array< real, nstate > &primitive_soln) const =0
Convert primitive solution to conservative solution.
void build_1D_surface_operator(const dealii::FESystem< 1, 1 > &finite_element, const dealii::Quadrature< 0 > &quadrature)
Assembles the one dimensional operator.
Definition: operators.cpp:1257
unsigned int current_degree
Stores the degree of the current poly degree.
Definition: operators.h:1081
dealii::Tensor< 1, dim, std::vector< real > > flux_nodes_vol
Stores the physical volume flux nodes.
Definition: operators.h:1225
virtual std::array< dealii::Tensor< 1, dim, real >, nstate > convective_numerical_split_flux(const std::array< real, nstate > &conservative_soln1, const std::array< real, nstate > &conservative_soln2) const
Convective Numerical Split Flux for split form.
Definition: physics.cpp:66
virtual std::array< dealii::Tensor< 1, dim, real >, nstate > dissipative_flux(const std::array< real, nstate > &solution, const std::array< dealii::Tensor< 1, dim, real >, nstate > &solution_gradient, const std::array< real, nstate > &filtered_solution, const std::array< dealii::Tensor< 1, dim, real >, nstate > &filtered_solution_gradient, const dealii::types::global_dof_index cell_index)
Dissipative fluxes that will be differentiated ONCE in space.
Definition: physics.cpp:128
Projection operator corresponding to basis functions onto M-norm (L2).
Definition: operators.h:723
Local stiffness matrix without jacobian dependence.
Definition: operators.h:497
real evaluate_CFL(std::vector< std::array< real, nstate > > soln_at_q, const real artificial_dissipation, const real cell_diameter, const unsigned int cell_degree)
Evaluate the time it takes for the maximum wavespeed to cross the cell domain.
dealii::TrilinosWrappers::SparseMatrix global_inverse_mass_matrix_auxiliary
Global inverse of the auxiliary mass matrix.
Definition: dg_base.hpp:339