[P]arallel [Hi]gh-order [Li]brary for [P]DEs  Latest
Parallel High-Order Library for PDEs through hp-adaptive Discontinuous Galerkin methods
euler_split_inviscid_taylor_green_vortex.cpp
1 #include <fstream>
2 #include "dg/dg_factory.hpp"
3 #include "euler_split_inviscid_taylor_green_vortex.h"
4 #include "physics/initial_conditions/set_initial_condition.h"
5 #include "physics/initial_conditions/initial_condition_function.h"
6 #include "mesh/grids/nonsymmetric_curved_periodic_grid.hpp"
7 #include "mesh/grids/straight_periodic_cube.hpp"
8 
9 namespace PHiLiP {
10 namespace Tests {
11 
12 template <int dim, int nstate>
14  : TestsBase::TestsBase(parameters_input)
15 {}
16 template<int dim, int nstate>
17 std::array<double,2> EulerTaylorGreen<dim, nstate>::compute_change_in_entropy(const std::shared_ptr < DGBase<dim, double> > &dg, unsigned int poly_degree) const
18 {
19  const unsigned int n_dofs_cell = dg->fe_collection[poly_degree].dofs_per_cell;
20  const unsigned int n_quad_pts = dg->volume_quadrature_collection[poly_degree].size();
21  const unsigned int n_shape_fns = n_dofs_cell / nstate;
22  //We have to project the vector of entropy variables because the mass matrix has an interpolation from solution nodes built into it.
23  OPERATOR::vol_projection_operator<dim,2*dim,double> vol_projection(1, poly_degree, dg->max_grid_degree);
24  vol_projection.build_1D_volume_operator(dg->oneD_fe_collection_1state[poly_degree], dg->oneD_quadrature_collection[poly_degree]);
25 
26  OPERATOR::basis_functions<dim,2*dim,double> soln_basis(1, poly_degree, dg->max_grid_degree);
27  soln_basis.build_1D_volume_operator(dg->oneD_fe_collection_1state[poly_degree], dg->oneD_quadrature_collection[poly_degree]);
28 
29  dealii::LinearAlgebra::distributed::Vector<double> entropy_var_hat_global(dg->right_hand_side);
30  dealii::LinearAlgebra::distributed::Vector<double> energy_var_hat_global(dg->right_hand_side);
31  std::vector<dealii::types::global_dof_index> dofs_indices (n_dofs_cell);
32 
33  std::shared_ptr < Physics::Euler<dim, nstate, double > > euler_double = std::dynamic_pointer_cast<Physics::Euler<dim,dim+2,double>>(PHiLiP::Physics::PhysicsFactory<dim,nstate,double>::create_Physics(dg->all_parameters));
34 
35  for (auto cell = dg->dof_handler.begin_active(); cell!=dg->dof_handler.end(); ++cell) {
36  if (!cell->is_locally_owned()) continue;
37  cell->get_dof_indices (dofs_indices);
38 
39  //get solution modal coeff
40  std::array<std::vector<double>,nstate> soln_coeff;
41  for(unsigned int idof=0; idof<n_dofs_cell; idof++){
42  const unsigned int istate = dg->fe_collection[poly_degree].system_to_component_index(idof).first;
43  const unsigned int ishape = dg->fe_collection[poly_degree].system_to_component_index(idof).second;
44  if(ishape == 0)
45  soln_coeff[istate].resize(n_shape_fns);
46  soln_coeff[istate][ishape] = dg->solution(dofs_indices[idof]);
47  }
48 
49  //interpolate solution to quadrature points
50  std::array<std::vector<double>,nstate> soln_at_q;
51  for(int istate=0; istate<nstate; istate++){
52  soln_at_q[istate].resize(n_quad_pts);
53  soln_basis.matrix_vector_mult_1D(soln_coeff[istate], soln_at_q[istate],
54  soln_basis.oneD_vol_operator);
55  }
56  //compute entropy and kinetic energy "entropy" variables at quad points
57  std::array<std::vector<double>,nstate> entropy_var_at_q;
58  std::array<std::vector<double>,nstate> energy_var_at_q;
59  for(unsigned int iquad=0; iquad<n_quad_pts; iquad++){
60  std::array<double,nstate> soln_state;
61  for(int istate=0; istate<nstate; istate++){
62  soln_state[istate] = soln_at_q[istate][iquad];
63  }
64  std::array<double,nstate> entropy_var_state = euler_double->compute_entropy_variables(soln_state);
65  std::array<double,nstate> kin_energy_state = euler_double->compute_kinetic_energy_variables(soln_state);
66  for(int istate=0; istate<nstate; istate++){
67  if(iquad==0){
68  entropy_var_at_q[istate].resize(n_quad_pts);
69  energy_var_at_q[istate].resize(n_quad_pts);
70  }
71  energy_var_at_q[istate][iquad] = kin_energy_state[istate];
72  entropy_var_at_q[istate][iquad] = entropy_var_state[istate];
73  }
74  }
75  //project the enrtopy and KE var to modal coefficients
76  //then write it into a global vector
77  for(int istate=0; istate<nstate; istate++){
78  //Projected vector of entropy variables.
79  std::vector<double> entropy_var_hat(n_shape_fns);
80  vol_projection.matrix_vector_mult_1D(entropy_var_at_q[istate], entropy_var_hat,
81  vol_projection.oneD_vol_operator);
82  std::vector<double> energy_var_hat(n_shape_fns);
83  vol_projection.matrix_vector_mult_1D(energy_var_at_q[istate], energy_var_hat,
84  vol_projection.oneD_vol_operator);
85 
86  for(unsigned int ishape=0; ishape<n_shape_fns; ishape++){
87  const unsigned int idof = istate * n_shape_fns + ishape;
88  entropy_var_hat_global[dofs_indices[idof]] = entropy_var_hat[ishape];
89  energy_var_hat_global[dofs_indices[idof]] = energy_var_hat[ishape];
90  }
91  }
92  }
93 
94  //evaluate the change in entropy and change in KE
95  dg->assemble_residual();
96  std::array<double,2> change_entropy_and_energy;
97  change_entropy_and_energy[0] = entropy_var_hat_global * dg->right_hand_side;
98  change_entropy_and_energy[1] = energy_var_hat_global * dg->right_hand_side;
99  return change_entropy_and_energy;
100 }
101 template<int dim, int nstate>
102 double EulerTaylorGreen<dim, nstate>::compute_volume_term(const std::shared_ptr < DGBase<dim, double> > &dg, unsigned int poly_degree) const
103 {
104  const unsigned int n_dofs_cell = dg->fe_collection[poly_degree].dofs_per_cell;
105  const unsigned int n_quad_pts = dg->volume_quadrature_collection[poly_degree].size();
106  const unsigned int n_shape_fns = n_dofs_cell / nstate;
107  const unsigned int grid_degree = dg->high_order_grid->fe_system.tensor_degree();
108 
109  OPERATOR::basis_functions<dim,2*dim,double> soln_basis(1, poly_degree, dg->max_grid_degree);
110  soln_basis.build_1D_volume_operator(dg->oneD_fe_collection_1state[poly_degree], dg->oneD_quadrature_collection[poly_degree]);
111  soln_basis.build_1D_gradient_operator(dg->oneD_fe_collection_1state[poly_degree], dg->oneD_quadrature_collection[poly_degree]);
112  soln_basis.build_1D_surface_operator(dg->oneD_fe_collection_1state[poly_degree], dg->oneD_face_quadrature);
113 
114  OPERATOR::basis_functions<dim,2*dim,double> flux_basis(1, poly_degree, dg->max_grid_degree);
115  flux_basis.build_1D_volume_operator(dg->oneD_fe_collection_flux[poly_degree], dg->oneD_quadrature_collection[poly_degree]);
116  flux_basis.build_1D_gradient_operator(dg->oneD_fe_collection_flux[poly_degree], dg->oneD_quadrature_collection[poly_degree]);
117  flux_basis.build_1D_surface_operator(dg->oneD_fe_collection_flux[poly_degree], dg->oneD_face_quadrature);
118 
119  OPERATOR::local_basis_stiffness<dim,2*dim,double> flux_basis_stiffness(1, poly_degree, dg->max_grid_degree, true);
120  //flux basis stiffness operator for skew-symmetric form
121  flux_basis_stiffness.build_1D_volume_operator(dg->oneD_fe_collection_flux[poly_degree], dg->oneD_quadrature_collection[poly_degree]);
122 
123  OPERATOR::mapping_shape_functions<dim,2*dim,double> mapping_basis(1, poly_degree, grid_degree);
124  mapping_basis.build_1D_shape_functions_at_grid_nodes(dg->high_order_grid->oneD_fe_system, dg->high_order_grid->oneD_grid_nodes);
125  mapping_basis.build_1D_shape_functions_at_flux_nodes(dg->high_order_grid->oneD_fe_system, dg->oneD_quadrature_collection[poly_degree], dg->oneD_face_quadrature);
126  const std::vector<double> &oneD_vol_quad_weights = dg->oneD_quadrature_collection[poly_degree].get_weights();
127 
128  OPERATOR::vol_projection_operator<dim,2*dim,double> vol_projection(1, poly_degree, dg->max_grid_degree);
129  vol_projection.build_1D_volume_operator(dg->oneD_fe_collection_1state[poly_degree], dg->oneD_quadrature_collection[poly_degree]);
130 
131  std::vector<dealii::types::global_dof_index> dofs_indices (n_dofs_cell);
132 
133  std::shared_ptr < Physics::Euler<dim, nstate, double > > euler_double = std::dynamic_pointer_cast<Physics::Euler<dim,dim+2,double>>(PHiLiP::Physics::PhysicsFactory<dim,nstate,double>::create_Physics(dg->all_parameters));
134 
135  double volume_term = 0.0;
136  auto metric_cell = dg->high_order_grid->dof_handler_grid.begin_active();
137  for (auto cell = dg->dof_handler.begin_active(); cell!= dg->dof_handler.end(); ++cell, ++metric_cell) {
138  if (!cell->is_locally_owned()) continue;
139  cell->get_dof_indices (dofs_indices);
140 
141  // We first need to extract the mapping support points (grid nodes) from high_order_grid.
142  const dealii::FESystem<dim> &fe_metric = dg->high_order_grid->fe_system;
143  const unsigned int n_metric_dofs = fe_metric.dofs_per_cell;
144  const unsigned int n_grid_nodes = n_metric_dofs / dim;
145  std::vector<dealii::types::global_dof_index> metric_dof_indices(n_metric_dofs);
146  metric_cell->get_dof_indices (metric_dof_indices);
147  std::array<std::vector<double>,dim> mapping_support_points;
148  for(int idim=0; idim<dim; idim++){
149  mapping_support_points[idim].resize(n_grid_nodes);
150  }
151  // Get the mapping support points (physical grid nodes) from high_order_grid.
152  // Store it in such a way we can use sum-factorization on it with the mapping basis functions.
153  const std::vector<unsigned int > &index_renumbering = dealii::FETools::hierarchic_to_lexicographic_numbering<dim>(grid_degree);
154  for (unsigned int idof = 0; idof< n_metric_dofs; ++idof) {
155  const double val = (dg->high_order_grid->volume_nodes[metric_dof_indices[idof]]);
156  const unsigned int istate = fe_metric.system_to_component_index(idof).first;
157  const unsigned int ishape = fe_metric.system_to_component_index(idof).second;
158  const unsigned int igrid_node = index_renumbering[ishape];
159  mapping_support_points[istate][igrid_node] = val;
160  }
161  // Construct the metric operators.
162  OPERATOR::metric_operators<double, dim, 2*dim> metric_oper(nstate, poly_degree, grid_degree, false, false);
163  // Build the metric terms to compute the gradient and volume node positions.
164  // This functions will compute the determinant of the metric Jacobian and metric cofactor matrix.
165  // If flags store_vol_flux_nodes and store_surf_flux_nodes set as true it will also compute the physical quadrature positions.
166  metric_oper.build_volume_metric_operators(
167  n_quad_pts, n_grid_nodes,
168  mapping_support_points,
169  mapping_basis,
170  dg->all_parameters->use_invariant_curl_form);
171 
172 
173 
174  std::array<std::vector<double>,nstate> soln_coeff;
175  for(unsigned int idof=0; idof<n_dofs_cell; idof++){
176  const unsigned int istate = dg->fe_collection[poly_degree].system_to_component_index(idof).first;
177  const unsigned int ishape = dg->fe_collection[poly_degree].system_to_component_index(idof).second;
178  if(ishape == 0)
179  soln_coeff[istate].resize(n_shape_fns);
180  soln_coeff[istate][ishape] = dg->solution(dofs_indices[idof]);
181  }
182 
183  std::array<std::vector<double>,nstate> soln_at_q;
184  std::array<std::vector<double>,dim> vel_at_q;
185  for(int istate=0; istate<nstate; istate++){
186  soln_at_q[istate].resize(n_quad_pts);
187  // Interpolate soln coeff to volume cubature nodes.
188  soln_basis.matrix_vector_mult_1D(soln_coeff[istate], soln_at_q[istate],
189  soln_basis.oneD_vol_operator);
190  }
191  //get volume entropy var and interp to face
192  std::array<std::vector<double>,nstate> energy_var_vol_int;
193  for(unsigned int iquad=0; iquad<n_quad_pts; iquad++){
194  std::array<double,nstate> soln_state;
195  for(int istate=0; istate<nstate; istate++){
196  soln_state[istate] = soln_at_q[istate][iquad];
197  }
198  std::array<double,nstate> energy_var;
199  energy_var = euler_double->compute_kinetic_energy_variables(soln_state);
200  for(int istate=0; istate<nstate; istate++){
201  if(iquad==0){
202  energy_var_vol_int[istate].resize(n_quad_pts);
203  }
204  energy_var_vol_int[istate][iquad] = energy_var[istate];
205  }
206  }
207 
208  std::array<std::vector<double>,nstate> energy_var_hat;
209  for(int istate=0; istate<nstate; istate++){
210  //Projected vector of entropy variables.
211  energy_var_hat[istate].resize(n_shape_fns);
212  vol_projection.matrix_vector_mult_1D(energy_var_vol_int[istate], energy_var_hat[istate],
213  vol_projection.oneD_vol_operator);
214  }
215  // The matrix of two-pt fluxes for Hadamard products
216  std::array<dealii::Tensor<1,dim,dealii::FullMatrix<double>>,nstate> conv_ref_2pt_flux_at_q;
217 
218  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
219  //extract soln and auxiliary soln at quad pt to be used in physics
220  std::array<double,nstate> soln_state;
221  for(int istate=0; istate<nstate; istate++){
222  soln_state[istate] = soln_at_q[istate][iquad];
223  }
224 
225  // Copy Metric Cofactor in a way can use for transforming Tensor Blocks to reference space
226  // The way it is stored in metric_operators is to use sum-factorization in each direction,
227  // but here it is cleaner to apply a reference transformation in each Tensor block returned by physics.
228  dealii::Tensor<2,dim,double> metric_cofactor;
229  for(int idim=0; idim<dim; idim++){
230  for(int jdim=0; jdim<dim; jdim++){
231  metric_cofactor[idim][jdim] = metric_oper.metric_cofactor_vol[idim][jdim][iquad];
232  }
233  }
234 
235  // Evaluate physical convective flux
236  // If 2pt flux, transform to reference at construction to improve performance.
237  // We technically use a REFERENCE 2pt flux for all entropy stable schemes.
238  std::array<dealii::Tensor<1,dim,double>,nstate> conv_phys_flux_2pt;
239  std::vector<std::array<dealii::Tensor<1,dim,double>,nstate>> conv_ref_flux_2pt(n_quad_pts);
240  for (unsigned int flux_basis=iquad; flux_basis<n_quad_pts; ++flux_basis) {
241 
242  // Copy Metric Cofactor in a way can use for transforming Tensor Blocks to reference space
243  // The way it is stored in metric_operators is to use sum-factorization in each direction,
244  // but here it is cleaner to apply a reference transformation in each Tensor block returned by physics.
245  dealii::Tensor<2,dim,double> metric_cofactor_flux_basis;
246  for(int idim=0; idim<dim; idim++){
247  for(int jdim=0; jdim<dim; jdim++){
248  metric_cofactor_flux_basis[idim][jdim] = metric_oper.metric_cofactor_vol[idim][jdim][flux_basis];
249  }
250  }
251  std::array<double,nstate> soln_state_flux_basis;
252  for(int istate=0; istate<nstate; istate++){
253  soln_state_flux_basis[istate] = soln_at_q[istate][flux_basis];
254  }
255  //Compute the physical flux
256  conv_phys_flux_2pt = euler_double->convective_numerical_split_flux(soln_state, soln_state_flux_basis);
257 
258  //Need to subtract off the pressure average term
259  const double pressure_int = euler_double->compute_pressure(soln_state);
260  const double pressure_ext = euler_double->compute_pressure(soln_state_flux_basis);
261  for(int idim=0; idim<dim; idim++){
262  conv_phys_flux_2pt[1+idim][idim] -= 0.5*(pressure_int + pressure_ext);
263  }
264 
265 
266  for(int istate=0; istate<nstate; istate++){
267  //For each state, transform the physical flux to a reference flux.
269  conv_phys_flux_2pt[istate],
270  0.5*(metric_cofactor + metric_cofactor_flux_basis),
271  conv_ref_flux_2pt[flux_basis][istate]);
272  }
273  }
274 
275  //Write the values in a way that we can use sum-factorization on.
276  for(int istate=0; istate<nstate; istate++){
277  //Write the data in a way that we can use sum-factorization on.
278  //Since sum-factorization improves the speed for matrix-vector multiplications,
279  //We need the values to have their inner elements be vectors.
280  for(int idim=0; idim<dim; idim++){
281  //allocate
282  if(iquad == 0){
283  conv_ref_2pt_flux_at_q[istate][idim].reinit(n_quad_pts, n_quad_pts);
284  }
285  //write data
286  for (unsigned int flux_basis=iquad; flux_basis<n_quad_pts; ++flux_basis) {
287  //Note that the 2pt flux matrix is symmetric so we only computed upper triangular
288  conv_ref_2pt_flux_at_q[istate][idim][iquad][flux_basis] = conv_ref_flux_2pt[flux_basis][istate][idim];
289  conv_ref_2pt_flux_at_q[istate][idim][flux_basis][iquad] = conv_ref_flux_2pt[flux_basis][istate][idim];
290  }
291  }
292  }
293  }
294  //For each state we:
295  // 1. Compute reference divergence.
296  // 2. Then compute and write the rhs for the given state.
297  for(int istate=0; istate<nstate; istate++){
298 
299  //Compute reference divergence of the reference fluxes.
300  std::vector<double> conv_flux_divergence(n_quad_pts);
301 
302  //2pt flux Hadamard Product, and then multiply by vector of ones scaled by 1.
303  // 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,
304  // 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
305  // sum-factorization type algorithm that exploits the structure of the flux basis in the reference space to have O(n^{d+1}).
306  flux_basis.divergence_two_pt_flux_Hadamard_product(conv_ref_2pt_flux_at_q[istate], conv_flux_divergence, oneD_vol_quad_weights, flux_basis_stiffness.oneD_skew_symm_vol_oper, 1.0);
307 
308  // Strong form
309  // The right-hand side sends all the term to the side of the source term
310  // Therefore,
311  // \divergence ( Fconv + Fdiss ) = source
312  // has the right-hand side
313  // rhs = - \divergence( Fconv + Fdiss ) + source
314  // Since we have done an integration by parts, the volume term resulting from the divergence of Fconv and Fdiss
315  // is negative. Therefore, negative of negative means we add that volume term to the right-hand-side
316  std::vector<double> rhs(n_shape_fns);
317 
318  // Convective
319  std::vector<double> ones(n_quad_pts, 1.0);
320  soln_basis.inner_product_1D(conv_flux_divergence, ones, rhs, soln_basis.oneD_vol_operator, false, -1.0);
321 
322  for(unsigned int ishape=0; ishape<n_shape_fns; ishape++){
323  volume_term += energy_var_hat[istate][ishape] * rhs[ishape];
324  }
325  }
326  }
327 
328  return volume_term;
329 }
330 
331 template<int dim, int nstate>
332 double EulerTaylorGreen<dim, nstate>::compute_entropy(const std::shared_ptr < DGBase<dim, double> > &dg, unsigned int poly_degree) const
333 {
334  const unsigned int n_dofs_cell = dg->fe_collection[poly_degree].dofs_per_cell;
335  const unsigned int n_quad_pts = dg->volume_quadrature_collection[poly_degree].size();
336  const unsigned int n_shape_fns = n_dofs_cell / nstate;
337  //We have to project the vector of entropy variables because the mass matrix has an interpolation from solution nodes built into it.
338 
339  OPERATOR::basis_functions<dim,2*dim,double> soln_basis(1, poly_degree, dg->max_grid_degree);
340  soln_basis.build_1D_volume_operator(dg->oneD_fe_collection_1state[poly_degree], dg->oneD_quadrature_collection[poly_degree]);
341 
342  OPERATOR::mapping_shape_functions<dim,2*dim,double> mapping_basis(1, poly_degree, dg->max_grid_degree);
343  mapping_basis.build_1D_shape_functions_at_grid_nodes(dg->high_order_grid->oneD_fe_system, dg->high_order_grid->oneD_grid_nodes);
344  mapping_basis.build_1D_shape_functions_at_flux_nodes(dg->high_order_grid->oneD_fe_system, dg->oneD_quadrature_collection[poly_degree], dg->oneD_face_quadrature);
345 
346  std::vector<dealii::types::global_dof_index> dofs_indices (n_dofs_cell);
347 
348  std::shared_ptr < Physics::Euler<dim, nstate, double > > euler_double = std::dynamic_pointer_cast<Physics::Euler<dim,dim+2,double>>(PHiLiP::Physics::PhysicsFactory<dim,nstate,double>::create_Physics(dg->all_parameters));
349 
350  double entropy_fn = 0.0;
351  const std::vector<double> &quad_weights = dg->volume_quadrature_collection[poly_degree].get_weights();
352 
353  auto metric_cell = dg->high_order_grid->dof_handler_grid.begin_active();
354  for (auto cell = dg->dof_handler.begin_active(); cell!= dg->dof_handler.end(); ++cell, ++metric_cell) {
355  if (!cell->is_locally_owned()) continue;
356  cell->get_dof_indices (dofs_indices);
357 
358  // We first need to extract the mapping support points (grid nodes) from high_order_grid.
359  const dealii::FESystem<dim> &fe_metric = dg->high_order_grid->fe_system;
360  const unsigned int n_metric_dofs = fe_metric.dofs_per_cell;
361  const unsigned int n_grid_nodes = n_metric_dofs / dim;
362  std::vector<dealii::types::global_dof_index> metric_dof_indices(n_metric_dofs);
363  metric_cell->get_dof_indices (metric_dof_indices);
364  std::array<std::vector<double>,dim> mapping_support_points;
365  for(int idim=0; idim<dim; idim++){
366  mapping_support_points[idim].resize(n_grid_nodes);
367  }
368  // Get the mapping support points (physical grid nodes) from high_order_grid.
369  // Store it in such a way we can use sum-factorization on it with the mapping basis functions.
370  const std::vector<unsigned int > &index_renumbering = dealii::FETools::hierarchic_to_lexicographic_numbering<dim>(dg->max_grid_degree);
371  for (unsigned int idof = 0; idof< n_metric_dofs; ++idof) {
372  const double val = (dg->high_order_grid->volume_nodes[metric_dof_indices[idof]]);
373  const unsigned int istate = fe_metric.system_to_component_index(idof).first;
374  const unsigned int ishape = fe_metric.system_to_component_index(idof).second;
375  const unsigned int igrid_node = index_renumbering[ishape];
376  mapping_support_points[istate][igrid_node] = val;
377  }
378  // Construct the metric operators.
379  OPERATOR::metric_operators<double, dim, 2*dim> metric_oper(nstate, poly_degree, dg->max_grid_degree, false, false);
380  // Build the metric terms to compute the gradient and volume node positions.
381  // This functions will compute the determinant of the metric Jacobian and metric cofactor matrix.
382  // If flags store_vol_flux_nodes and store_surf_flux_nodes set as true it will also compute the physical quadrature positions.
383  metric_oper.build_volume_metric_operators(
384  n_quad_pts, n_grid_nodes,
385  mapping_support_points,
386  mapping_basis,
387  dg->all_parameters->use_invariant_curl_form);
388 
389  std::array<std::vector<double>,nstate> soln_coeff;
390  for(unsigned int idof=0; idof<n_dofs_cell; idof++){
391  const unsigned int istate = dg->fe_collection[poly_degree].system_to_component_index(idof).first;
392  const unsigned int ishape = dg->fe_collection[poly_degree].system_to_component_index(idof).second;
393  if(ishape == 0)
394  soln_coeff[istate].resize(n_shape_fns);
395  soln_coeff[istate][ishape] = dg->solution(dofs_indices[idof]);
396  }
397 
398  std::array<std::vector<double>,nstate> soln_at_q;
399  for(int istate=0; istate<nstate; istate++){
400  soln_at_q[istate].resize(n_quad_pts);
401  soln_basis.matrix_vector_mult_1D(soln_coeff[istate], soln_at_q[istate],
402  soln_basis.oneD_vol_operator);
403  }
404  for(unsigned int iquad=0; iquad<n_quad_pts; iquad++){
405  std::array<double,nstate> soln_state;
406  for(int istate=0; istate<nstate; istate++){
407  soln_state[istate] = soln_at_q[istate][iquad];
408  }
409  const double density = soln_state[0];
410  const double pressure = euler_double->compute_pressure(soln_state);
411  const double entropy = log(pressure) - euler_double->gam * log(density);
412  const double quadrature_entropy = -density*entropy/euler_double->gamm1;
413 
414  entropy_fn += quadrature_entropy * quad_weights[iquad] * metric_oper.det_Jac_vol[iquad];
415 
416  }
417  }
418 
419  return entropy_fn;
420 }
421 
422 template<int dim, int nstate>
423 double EulerTaylorGreen<dim, nstate>::compute_kinetic_energy(const std::shared_ptr < DGBase<dim, double> > &dg, unsigned int poly_degree) const
424 {
425  //returns the energy in the L2-norm (physically relevant)
426  int overintegrate = 10 ;
427  dealii::QGauss<1> quad_extra(dg->max_degree+1+overintegrate);
428  const unsigned int n_dofs_cell = dg->fe_collection[poly_degree].dofs_per_cell;
429  const unsigned int n_quad_pts = dg->volume_quadrature_collection[poly_degree].size();
430  const unsigned int n_shape_fns = n_dofs_cell / nstate;
431  //We have to project the vector of entropy variables because the mass matrix has an interpolation from solution nodes built into it.
432 
433  OPERATOR::basis_functions<dim,2*dim,double> soln_basis(1, poly_degree, dg->max_grid_degree);
434  soln_basis.build_1D_volume_operator(dg->oneD_fe_collection_1state[poly_degree], dg->oneD_quadrature_collection[poly_degree]);
435 
436  OPERATOR::mapping_shape_functions<dim,2*dim,double> mapping_basis(1, poly_degree, dg->max_grid_degree);
437  mapping_basis.build_1D_shape_functions_at_grid_nodes(dg->high_order_grid->oneD_fe_system, dg->high_order_grid->oneD_grid_nodes);
438  mapping_basis.build_1D_shape_functions_at_flux_nodes(dg->high_order_grid->oneD_fe_system, dg->oneD_quadrature_collection[poly_degree], dg->oneD_face_quadrature);
439 
440  std::vector<dealii::types::global_dof_index> dofs_indices (n_dofs_cell);
441 
442  double total_kinetic_energy = 0;
443 
444  const std::vector<double> &quad_weights = dg->volume_quadrature_collection[poly_degree].get_weights();
445 
446  auto metric_cell = dg->high_order_grid->dof_handler_grid.begin_active();
447  for (auto cell = dg->dof_handler.begin_active(); cell!= dg->dof_handler.end(); ++cell, ++metric_cell) {
448  if (!cell->is_locally_owned()) continue;
449 
450  // fe_values_extra.reinit (cell);
451  cell->get_dof_indices (dofs_indices);
452  // We first need to extract the mapping support points (grid nodes) from high_order_grid.
453  const dealii::FESystem<dim> &fe_metric = dg->high_order_grid->fe_system;
454  const unsigned int n_metric_dofs = fe_metric.dofs_per_cell;
455  const unsigned int n_grid_nodes = n_metric_dofs / dim;
456  std::vector<dealii::types::global_dof_index> metric_dof_indices(n_metric_dofs);
457  metric_cell->get_dof_indices (metric_dof_indices);
458  std::array<std::vector<double>,dim> mapping_support_points;
459  for(int idim=0; idim<dim; idim++){
460  mapping_support_points[idim].resize(n_grid_nodes);
461  }
462  // Get the mapping support points (physical grid nodes) from high_order_grid.
463  // Store it in such a way we can use sum-factorization on it with the mapping basis functions.
464  const std::vector<unsigned int > &index_renumbering = dealii::FETools::hierarchic_to_lexicographic_numbering<dim>(dg->max_grid_degree);
465  for (unsigned int idof = 0; idof< n_metric_dofs; ++idof) {
466  const double val = (dg->high_order_grid->volume_nodes[metric_dof_indices[idof]]);
467  const unsigned int istate = fe_metric.system_to_component_index(idof).first;
468  const unsigned int ishape = fe_metric.system_to_component_index(idof).second;
469  const unsigned int igrid_node = index_renumbering[ishape];
470  mapping_support_points[istate][igrid_node] = val;
471  }
472  // Construct the metric operators.
473  OPERATOR::metric_operators<double, dim, 2*dim> metric_oper(nstate, poly_degree, dg->max_grid_degree, false, false);
474  // Build the metric terms to compute the gradient and volume node positions.
475  // This functions will compute the determinant of the metric Jacobian and metric cofactor matrix.
476  // If flags store_vol_flux_nodes and store_surf_flux_nodes set as true it will also compute the physical quadrature positions.
477  metric_oper.build_volume_metric_operators(
478  n_quad_pts, n_grid_nodes,
479  mapping_support_points,
480  mapping_basis,
481  dg->all_parameters->use_invariant_curl_form);
482 
483  std::array<std::vector<double>,nstate> soln_coeff;
484  for(unsigned int idof=0; idof<n_dofs_cell; idof++){
485  const unsigned int istate = dg->fe_collection[poly_degree].system_to_component_index(idof).first;
486  const unsigned int ishape = dg->fe_collection[poly_degree].system_to_component_index(idof).second;
487  if(ishape == 0)
488  soln_coeff[istate].resize(n_shape_fns);
489  soln_coeff[istate][ishape] = dg->solution(dofs_indices[idof]);
490  }
491 
492  std::array<std::vector<double>,nstate> soln_at_q;
493  for(int istate=0; istate<nstate; istate++){
494  soln_at_q[istate].resize(n_quad_pts);
495  soln_basis.matrix_vector_mult_1D(soln_coeff[istate], soln_at_q[istate],
496  soln_basis.oneD_vol_operator);
497  }
498  for(unsigned int iquad=0; iquad<n_quad_pts; iquad++){
499  std::array<double,nstate> soln_state;
500  for(int istate=0; istate<nstate; istate++){
501  soln_state[istate] = soln_at_q[istate][iquad];
502  }
503  const double density = soln_state[0];
504  const double quadrature_kinetic_energy = 0.5*(soln_state[1]*soln_state[1]
505  + soln_state[2]*soln_state[2]
506  + soln_state[3]*soln_state[3])/density;
507 
508  total_kinetic_energy += quadrature_kinetic_energy * quad_weights[iquad] * metric_oper.det_Jac_vol[iquad];
509  }
510  }
511  return total_kinetic_energy;
512 }
513 
514 template<int dim, int nstate>
515 double EulerTaylorGreen<dim, nstate>::get_timestep(const std::shared_ptr < DGBase<dim, double> > &dg, unsigned int poly_degree, const double delta_x) const
516 {
517  //get local CFL
518  const unsigned int n_dofs_cell = nstate*pow(poly_degree+1,dim);
519  const unsigned int n_quad_pts = pow(poly_degree+1,dim);
520  std::vector<dealii::types::global_dof_index> dofs_indices1 (n_dofs_cell);
521 
522  double cfl_min = 1e100;
523  std::shared_ptr < Physics::PhysicsBase<dim, nstate, double > > pde_physics_double = PHiLiP::Physics::PhysicsFactory<dim,nstate,double>::create_Physics(dg->all_parameters);
524  for (auto cell = dg->dof_handler.begin_active(); cell!=dg->dof_handler.end(); ++cell) {
525  if (!cell->is_locally_owned()) continue;
526 
527  cell->get_dof_indices (dofs_indices1);
528  std::vector< std::array<double,nstate>> soln_at_q(n_quad_pts);
529  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
530  for (int istate=0; istate<nstate; istate++) {
531  soln_at_q[iquad][istate] = 0;
532  }
533  }
534  for(unsigned int iquad=0; iquad<n_quad_pts; iquad++){
535  dealii::Point<dim> qpoint = dg->volume_quadrature_collection[poly_degree].point(iquad);
536  for(unsigned int idof=0; idof<n_dofs_cell; idof++){
537  const unsigned int istate = dg->fe_collection[poly_degree].system_to_component_index(idof).first;
538  soln_at_q[iquad][istate] += dg->solution[dofs_indices1[idof]] * dg->fe_collection[poly_degree].shape_value_component(idof, qpoint, istate);
539  }
540  }
541 
542  std::vector< double > convective_eigenvalues(n_quad_pts);
543  for (unsigned int isol = 0; isol < n_quad_pts; ++isol) {
544  convective_eigenvalues[isol] = pde_physics_double->max_convective_eigenvalue (soln_at_q[isol]);
545  }
546  const double max_eig = *(std::max_element(convective_eigenvalues.begin(), convective_eigenvalues.end()));
547 
548  double cfl = 0.1 * delta_x/max_eig;
549  if(cfl < cfl_min)
550  cfl_min = cfl;
551 
552  }
553  return cfl_min;
554 }
555 
556 template <int dim, int nstate>
558 {
559  using Triangulation = dealii::parallel::distributed::Triangulation<dim>;
560  std::shared_ptr<Triangulation> grid = std::make_shared<Triangulation>(
561  MPI_COMM_WORLD,
562  typename dealii::Triangulation<dim>::MeshSmoothing(
563  dealii::Triangulation<dim>::smoothing_on_refinement |
564  dealii::Triangulation<dim>::smoothing_on_coarsening));
565 
566  using real = double;
567 
568  PHiLiP::Parameters::AllParameters all_parameters_new = *all_parameters;
569  double left = 0.0;
570  double right = 2 * dealii::numbers::PI;
571  const int n_refinements = 2;
572  unsigned int poly_degree = 3;
573 
574  const unsigned int grid_degree = all_parameters->use_curvilinear_grid ? poly_degree : 1;
576  //if curvilinear
577  PHiLiP::Grids::nonsymmetric_curved_grid<dim,Triangulation>(*grid, n_refinements);
578  }
579  else{
580  //if straight
581  PHiLiP::Grids::straight_periodic_cube<dim,Triangulation>(grid, left, right, pow(2.0,n_refinements));
582  }
583 
584  // Create DG
585  std::shared_ptr < PHiLiP::DGBase<dim, double> > dg = PHiLiP::DGFactory<dim,double>::create_discontinuous_galerkin(&all_parameters_new, poly_degree, poly_degree, grid_degree, grid);
586  dg->allocate_system (false,false,false);
587 
588  pcout << "Implement initial conditions" << std::endl;
589  std::shared_ptr< InitialConditionFunction<dim,nstate,double> > initial_condition_function =
591  SetInitialCondition<dim,nstate,double>::set_initial_condition(initial_condition_function, dg, &all_parameters_new);
592 
593  const unsigned int n_global_active_cells2 = grid->n_global_active_cells();
594  double delta_x = (right-left)/pow(n_global_active_cells2,1.0/dim)/(poly_degree+1.0);
595  pcout<<" delta x "<<delta_x<<std::endl;
596 
597  all_parameters_new.ode_solver_param.initial_time_step = get_timestep(dg,poly_degree,delta_x);
598 
599  pcout << "creating ODE solver" << std::endl;
600  std::shared_ptr<ODE::ODESolverBase<dim, double>> ode_solver = ODE::ODESolverFactory<dim, double>::create_ODESolver(dg);
601  pcout << "ODE solver successfully created" << std::endl;
602  double finalTime = 14.;
603 
604  pcout << " number dofs " << dg->dof_handler.n_dofs()<<std::endl;
605  pcout << "preparing to advance solution in time" << std::endl;
606 
607  ode_solver->current_iteration = 0;
608  ode_solver->allocate_ode_system();
609 
610  const double initial_energy = compute_kinetic_energy(dg, poly_degree);
611  const double initial_energy_mpi = (dealii::Utilities::MPI::sum(initial_energy, mpi_communicator));
612  const double initial_entropy = compute_entropy(dg, poly_degree);
613  const double initial_entropy_mpi = (dealii::Utilities::MPI::sum(initial_entropy, mpi_communicator));
614  //create a file to wirte entorpy and energy results to
615  std::ofstream myfile (all_parameters_new.energy_file + ".gpl" , std::ios::trunc);
616  //loop over time
617  while(ode_solver->current_time < finalTime){
618  //get timestep
619  const double time_step = get_timestep(dg,poly_degree, delta_x);
620  if(ode_solver->current_iteration%all_parameters_new.ode_solver_param.print_iteration_modulo==0)
621  pcout<<"time step "<<time_step<<" current time "<<ode_solver->current_time<<std::endl;
622  //take the minimum timestep from all processors.
623  const double dt = dealii::Utilities::MPI::min(time_step, mpi_communicator);
624  //integrate in time
625  ode_solver->step_in_time(dt, false);
626  ode_solver->current_iteration += 1;
627  //check if print solution
628  const bool is_output_iteration = (ode_solver->current_iteration % all_parameters_new.ode_solver_param.output_solution_every_x_steps == 0);
629  if (is_output_iteration) {
630  const int file_number = ode_solver->current_iteration / all_parameters_new.ode_solver_param.output_solution_every_x_steps;
631  dg->output_results_vtk(file_number);
632  }
633 
634  //get the change in entropy
635  const std::array<double,2> current_change_entropy = compute_change_in_entropy(dg, poly_degree);
636  const double current_change_entropy_mpi = dealii::Utilities::MPI::sum(current_change_entropy[0], mpi_communicator);
637  const double current_change_energy_mpi = dealii::Utilities::MPI::sum(current_change_entropy[1], mpi_communicator);
638  //write to the file the change in entropy mpi
639  myfile<<ode_solver->current_time<<" "<< current_change_entropy_mpi <<std::endl;
640  pcout << "M plus K norm Change in Entropy at time " << ode_solver->current_time << " is " << current_change_entropy_mpi<< std::endl;
641  pcout << "M plus K norm Change in Kinetic Energy at time " << ode_solver->current_time << " is " << current_change_energy_mpi<< std::endl;
642  //check if change in entropy is conserved at machine precision
643  if(abs(current_change_entropy[0]) > 1e-12 && (dg->all_parameters->two_point_num_flux_type == Parameters::AllParameters::TwoPointNumericalFlux::IR || dg->all_parameters->two_point_num_flux_type == Parameters::AllParameters::TwoPointNumericalFlux::CH || dg->all_parameters->two_point_num_flux_type == Parameters::AllParameters::TwoPointNumericalFlux::Ra)){
644  pcout << " Change in entropy was not monotonically conserved." << std::endl;
645  return 1;
646  }
647 
648  //get the kinetic energy
649  const double current_energy = compute_kinetic_energy(dg, poly_degree);
650  const double current_energy_mpi = (dealii::Utilities::MPI::sum(current_energy, mpi_communicator));
651  pcout << "Normalized kinetic energy " << ode_solver->current_time << " is " << current_energy_mpi/initial_energy_mpi<< std::endl;
652  //get the entropy
653  const double current_entropy = compute_entropy(dg, poly_degree);
654  const double current_entropy_mpi = (dealii::Utilities::MPI::sum(current_entropy, mpi_communicator));
655  pcout << "Normalized entropy " << ode_solver->current_time << " is " << current_entropy_mpi/initial_entropy_mpi<< std::endl;
656 
657  //get the volume work for kinetic energy
658  double current_vol_work = compute_volume_term(dg, poly_degree);
659  double current_vol_work_mpi = (dealii::Utilities::MPI::sum(current_vol_work, mpi_communicator));
660  pcout<<"volume work "<<current_vol_work_mpi<<std::endl;
661  myfile << ode_solver->current_time << " " << std::fixed << std::setprecision(16) << current_vol_work_mpi<< std::endl;
662  //check for non overintegrated case
664  if(abs(current_vol_work_mpi) > 1e-12 && (dg->all_parameters->two_point_num_flux_type == Parameters::AllParameters::TwoPointNumericalFlux::Ra || dg->all_parameters->two_point_num_flux_type == Parameters::AllParameters::TwoPointNumericalFlux::KG ) ){
665  pcout<< "The kinetic energy volume work is not zero."<<std::endl;
666  return 1;
667  }
668  }
669  }
670  myfile.close();
671 
672  return 0;
673 }
674 
675 #if PHILIP_DIM==3
677 #endif
678 
679 } // Tests namespace
680 } // PHiLiP namespace
681 
dealii::Tensor< 2, dim, std::vector< real > > metric_cofactor_vol
The volume metric cofactor matrix.
Definition: operators.h:1165
void build_1D_volume_operator(const dealii::FESystem< 1, 1 > &finite_element, const dealii::Quadrature< 1 > &quadrature)
Assembles the one dimensional operator.
Definition: operators.cpp:1082
bool use_curvilinear_grid
Flag to use curvilinear grid.
void divergence_two_pt_flux_Hadamard_product(const dealii::Tensor< 1, dim, dealii::FullMatrix< real >> &input_mat, std::vector< real > &output_vect, const std::vector< real > &weights, const dealii::FullMatrix< double > &basis, const double scaling=2.0)
Computes the divergence of the 2pt flux Hadamard products, then sums the rows.
Definition: operators.cpp:536
double compute_kinetic_energy(const std::shared_ptr< DGBase< dim, double > > &dg, unsigned int poly_degree) const
Computes kinetic energy.
const MPI_Comm mpi_communicator
MPI communicator.
Definition: tests.h:39
void build_1D_volume_operator(const dealii::FESystem< 1, 1 > &finite_element, const dealii::Quadrature< 1 > &quadrature)
Assembles the one dimensional operator.
Definition: operators.cpp:1732
int output_solution_every_x_steps
Outputs the solution every x steps to .vtk file.
void inner_product_1D(const std::vector< real > &input_vect, const std::vector< real > &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:524
Files for the baseline physics.
Definition: ADTypes.hpp:10
unsigned int print_iteration_modulo
If ode_output==verbose, print every print_iteration_modulo iterations.
dealii::FullMatrix< double > oneD_skew_symm_vol_oper
Skew-symmetric volume operator .
Definition: operators.h:494
void build_1D_shape_functions_at_grid_nodes(const dealii::FESystem< 1, 1 > &finite_element, const dealii::Quadrature< 1 > &quadrature)
Constructs the volume operator and gradient operator.
Definition: operators.cpp:2155
double compute_volume_term(const std::shared_ptr< DGBase< dim, double > > &dg, unsigned int poly_degree) const
Computes the volume term kinetic energy production.
Main parameter class that contains the various other sub-parameter classes.
std::string energy_file
Energy file.
std::vector< real > det_Jac_vol
The determinant of the metric Jacobian at volume cubature nodes.
Definition: operators.h:1171
ODESolverParam ode_solver_param
Contains parameters for ODE solver.
double initial_time_step
Time step used in ODE solver.
const Parameters::AllParameters *const all_parameters
Pointer to all parameters.
Definition: tests.h:20
void build_1D_gradient_operator(const dealii::FESystem< 1, 1 > &finite_element, const dealii::Quadrature< 1 > &quadrature)
Assembles the one dimensional operator.
Definition: operators.cpp:1102
static std::shared_ptr< DGBase< dim, real, MeshType > > create_discontinuous_galerkin(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)
Creates a derived object DG, but returns it as DGBase.
Definition: dg_factory.cpp:10
double get_timestep(const std::shared_ptr< DGBase< dim, double > > &dg, unsigned int poly_degree, const double delta_x) const
Computes the timestep from max eignevector.
Base metric operators class that stores functions used in both the volume and on surface.
Definition: operators.h:1086
The mapping shape functions evaluated at the desired nodes (facet set included in volume grid nodes f...
Definition: operators.h:1026
EulerTaylorGreen(const Parameters::AllParameters *const parameters_input)
Constructor.
Euler equations. Derived from PhysicsBase.
Definition: euler.h:78
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:2205
dealii::FullMatrix< double > oneD_vol_operator
Stores the one dimensional volume operator.
Definition: operators.h:355
static std::shared_ptr< PhysicsBase< dim, nstate, real > > create_Physics(const Parameters::AllParameters *const parameters_input, std::shared_ptr< ModelBase< dim, nstate, real > > model_input=nullptr)
Factory to return the correct physics given input file.
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:1122
void build_volume_metric_operators(const unsigned int n_quad_pts, const unsigned int n_metric_dofs, const std::array< std::vector< real >, dim > &mapping_support_points, mapping_shape_functions< dim, n_faces, real > &mapping_basis, const bool use_invariant_curl_form=false)
Builds the volume metric operators.
Definition: operators.cpp:2294
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:1305
dealii::ConditionalOStream pcout
ConditionalOStream.
Definition: tests.h:45
DGBase is independent of the number of state variables.
Definition: dg_base.hpp:82
double compute_entropy(const std::shared_ptr< DGBase< dim, double > > &dg, unsigned int poly_degree) const
Computes entropy in the norm.
void build_1D_shape_functions_at_flux_nodes(const dealii::FESystem< 1, 1 > &finite_element, const dealii::Quadrature< 1 > &quadrature, const dealii::Quadrature< 0 > &face_quadrature)
Constructs the volume, gradient, surface, and surface gradient operator.
Definition: operators.cpp:2164
void matrix_vector_mult_1D(const std::vector< real > &input_vect, std::vector< real > &output_vect, const dealii::FullMatrix< double > &basis_x, const bool adding=false, const double factor=1.0)
Apply the matrix vector operation using the 1D operator in each direction.
Definition: operators.cpp:308
static std::shared_ptr< ODESolverBase< dim, real, MeshType > > create_ODESolver(std::shared_ptr< DGBase< dim, real, MeshType > > dg_input)
Creates either implicit or explicit ODE solver based on parameter value(no POD basis given) ...
Projection operator corresponding to basis functions onto M-norm (L2).
Definition: operators.h:694
static void set_initial_condition(std::shared_ptr< InitialConditionFunction< dim, nstate, double > > initial_condition_function_input, std::shared_ptr< PHiLiP::DGBase< dim, real > > dg_input, const Parameters::AllParameters *const parameters_input)
Applies the given initial condition function to the given dg object.
Local stiffness matrix without jacobian dependence.
Definition: operators.h:472
Base class of all the tests.
Definition: tests.h:17
int overintegration
Number of additional quadrature points to use.
int run_test() const override
Ensure that the kinetic energy is bounded.
static std::shared_ptr< InitialConditionFunction< dim, nstate, real > > create_InitialConditionFunction(Parameters::AllParameters const *const param)
Construct InitialConditionFunction object from global parameter file.
std::array< double, 2 > compute_change_in_entropy(const std::shared_ptr< DGBase< dim, double > > &dg, unsigned int poly_degree) const
Computes change in entropy in the norm.