[P]arallel [Hi]gh-order [Li]brary for [P]DEs  Latest
Parallel High-Order Library for PDEs through hp-adaptive Discontinuous Galerkin methods
periodic_turbulence.cpp
1 #include "periodic_turbulence.h"
2 #include "ode_solver/low_storage_runge_kutta_ode_solver.h"
3 #include <deal.II/base/function.h>
4 #include <stdlib.h>
5 #include <iostream>
6 #include <deal.II/dofs/dof_tools.h>
7 #include <deal.II/grid/grid_tools.h>
8 #include <deal.II/numerics/vector_tools.h>
9 #include <deal.II/fe/fe_values.h>
10 #include "physics/physics_factory.h"
11 #include <deal.II/base/table_handler.h>
12 #include <deal.II/base/tensor.h>
13 #include "math.h"
14 #include <string>
15 #include <deal.II/base/quadrature_lib.h>
16 
17 namespace PHiLiP {
18 
19 namespace FlowSolver {
20 
21 //=========================================================
22 // TURBULENCE IN PERIODIC CUBE DOMAIN
23 //=========================================================
24 template <int dim, int nspecies, int nstate>
26  : PeriodicCubeFlow<dim, nspecies, nstate>(parameters_input)
27  , unsteady_data_table_filename_with_extension(this->all_param.flow_solver_param.unsteady_data_table_filename+".txt")
28  , number_of_times_to_output_velocity_field(this->all_param.flow_solver_param.number_of_times_to_output_velocity_field)
29  , output_velocity_field_at_fixed_times(this->all_param.flow_solver_param.output_velocity_field_at_fixed_times)
30  , output_vorticity_magnitude_field_in_addition_to_velocity(this->all_param.flow_solver_param.output_vorticity_magnitude_field_in_addition_to_velocity)
31  , output_density_field_in_addition_to_velocity(this->all_param.flow_solver_param.output_density_field_in_addition_to_velocity)
32  , output_viscosity_field_in_addition_to_velocity(this->all_param.flow_solver_param.output_viscosity_field_in_addition_to_velocity)
33  , output_flow_field_files_directory_name(this->all_param.flow_solver_param.output_flow_field_files_directory_name)
34  , output_solution_at_exact_fixed_times(this->all_param.ode_solver_param.output_solution_at_exact_fixed_times)
35  , output_velocity_number_of_subvisions(this->all_param.flow_solver_param.output_velocity_number_of_subvisions)
36  , do_compute_angular_momentum(this->all_param.flow_solver_param.do_compute_angular_momentum)
37 {
38  // Get the flow case type
39  using FlowCaseEnum = Parameters::FlowSolverParam::FlowCaseType;
40  const FlowCaseEnum flow_type = this->all_param.flow_solver_param.flow_case_type;
41 
42  // Flow case identifiers
43  this->is_taylor_green_vortex = (flow_type == FlowCaseEnum::taylor_green_vortex);
44  this->is_decaying_homogeneous_isotropic_turbulence = (flow_type == FlowCaseEnum::decaying_homogeneous_isotropic_turbulence);
45  this->is_viscous_flow = (this->all_param.pde_type != Parameters::AllParameters::PartialDifferentialEquation::euler);
47 
48  // Navier-Stokes object; create using dynamic_pointer_cast and the create_Physics factory
50  PHiLiP::Parameters::AllParameters parameters_navier_stokes = this->all_param;
51  parameters_navier_stokes.pde_type = PDE_enum::navier_stokes;
54 
55  /* Initialize integrated quantities as NAN;
56  done as a precaution in the case compute_integrated_quantities() is not called
57  before a member function of kind get_integrated_quantity() is called
58  */
59  std::fill(this->integrated_quantities.begin(), this->integrated_quantities.end(), NAN);
60 
61  // Initialize the integrated kinetic energy as NAN
63 
66  exact_output_times_of_velocity_field_files_table = std::make_shared<dealii::TableHandler>();
68 
69  // Get output_velocity_field_times from string
70  const std::string output_velocity_field_times_string = this->all_param.flow_solver_param.output_velocity_field_times_string;
71  std::string line = output_velocity_field_times_string;
72  std::string::size_type sz1;
73  output_velocity_field_times[0] = std::stod(line,&sz1);
74  for(unsigned int i=1; i<number_of_times_to_output_velocity_field; ++i) {
75  line = line.substr(sz1);
76  sz1 = 0;
77  output_velocity_field_times[i] = std::stod(line,&sz1);
78  }
79 
80  // Get flow_field_quantity_filename_prefix
83  flow_field_quantity_filename_prefix += std::string("_vorticity");
84  }
85  }
86 
89  // If restarting, get the index of the current desired time to output velocity field based on the initial time
90  const double initial_simulation_time = this->all_param.ode_solver_param.initial_time;
91  for(unsigned int i=1; i<number_of_times_to_output_velocity_field; ++i) {
92  if((output_velocity_field_times[i-1] < initial_simulation_time) && (initial_simulation_time < output_velocity_field_times[i])) {
94  }
95  }
96  }
97 }
98 
99 template <int dim, int nspecies, int nstate>
101 {
103  this->pcout << "- - Courant-Friedrichs-Lewy number: " << this->all_param.flow_solver_param.courant_friedrichs_lewy_number << std::endl;
104  else
105  this->pcout << "- - Constant time step: " << this->all_param.flow_solver_param.constant_time_step << std::endl;
106  std::string flow_type_string;
108  this->pcout << "- - Freestream Reynolds number: " << this->all_param.navier_stokes_param.reynolds_number_inf << std::endl;
109  this->pcout << "- - Freestream Mach number: " << this->all_param.euler_param.mach_inf << std::endl;
110  }
111  this->display_grid_parameters();
112 }
113 
114 template <int dim, int nspecies, int nstate>
116 {
118  const double constant_time_step = this->all_param.flow_solver_param.constant_time_step;
119  return constant_time_step;
120  } else {
121  const unsigned int number_of_degrees_of_freedom_per_state = dg->dof_handler.n_dofs()/nstate;
122  const double approximate_grid_spacing = (this->domain_right-this->domain_left)/pow(number_of_degrees_of_freedom_per_state,(1.0/dim));
123  const double constant_time_step = this->all_param.flow_solver_param.courant_friedrichs_lewy_number * approximate_grid_spacing;
124  return constant_time_step;
125  }
126 }
127 
128 template <int dim, int nspecies, int nstate>
130 {
131  // expression for a uniform grid (i.e. same number of cells in all directions)
132  const unsigned int number_of_degrees_of_freedom_per_state = pow(this->number_of_cells_per_direction*(poly_degree_input+1),dim);
133  return number_of_degrees_of_freedom_per_state;
134 }
135 
136 std::string get_padded_mpi_rank_string(const int mpi_rank_input) {
137  // returns the mpi rank as a string with appropriate padding
138  std::string mpi_rank_string = std::to_string(mpi_rank_input);
139  const unsigned int length_of_mpi_rank_with_padding = 5;
140  const int number_of_zeros = length_of_mpi_rank_with_padding - mpi_rank_string.length();
141  mpi_rank_string.insert(0, number_of_zeros, '0');
142 
143  return mpi_rank_string;
144 }
145 
146 template<int dim, int nspecies, int nstate>
148  std::shared_ptr<DGBase<dim,nspecies,double>> dg,
149  const unsigned int output_file_index,
150  const double current_time) const
151 {
152  this->pcout << " ... Writting velocity field ... " << std::flush;
153 
154  // NOTE: Same loop from read_values_from_file_and_project() in set_initial_condition.cpp
155 
156  // Get filename prefix based on output file index and the flow field quantity filename prefix
157  const std::string filename_prefix = flow_field_quantity_filename_prefix + std::string("-") + std::to_string(output_file_index);
158 
159  // (1) Get filename based on MPI rank
160  //-------------------------------------------------------------
161  // -- Get padded mpi rank string
162  const std::string mpi_rank_string = get_padded_mpi_rank_string(this->mpi_rank);
163  // -- Assemble filename string
164  const std::string filename_without_extension = filename_prefix + std::string("-") + mpi_rank_string;
165  const std::string filename = output_flow_field_files_directory_name + std::string("/") + filename_without_extension + std::string(".dat");
166  //-------------------------------------------------------------
167 
168  // (1.5) Write the exact output time for the file to the table
169  //-------------------------------------------------------------
170  if(this->mpi_rank==0) {
171  const std::string filename_for_time_table = output_flow_field_files_directory_name + std::string("/") + std::string("exact_output_times_of_velocity_field_files.txt");
172  // Add values to data table
173  this->add_value_to_data_table(output_file_index,"output_file_index",this->exact_output_times_of_velocity_field_files_table);
175  // Write to file
176  std::ofstream data_table_file(filename_for_time_table);
177  this->exact_output_times_of_velocity_field_files_table->write_text(data_table_file);
178  }
179  //-------------------------------------------------------------
180 
181  // (2) Write file
182  //-------------------------------------------------------------
183  std::ofstream FILE (filename);
184 
185  const unsigned int higher_poly_degree = this->output_velocity_number_of_subvisions*(dg->max_degree+1)-1; // Note: -1 so that n_quad_pts in 1D is n_subdiv*(P+1)
186 
187  // check that the file is open and write DOFs
188  if (!FILE.is_open()) {
189  this->pcout << "ERROR: Cannot open file " << filename << std::endl;
190  std::abort();
191  } else if(this->mpi_rank==0) {
192  const unsigned int number_of_degrees_of_freedom_per_state = this->get_number_of_degrees_of_freedom_per_state_from_poly_degree(higher_poly_degree);
193  FILE << number_of_degrees_of_freedom_per_state << std::string("\n");
194  }
195 
196  // build a basis oneD on equidistant nodes in 1D
197  dealii::Quadrature<1> vol_quad_equidistant_1D = dealii::QIterated<1>(dealii::QTrapez<1>(),higher_poly_degree);
198  const unsigned int n_quad_pts = pow(vol_quad_equidistant_1D.size(),dim);
199 
200  const unsigned int init_grid_degree = dg->high_order_grid->fe_system.tensor_degree();
201  OPERATOR::basis_functions<dim,2*dim> soln_basis(1, dg->max_degree, init_grid_degree);
202  soln_basis.build_1D_volume_operator(dg->oneD_fe_collection_1state[dg->max_degree], vol_quad_equidistant_1D);
203  soln_basis.build_1D_gradient_operator(dg->oneD_fe_collection_1state[dg->max_degree], vol_quad_equidistant_1D);
204 
205  // mapping basis for the equidistant node set because we output the physical coordinates
206  OPERATOR::mapping_shape_functions<dim,2*dim> mapping_basis_at_equidistant(1, dg->max_degree, init_grid_degree);
207  mapping_basis_at_equidistant.build_1D_shape_functions_at_grid_nodes(dg->high_order_grid->oneD_fe_system, dg->high_order_grid->oneD_grid_nodes);
208  mapping_basis_at_equidistant.build_1D_shape_functions_at_flux_nodes(dg->high_order_grid->oneD_fe_system, vol_quad_equidistant_1D, dg->oneD_face_quadrature);
209 
210  const unsigned int max_dofs_per_cell = dg->dof_handler.get_fe_collection().max_dofs_per_cell();
211  std::vector<dealii::types::global_dof_index> current_dofs_indices(max_dofs_per_cell);
212  auto metric_cell = dg->high_order_grid->dof_handler_grid.begin_active();
213  for (auto current_cell = dg->dof_handler.begin_active(); current_cell!=dg->dof_handler.end(); ++current_cell, ++metric_cell) {
214  if (!current_cell->is_locally_owned()) continue;
215 
216  const int i_fele = current_cell->active_fe_index();
217  const unsigned int poly_degree = i_fele;
218  const unsigned int n_dofs_cell = dg->fe_collection[poly_degree].dofs_per_cell;
219  const unsigned int n_shape_fns = n_dofs_cell / nstate;
220 
221  // We first need to extract the mapping support points (grid nodes) from high_order_grid.
222  const dealii::FESystem<dim> &fe_metric = dg->high_order_grid->fe_system;
223  const unsigned int n_metric_dofs = fe_metric.dofs_per_cell;
224  const unsigned int n_grid_nodes = n_metric_dofs / dim;
225  std::vector<dealii::types::global_dof_index> metric_dof_indices(n_metric_dofs);
226  metric_cell->get_dof_indices (metric_dof_indices);
227  std::array<std::vector<double>,dim> mapping_support_points;
228  for(int idim=0; idim<dim; idim++){
229  mapping_support_points[idim].resize(n_grid_nodes);
230  }
231  // Get the mapping support points (physical grid nodes) from high_order_grid.
232  // Store it in such a way we can use sum-factorization on it with the mapping basis functions.
233  const std::vector<unsigned int > &index_renumbering = dealii::FETools::hierarchic_to_lexicographic_numbering<dim>(init_grid_degree);
234  for (unsigned int idof = 0; idof< n_metric_dofs; ++idof) {
235  const double val = (dg->high_order_grid->volume_nodes[metric_dof_indices[idof]]);
236  const unsigned int istate = fe_metric.system_to_component_index(idof).first;
237  const unsigned int ishape = fe_metric.system_to_component_index(idof).second;
238  const unsigned int igrid_node = index_renumbering[ishape];
239  mapping_support_points[istate][igrid_node] = val;
240  }
241  // Construct the metric operators
242  OPERATOR::metric_operators<double, dim, 2*dim> metric_oper_equid(nstate, poly_degree, init_grid_degree, true, false);
243  // Build the metric terms to compute the gradient and volume node positions.
244  // This functions will compute the determinant of the metric Jacobian and metric cofactor matrix.
245  // If flags store_vol_flux_nodes and store_surf_flux_nodes set as true it will also compute the physical quadrature positions.
246  metric_oper_equid.build_volume_metric_operators(
247  n_quad_pts, n_grid_nodes,
248  mapping_support_points,
249  mapping_basis_at_equidistant,
250  dg->all_parameters->use_invariant_curl_form);
251 
252  current_dofs_indices.resize(n_dofs_cell);
253  current_cell->get_dof_indices (current_dofs_indices);
254 
255  std::array<std::vector<double>,nstate> soln_coeff;
256  for(unsigned int idof=0; idof<n_dofs_cell; idof++){
257  const unsigned int istate = dg->fe_collection[poly_degree].system_to_component_index(idof).first;
258  const unsigned int ishape = dg->fe_collection[poly_degree].system_to_component_index(idof).second;
259  if(ishape == 0) {
260  soln_coeff[istate].resize(n_shape_fns);
261  }
262  soln_coeff[istate][ishape] = dg->solution(current_dofs_indices[idof]);
263  }
264 
265  std::array<std::vector<double>,nstate> soln_at_q;
266  std::array<dealii::Tensor<1,dim,std::vector<double>>,nstate> soln_grad_at_q;
267  for(int istate=0; istate<nstate; istate++){
268  soln_at_q[istate].resize(n_quad_pts);
269  // Interpolate soln coeff to volume cubature nodes.
270  soln_basis.matrix_vector_mult_1D(soln_coeff[istate], soln_at_q[istate],
271  soln_basis.oneD_vol_operator);
272  // apply gradient of reference basis functions on the solution at volume cubature nodes
273  dealii::Tensor<1,dim,std::vector<double>> ref_gradient_basis_fns_times_soln;
274  for(int idim=0; idim<dim; idim++){
275  ref_gradient_basis_fns_times_soln[idim].resize(n_quad_pts);
276  }
277  soln_basis.gradient_matrix_vector_mult_1D(soln_coeff[istate], ref_gradient_basis_fns_times_soln,
278  soln_basis.oneD_vol_operator,
279  soln_basis.oneD_grad_operator);
280  // transform the gradient into a physical gradient operator scaled by determinant of metric Jacobian
281  // then apply the inner product in each direction
282  for(int idim=0; idim<dim; idim++){
283  soln_grad_at_q[istate][idim].resize(n_quad_pts);
284  for(unsigned int iquad=0; iquad<n_quad_pts; iquad++){
285  for(int jdim=0; jdim<dim; jdim++){
286  //transform into the physical gradient
287  soln_grad_at_q[istate][idim][iquad] += metric_oper_equid.metric_cofactor_vol[idim][jdim][iquad]
288  * ref_gradient_basis_fns_times_soln[jdim][iquad]
289  / metric_oper_equid.det_Jac_vol[iquad];
290  }
291  }
292  }
293  }
294  // compute quantities at quad nodes (equisdistant)
295  dealii::Tensor<1,dim,std::vector<double>> velocity_at_q;
296  std::vector<double> vorticity_magnitude_at_q(n_quad_pts);
297  std::vector<double> density_at_q(n_quad_pts);
298  std::vector<double> viscosity_at_q(n_quad_pts);
299  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
300  std::array<double,nstate> soln_state;
301  std::array<dealii::Tensor<1,dim,double>,nstate> soln_grad_state;
302  for(int istate=0; istate<nstate; istate++){
303  soln_state[istate] = soln_at_q[istate][iquad];
304  for(int idim=0; idim<dim; idim++){
305  soln_grad_state[istate][idim] = soln_grad_at_q[istate][idim][iquad];
306  }
307  }
308  const dealii::Tensor<1,dim,double> velocity = this->navier_stokes_physics->compute_velocities(soln_state);
309  for(int idim=0; idim<dim; idim++){
310  if(iquad==0)
311  velocity_at_q[idim].resize(n_quad_pts);
312  velocity_at_q[idim][iquad] = velocity[idim];
313  }
314 
315  // write vorticity magnitude field if desired
317  vorticity_magnitude_at_q[iquad] = this->navier_stokes_physics->compute_vorticity_magnitude(soln_state, soln_grad_state);
318  }
319  // write density field if desired
321  density_at_q[iquad] = soln_state[0];
322  }
323  // write viscosity field if desired
325  const std::array<double,nstate> primitive_soln = this->navier_stokes_physics->convert_conservative_to_primitive(soln_state);
326  viscosity_at_q[iquad] = this->navier_stokes_physics->compute_viscosity_coefficient(primitive_soln);
327  }
328  }
329  // write out all values at equidistant nodes
330  for(unsigned int ishape=0; ishape<n_quad_pts; ishape++){
331  dealii::Point<dim,double> vol_equid_node;
332  // write coordinates
333  for(int idim=0; idim<dim; idim++) {
334  vol_equid_node[idim] = metric_oper_equid.flux_nodes_vol[idim][ishape];
335  FILE << std::setprecision(17) << vol_equid_node[idim] << std::string(" ");
336  }
337  // write velocity field
338  for (int d=0; d<dim; ++d) {
339  FILE << std::setprecision(17) << velocity_at_q[d][ishape] << std::string(" ");
340  }
341  // write vorticity magnitude field if desired
343  FILE << std::setprecision(17) << vorticity_magnitude_at_q[ishape] << std::string(" ");
344  }
345  // write density field if desired
347  FILE << std::setprecision(17) << density_at_q[ishape] << std::string(" ");
348  }
349  // write viscosity field if desired
351  FILE << std::setprecision(17) << viscosity_at_q[ishape] << std::string(" ");
352  }
353  FILE << std::string("\n"); // next line
354  }
355  }
356  FILE.close();
357  this->pcout << "done." << std::endl;
358 }
359 
360 template <int dim, int nspecies, int nstate>
362 {
363  // compute time step based on advection speed (i.e. maximum local wave speed)
364  const unsigned int number_of_degrees_of_freedom_per_state = dg->dof_handler.n_dofs()/nstate;
365  const double approximate_grid_spacing = (this->domain_right-this->domain_left)/pow(number_of_degrees_of_freedom_per_state,(1.0/dim));
366  const double cfl_number = this->all_param.flow_solver_param.courant_friedrichs_lewy_number;
367  const double time_step = cfl_number * approximate_grid_spacing / this->maximum_local_wave_speed;
368  return time_step;
369 }
370 
371 template<int dim, int nspecies, int nstate>
373 {
374  // Initialize the maximum local wave speed to zero
375  this->maximum_local_wave_speed = 0.0;
376 
377  // Overintegrate the error to make sure there is not integration error in the error estimate
378  int overintegrate = 10;
379  // int overintegrate = 0;
380  const unsigned int grid_degree = dg.high_order_grid->fe_system.tensor_degree();
381  const unsigned int poly_degree = dg.max_degree;
382  dealii::QGauss<dim> quad_extra(dg.max_degree+1+overintegrate);
383  const unsigned int n_quad_pts = quad_extra.size();
384  dealii::QGauss<1> quad_extra_1D(dg.max_degree+1+overintegrate);
385  OPERATOR::basis_functions<dim,2*dim> soln_basis(1, poly_degree, grid_degree);
386  soln_basis.build_1D_volume_operator(dg.oneD_fe_collection_1state[poly_degree], quad_extra_1D);
387 
388  const unsigned int n_dofs = dg.fe_collection[poly_degree].n_dofs_per_cell();
389  const unsigned int n_shape_fns = n_dofs / nstate;
390 
391  std::vector<dealii::types::global_dof_index> dofs_indices (n_dofs);
392  for (auto cell = dg.dof_handler.begin_active(); cell!=dg.dof_handler.end(); ++cell) {
393  if (!cell->is_locally_owned()) continue;
394  cell->get_dof_indices (dofs_indices);
395 
396  std::array<std::vector<double>,nstate> soln_coeff;
397  for (unsigned int idof = 0; idof < n_dofs; ++idof) {
398  const unsigned int istate = dg.fe_collection[poly_degree].system_to_component_index(idof).first;
399  const unsigned int ishape = dg.fe_collection[poly_degree].system_to_component_index(idof).second;
400  if(ishape == 0){
401  soln_coeff[istate].resize(n_shape_fns);
402  }
403 
404  soln_coeff[istate][ishape] = dg.solution(dofs_indices[idof]);
405  }
406  std::array<std::vector<double>,nstate> soln_at_q_vect;
407  for(int istate=0; istate<nstate; istate++){
408  soln_at_q_vect[istate].resize(n_quad_pts);
409  // Interpolate soln coeff to volume cubature nodes.
410  soln_basis.matrix_vector_mult_1D(soln_coeff[istate], soln_at_q_vect[istate],
411  soln_basis.oneD_vol_operator);
412  }
413 
414  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
415  std::array<double,nstate> soln_at_q;
416  for(int istate=0; istate<nstate; istate++){
417  soln_at_q[istate] = soln_at_q_vect[istate][iquad];
418  }
419 
420  // Update the maximum local wave speed (i.e. convective eigenvalue)
421  const double local_wave_speed = this->navier_stokes_physics->max_convective_eigenvalue(soln_at_q);
422  if(local_wave_speed > this->maximum_local_wave_speed) this->maximum_local_wave_speed = local_wave_speed;
423  }
424  }
425  this->maximum_local_wave_speed = dealii::Utilities::MPI::max(this->maximum_local_wave_speed, this->mpi_communicator);
426 }
427 
428 template <int dim, int nspecies, int nstate>
429 double PeriodicTurbulence<dim,nspecies,nstate>::compute_angular_momentum(const dealii::Point<dim> position, const dealii::Tensor<1,3,double> vorticity) const
430 {
431  // Reference: Equation 13 in article H.J.H. Clercx, C.-H. Bruneau / Computers & Fluids 35 (2006) 245–279
432  const double radius_squared = position.norm_square(); // radius squared (i.e. r^2)
433  const double vorticity_magnitude = vorticity.norm();
434  const double angular_momentum = 0.5*radius_squared*vorticity_magnitude; // Note: removed the negative sign since since vorticity magnitude will always be positive
435  return angular_momentum;
436 }
437 
438 template<int dim, int nspecies, int nstate>
440 {
441  std::array<double,NUMBER_OF_INTEGRATED_QUANTITIES> integral_values;
442  std::fill(integral_values.begin(), integral_values.end(), 0.0);
443 
444  // Initialize the maximum local wave speed to zero; only used for adaptive time step
445  if(this->all_param.flow_solver_param.adaptive_time_step == true || this->all_param.flow_solver_param.error_adaptive_time_step == true) this->maximum_local_wave_speed = 0.0;
446 
447  // Overintegrate the error to make sure there is not integration error in the error estimate
448  int overintegrate = 10;
449 
450  // Set the quadrature of size dim and 1D for sum-factorization.
451  dealii::QGauss<dim> quad_extra(dg.max_degree+1+overintegrate);
452  dealii::QGauss<1> quad_extra_1D(dg.max_degree+1+overintegrate);
453 
454  const unsigned int n_quad_pts = quad_extra.size();
455  const unsigned int grid_degree = dg.high_order_grid->fe_system.tensor_degree();
456  const unsigned int poly_degree = dg.max_degree;
457  // Construct the basis functions and mapping shape functions.
458  OPERATOR::basis_functions<dim,2*dim> soln_basis(1, poly_degree, grid_degree);
459  OPERATOR::mapping_shape_functions<dim,2*dim> mapping_basis(1, poly_degree, grid_degree);
460  // Build basis function volume operator and gradient operator from 1D finite element for 1 state.
461  soln_basis.build_1D_volume_operator(dg.oneD_fe_collection_1state[poly_degree], quad_extra_1D);
462  soln_basis.build_1D_gradient_operator(dg.oneD_fe_collection_1state[poly_degree], quad_extra_1D);
463  // Build mapping shape functions operators using the oneD high_ordeR_grid finite element
464  mapping_basis.build_1D_shape_functions_at_grid_nodes(dg.high_order_grid->oneD_fe_system, dg.high_order_grid->oneD_grid_nodes);
465  mapping_basis.build_1D_shape_functions_at_flux_nodes(dg.high_order_grid->oneD_fe_system, quad_extra_1D, dg.oneD_face_quadrature);
466  // Construct and build projection operator
467  OPERATOR::vol_projection_operator<dim,2*dim> soln_basis_projection_oper(1, poly_degree, grid_degree);
468  soln_basis_projection_oper.build_1D_volume_operator(dg.oneD_fe_collection_1state[poly_degree], quad_extra_1D);
469  const std::vector<double> &quad_weights = quad_extra.get_weights();
470  // If in the future we need the physical quadrature node location, turn these flags to true and the constructor will
471  // automatically compute it for you. Currently set to false as to not compute extra unused terms.
472  bool store_vol_flux_nodes = false;//currently doesn't need the volume physical nodal position
473  if(this->do_compute_angular_momentum) store_vol_flux_nodes = true;
474  const bool store_surf_flux_nodes = false;//currently doesn't need the surface physical nodal position
475 
476  const unsigned int n_dofs = dg.fe_collection[poly_degree].n_dofs_per_cell();
477  const unsigned int n_shape_fns = n_dofs / nstate;
478  std::vector<dealii::types::global_dof_index> dofs_indices (n_dofs);
479  auto metric_cell = dg.high_order_grid->dof_handler_grid.begin_active();
480  // Changed for loop to update metric_cell.
481  for (auto cell = dg.dof_handler.begin_active(); cell!= dg.dof_handler.end(); ++cell, ++metric_cell) {
482  if (!cell->is_locally_owned()) continue;
483  cell->get_dof_indices (dofs_indices);
484 
485  // We first need to extract the mapping support points (grid nodes) from high_order_grid.
486  const dealii::FESystem<dim> &fe_metric = dg.high_order_grid->fe_system;
487  const unsigned int n_metric_dofs = fe_metric.dofs_per_cell;
488  const unsigned int n_grid_nodes = n_metric_dofs / dim;
489  std::vector<dealii::types::global_dof_index> metric_dof_indices(n_metric_dofs);
490  metric_cell->get_dof_indices (metric_dof_indices);
491  std::array<std::vector<double>,dim> mapping_support_points;
492  for(int idim=0; idim<dim; idim++){
493  mapping_support_points[idim].resize(n_grid_nodes);
494  }
495  // Get the mapping support points (physical grid nodes) from high_order_grid.
496  // Store it in such a way we can use sum-factorization on it with the mapping basis functions.
497  const std::vector<unsigned int > &index_renumbering = dealii::FETools::hierarchic_to_lexicographic_numbering<dim>(grid_degree);
498  for (unsigned int idof = 0; idof< n_metric_dofs; ++idof) {
499  const double val = (dg.high_order_grid->volume_nodes[metric_dof_indices[idof]]);
500  const unsigned int istate = fe_metric.system_to_component_index(idof).first;
501  const unsigned int ishape = fe_metric.system_to_component_index(idof).second;
502  const unsigned int igrid_node = index_renumbering[ishape];
503  mapping_support_points[istate][igrid_node] = val;
504  }
505  // Construct the metric operators.
506  OPERATOR::metric_operators<double, dim, 2*dim> metric_oper(nstate, poly_degree, grid_degree, store_vol_flux_nodes, store_surf_flux_nodes);
507  // Build the metric terms to compute the gradient and volume node positions.
508  // This functions will compute the determinant of the metric Jacobian and metric cofactor matrix.
509  // If flags store_vol_flux_nodes and store_surf_flux_nodes set as true it will also compute the physical quadrature positions.
510  metric_oper.build_volume_metric_operators(
511  n_quad_pts, n_grid_nodes,
512  mapping_support_points,
513  mapping_basis,
514  dg.all_parameters->use_invariant_curl_form);
515 
516  // Fetch the modal soln coefficients
517  // We immediately separate them by state as to be able to use sum-factorization
518  // in the interpolation operator. If we left it by n_dofs_cell, then the matrix-vector
519  // mult would sum the states at the quadrature point.
520  // That is why the basis functions are based off the 1state oneD fe_collection.
521  std::array<std::vector<double>,nstate> soln_coeff;
522  for (unsigned int idof = 0; idof < n_dofs; ++idof) {
523  const unsigned int istate = dg.fe_collection[poly_degree].system_to_component_index(idof).first;
524  const unsigned int ishape = dg.fe_collection[poly_degree].system_to_component_index(idof).second;
525  if(ishape == 0){
526  soln_coeff[istate].resize(n_shape_fns);
527  }
528 
529  soln_coeff[istate][ishape] = dg.solution(dofs_indices[idof]);
530  }
531  // Interpolate each state to the quadrature points using sum-factorization
532  // with the basis functions in each reference direction.
533  std::array<std::vector<double>,nstate> soln_at_q_vect;
534  std::array<dealii::Tensor<1,dim,std::vector<double>>,nstate> soln_grad_at_q_vect;
535  for(int istate=0; istate<nstate; istate++){
536  soln_at_q_vect[istate].resize(n_quad_pts);
537  // Interpolate soln coeff to volume cubature nodes.
538  soln_basis.matrix_vector_mult_1D(soln_coeff[istate], soln_at_q_vect[istate],
539  soln_basis.oneD_vol_operator);
540  // We need to first compute the reference gradient of the solution, then transform that to a physical gradient.
541  dealii::Tensor<1,dim,std::vector<double>> ref_gradient_basis_fns_times_soln;
542  for(int idim=0; idim<dim; idim++){
543  ref_gradient_basis_fns_times_soln[idim].resize(n_quad_pts);
544  soln_grad_at_q_vect[istate][idim].resize(n_quad_pts);
545  }
546  // Apply gradient of reference basis functions on the solution at volume cubature nodes.
547  soln_basis.gradient_matrix_vector_mult_1D(soln_coeff[istate], ref_gradient_basis_fns_times_soln,
548  soln_basis.oneD_vol_operator,
549  soln_basis.oneD_grad_operator);
550  // Transform the reference gradient into a physical gradient operator.
551  for(int idim=0; idim<dim; idim++){
552  for(unsigned int iquad=0; iquad<n_quad_pts; iquad++){
553  for(int jdim=0; jdim<dim; jdim++){
554  //transform into the physical gradient
555  soln_grad_at_q_vect[istate][idim][iquad] += metric_oper.metric_cofactor_vol[idim][jdim][iquad]
556  * ref_gradient_basis_fns_times_soln[jdim][iquad]
557  / metric_oper.det_Jac_vol[iquad];
558  }
559  }
560  }
561  }
562 
563  std::array<std::vector<double>,3> vorticity_at_q_vect;// putting nstate as 3 for the 3 vorticity components
564  // Resize for n_quad_pts
565  for(int istate=0; istate<3; istate++){
566  vorticity_at_q_vect[istate].resize(n_quad_pts);
567  }
568  // Store vorticity at quadrature points
569  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
570  // Extract solution and gradient in a way that the physics can use them.
571  std::array<double,nstate> soln_at_q;
572  std::array<dealii::Tensor<1,dim,double>,nstate> soln_grad_at_q;
573  for(int istate=0; istate<nstate; istate++){
574  soln_at_q[istate] = soln_at_q_vect[istate][iquad];
575  for(int idim=0; idim<dim; idim++){
576  soln_grad_at_q[istate][idim] = soln_grad_at_q_vect[istate][idim][iquad];
577  }
578  }
579  dealii::Tensor<1,3,double> vorticity_at_q = this->navier_stokes_physics->compute_vorticity(soln_at_q,soln_grad_at_q);
580  for(int istate=0; istate<3; istate++){
581  vorticity_at_q_vect[istate][iquad] = vorticity_at_q[istate];
582  }
583  }
584 
585  // Now compute and store the gradient of vorticity:
586  // Interpolate each state to the quadrature points using sum-factorization
587  // with the basis functions in each reference direction.
588  std::array<dealii::Tensor<1,dim,std::vector<double>>,3> vorticity_grad_at_q_vect;
589  for(int istate=0; istate<3; istate++){
590  std::vector<double> vorticity_coeff(n_shape_fns);
591  soln_basis_projection_oper.matrix_vector_mult_1D(vorticity_at_q_vect[istate], vorticity_coeff,
592  soln_basis_projection_oper.oneD_vol_operator);
593  // We need to first compute the reference gradient of the solution, then transform that to a physical gradient.
594  dealii::Tensor<1,dim,std::vector<double>> ref_gradient_basis_fns_times_soln;
595  for(int idim=0; idim<dim; idim++){
596  ref_gradient_basis_fns_times_soln[idim].resize(n_quad_pts);
597  vorticity_grad_at_q_vect[istate][idim].resize(n_quad_pts);
598  }
599  // Apply gradient of reference basis functions on the solution at volume cubature nodes.
600  soln_basis.gradient_matrix_vector_mult_1D(vorticity_coeff, ref_gradient_basis_fns_times_soln,
601  soln_basis.oneD_vol_operator,
602  soln_basis.oneD_grad_operator);
603  // Transform the reference gradient into a physical gradient operator.
604  for(int idim=0; idim<dim; idim++){
605  for(unsigned int iquad=0; iquad<n_quad_pts; iquad++){
606  for(int jdim=0; jdim<dim; jdim++){
607  //transform into the physical gradient
608  vorticity_grad_at_q_vect[istate][idim][iquad] += metric_oper.metric_cofactor_vol[idim][jdim][iquad]
609  * ref_gradient_basis_fns_times_soln[jdim][iquad]
610  / metric_oper.det_Jac_vol[iquad];
611  }
612  }
613  }
614  }
615 
616 
617  // Loop over quadrature nodes, compute quantities to be integrated, and integrate them.
618  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
619 
620  std::array<double,nstate> soln_at_q;
621  std::array<dealii::Tensor<1,dim,double>,nstate> soln_grad_at_q;
622  dealii::Tensor<1,3,double> vorticity_at_q;
623  std::array<dealii::Tensor<1,dim,double>,3> vorticity_grad_at_q;
624  // Extract solution and gradient in a way that the physics can use them.
625  for(int istate=0; istate<nstate; istate++){
626  soln_at_q[istate] = soln_at_q_vect[istate][iquad];
627  if(istate<3) vorticity_at_q[istate] = vorticity_at_q_vect[istate][iquad];
628  for(int idim=0; idim<dim; idim++){
629  soln_grad_at_q[istate][idim] = soln_grad_at_q_vect[istate][idim][iquad];
630  if(istate<3) vorticity_grad_at_q[istate][idim] = vorticity_grad_at_q_vect[istate][idim][iquad];
631  }
632  }
633  dealii::Point<dim> qpoint;
634  if(this->do_compute_angular_momentum){
635  for(int idim=0; idim<dim; idim++){
636  qpoint[idim] = metric_oper.flux_nodes_vol[idim][iquad];
637  }
638  }
639 
640  std::array<double,NUMBER_OF_INTEGRATED_QUANTITIES> integrand_values;
641  std::fill(integrand_values.begin(), integrand_values.end(), 0.0);
642  integrand_values[IntegratedQuantitiesEnum::kinetic_energy] = this->navier_stokes_physics->compute_kinetic_energy_from_conservative_solution(soln_at_q);
643  integrand_values[IntegratedQuantitiesEnum::enstrophy] = this->navier_stokes_physics->compute_enstrophy(soln_at_q,soln_grad_at_q);
644  integrand_values[IntegratedQuantitiesEnum::pressure_dilatation] = this->navier_stokes_physics->compute_pressure_dilatation(soln_at_q,soln_grad_at_q);
645  integrand_values[IntegratedQuantitiesEnum::viscosity_times_deviatoric_strain_rate_tensor_magnitude_sqr] = this->navier_stokes_physics->compute_viscosity_times_deviatoric_strain_rate_tensor_magnitude_sqr(soln_at_q,soln_grad_at_q);
646  integrand_values[IntegratedQuantitiesEnum::viscosity_times_strain_rate_tensor_magnitude_sqr] = this->navier_stokes_physics->compute_viscosity_times_strain_rate_tensor_magnitude_sqr(soln_at_q,soln_grad_at_q);
647  integrand_values[IntegratedQuantitiesEnum::incompressible_kinetic_energy] = this->navier_stokes_physics->compute_incompressible_kinetic_energy_from_conservative_solution(soln_at_q);
648  integrand_values[IntegratedQuantitiesEnum::incompressible_enstrophy] = this->navier_stokes_physics->compute_incompressible_enstrophy(soln_at_q,soln_grad_at_q);
649  integrand_values[IntegratedQuantitiesEnum::incompressible_palinstrophy] = this->navier_stokes_physics->compute_incompressible_palinstrophy(soln_at_q,vorticity_grad_at_q);
650  if(this->do_compute_angular_momentum) integrand_values[IntegratedQuantitiesEnum::angular_momentum] = this->compute_angular_momentum(qpoint,vorticity_at_q);
651  else integrand_values[IntegratedQuantitiesEnum::angular_momentum] = 0.0;
652  for(int i_quantity=0; i_quantity<NUMBER_OF_INTEGRATED_QUANTITIES; ++i_quantity) {
653  integral_values[i_quantity] += integrand_values[i_quantity] * quad_weights[iquad] * metric_oper.det_Jac_vol[iquad];
654  }
655 
656  // Update the maximum local wave speed (i.e. convective eigenvalue) if using an adaptive time step
657  if(this->all_param.flow_solver_param.adaptive_time_step == true || this->all_param.flow_solver_param.error_adaptive_time_step == true) {
658  const double local_wave_speed = this->navier_stokes_physics->max_convective_eigenvalue(soln_at_q);
659  if(local_wave_speed > this->maximum_local_wave_speed) this->maximum_local_wave_speed = local_wave_speed;
660  }
661  }
662  }
664  this->maximum_local_wave_speed = dealii::Utilities::MPI::max(this->maximum_local_wave_speed, this->mpi_communicator);
665  }
666  // update integrated quantities
667  for(int i_quantity=0; i_quantity<NUMBER_OF_INTEGRATED_QUANTITIES; ++i_quantity) {
668  this->integrated_quantities[i_quantity] = dealii::Utilities::MPI::sum(integral_values[i_quantity], this->mpi_communicator);
669  this->integrated_quantities[i_quantity] /= this->domain_size; // divide by total domain volume
670  }
671 }
672 
673 template<int dim, int nspecies, int nstate>
675 {
676  const double integrated_kinetic_energy = this->integrated_quantities[IntegratedQuantitiesEnum::kinetic_energy];
677  // // Abort if energy is nan
678  // if(std::isnan(integrated_kinetic_energy)) {
679  // this->pcout << " ERROR: Kinetic energy at time " << current_time << " is nan." << std::endl;
680  // this->pcout << " Consider decreasing the time step / CFL number." << std::endl;
681  // std::abort();
682  // }
683  return integrated_kinetic_energy;
684 }
685 
686 template<int dim, int nspecies, int nstate>
688 {
689  return this->integrated_quantities[IntegratedQuantitiesEnum::enstrophy];
690 }
691 
692 template<int dim, int nspecies, int nstate>
694 {
695  return this->integrated_quantities[IntegratedQuantitiesEnum::incompressible_kinetic_energy];
696 }
697 
698 template<int dim, int nspecies, int nstate>
700 {
701  return this->integrated_quantities[IntegratedQuantitiesEnum::incompressible_enstrophy];
702 }
703 
704 template<int dim, int nspecies, int nstate>
706 {
707  return this->integrated_quantities[IntegratedQuantitiesEnum::incompressible_palinstrophy];
708 }
709 
710 template<int dim, int nspecies, int nstate>
712 {
713  return this->integrated_quantities[IntegratedQuantitiesEnum::angular_momentum];
714 }
715 
716 template<int dim, int nspecies, int nstate>
718 {
719  const double integrated_enstrophy = this->integrated_quantities[IntegratedQuantitiesEnum::enstrophy];
720  double vorticity_based_dissipation_rate = 0.0;
721  if (is_viscous_flow){
722  vorticity_based_dissipation_rate = this->navier_stokes_physics->compute_vorticity_based_dissipation_rate_from_integrated_enstrophy(integrated_enstrophy);
723  }
724  return vorticity_based_dissipation_rate;
725 }
726 
727 template<int dim, int nspecies, int nstate>
729 {
730  const double integrated_pressure_dilatation = this->integrated_quantities[IntegratedQuantitiesEnum::pressure_dilatation];
731  return (-1.0*integrated_pressure_dilatation); // See reference (listed in header file), equation (57b)
732 }
733 
734 template<int dim, int nspecies, int nstate>
736 {
737  const double integrated_viscosity_times_deviatoric_strain_rate_tensor_magnitude_sqr = this->integrated_quantities[IntegratedQuantitiesEnum::viscosity_times_deviatoric_strain_rate_tensor_magnitude_sqr];
738  double deviatoric_strain_rate_tensor_based_dissipation_rate = 0.0;
739  if (is_viscous_flow){
740  deviatoric_strain_rate_tensor_based_dissipation_rate =
741  this->navier_stokes_physics->compute_deviatoric_strain_rate_tensor_based_dissipation_rate_from_integrated_viscosity_times_deviatoric_strain_rate_tensor_magnitude_sqr(integrated_viscosity_times_deviatoric_strain_rate_tensor_magnitude_sqr);
742  }
743  return deviatoric_strain_rate_tensor_based_dissipation_rate;
744 }
745 
746 template<int dim, int nspecies, int nstate>
748 {
749  const double integrated_viscosity_times_strain_rate_tensor_magnitude_sqr = this->integrated_quantities[IntegratedQuantitiesEnum::viscosity_times_strain_rate_tensor_magnitude_sqr];
750  double strain_rate_tensor_based_dissipation_rate = 0.0;
751  if (is_viscous_flow){
752  strain_rate_tensor_based_dissipation_rate =
753  this->navier_stokes_physics->compute_strain_rate_tensor_based_dissipation_rate_from_integrated_viscosity_times_strain_rate_tensor_magnitude_sqr(integrated_viscosity_times_strain_rate_tensor_magnitude_sqr);
754  }
755  return strain_rate_tensor_based_dissipation_rate;
756 }
757 
758 template<int dim, int nspecies, int nstate>
760 {
762 }
763 
764 template<int dim, int nspecies, int nstate>
766  const std::shared_ptr <DGBase<dim, nspecies, double>> dg
767  ) const
768 {
769  const double poly_degree = this->all_param.flow_solver_param.poly_degree;
770 
771  const unsigned int n_dofs_cell = dg->fe_collection[poly_degree].dofs_per_cell;
772  const unsigned int n_quad_pts = dg->volume_quadrature_collection[poly_degree].size();
773  const unsigned int n_shape_fns = n_dofs_cell / nstate;
774 
775  OPERATOR::vol_projection_operator<dim,2*dim> vol_projection(1, poly_degree, dg->max_grid_degree);
776  vol_projection.build_1D_volume_operator(dg->oneD_fe_collection_1state[poly_degree], dg->oneD_quadrature_collection[poly_degree]);
777 
778  // Construct the basis functions and mapping shape functions.
779  OPERATOR::basis_functions<dim,2*dim> soln_basis(1, poly_degree, dg->max_grid_degree);
780  soln_basis.build_1D_volume_operator(dg->oneD_fe_collection_1state[poly_degree], dg->oneD_quadrature_collection[poly_degree]);
781 
782  OPERATOR::mapping_shape_functions<dim,2*dim> mapping_basis(1, poly_degree, dg->max_grid_degree);
783  mapping_basis.build_1D_shape_functions_at_grid_nodes(dg->high_order_grid->oneD_fe_system, dg->high_order_grid->oneD_grid_nodes);
784  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);
785 
786  std::vector<dealii::types::global_dof_index> dofs_indices (n_dofs_cell);
787 
788  double integrand_numerical_entropy_function=0;
789  double integral_numerical_entropy_function=0;
790  const std::vector<double> &quad_weights = dg->volume_quadrature_collection[poly_degree].get_weights();
791 
792  auto metric_cell = dg->high_order_grid->dof_handler_grid.begin_active();
793  // Changed for loop to update metric_cell.
794  for (auto cell = dg->dof_handler.begin_active(); cell!= dg->dof_handler.end(); ++cell, ++metric_cell) {
795  if (!cell->is_locally_owned()) continue;
796  cell->get_dof_indices (dofs_indices);
797 
798  // We first need to extract the mapping support points (grid nodes) from high_order_grid.
799  const dealii::FESystem<dim> &fe_metric = dg->high_order_grid->fe_system;
800  const unsigned int n_metric_dofs = fe_metric.dofs_per_cell;
801  const unsigned int n_grid_nodes = n_metric_dofs / dim;
802  std::vector<dealii::types::global_dof_index> metric_dof_indices(n_metric_dofs);
803  metric_cell->get_dof_indices (metric_dof_indices);
804  std::array<std::vector<double>,dim> mapping_support_points;
805  for(int idim=0; idim<dim; idim++){
806  mapping_support_points[idim].resize(n_grid_nodes);
807  }
808  // Get the mapping support points (physical grid nodes) from high_order_grid.
809  // Store it in such a way we can use sum-factorization on it with the mapping basis functions.
810  const std::vector<unsigned int > &index_renumbering = dealii::FETools::hierarchic_to_lexicographic_numbering<dim>(dg->max_grid_degree);
811  for (unsigned int idof = 0; idof< n_metric_dofs; ++idof) {
812  const double val = (dg->high_order_grid->volume_nodes[metric_dof_indices[idof]]);
813  const unsigned int istate = fe_metric.system_to_component_index(idof).first;
814  const unsigned int ishape = fe_metric.system_to_component_index(idof).second;
815  const unsigned int igrid_node = index_renumbering[ishape];
816  mapping_support_points[istate][igrid_node] = val;
817  }
818  // Construct the metric operators.
819  OPERATOR::metric_operators<double, dim, 2*dim> metric_oper(nstate, poly_degree, dg->max_grid_degree, false, false);
820  // Build the metric terms to compute the gradient and volume node positions.
821  // This functions will compute the determinant of the metric Jacobian and metric cofactor matrix.
822  // If flags store_vol_flux_nodes and store_surf_flux_nodes set as true it will also compute the physical quadrature positions.
823  metric_oper.build_volume_metric_operators(
824  n_quad_pts, n_grid_nodes,
825  mapping_support_points,
826  mapping_basis,
827  dg->all_parameters->use_invariant_curl_form);
828 
829  // Fetch the modal soln coefficients
830  // We immediately separate them by state as to be able to use sum-factorization
831  // in the interpolation operator. If we left it by n_dofs_cell, then the matrix-vector
832  // mult would sum the states at the quadrature point.
833  // That is why the basis functions are based off the 1state oneD fe_collection.
834  std::array<std::vector<double>,nstate> soln_coeff;
835  for (unsigned int idof = 0; idof < n_dofs_cell; ++idof) {
836  const unsigned int istate = dg->fe_collection[poly_degree].system_to_component_index(idof).first;
837  const unsigned int ishape = dg->fe_collection[poly_degree].system_to_component_index(idof).second;
838  if(ishape == 0){
839  soln_coeff[istate].resize(n_shape_fns);
840  }
841  soln_coeff[istate][ishape] = dg->solution(dofs_indices[idof]);
842  }
843  // Interpolate each state to the quadrature points using sum-factorization
844  // with the basis functions in each reference direction.
845  std::array<std::vector<double>,nstate> soln_at_q;
846  for(int istate=0; istate<nstate; istate++){
847  soln_at_q[istate].resize(n_quad_pts);
848  // Interpolate soln coeff to volume cubature nodes.
849  soln_basis.matrix_vector_mult_1D(soln_coeff[istate], soln_at_q[istate],
850  soln_basis.oneD_vol_operator);
851  }
852 
853  // Loop over quadrature nodes, compute quantities to be integrated, and integrate them.
854  for (unsigned int iquad=0; iquad<n_quad_pts; ++iquad) {
855 
856  std::array<double,nstate> soln_state;
857  // Extract solution in a way that the physics ca n use them.
858  for(int istate=0; istate<nstate; istate++){
859  soln_state[istate] = soln_at_q[istate][iquad];
860  }
861  integrand_numerical_entropy_function = this->navier_stokes_physics->compute_numerical_entropy_function(soln_state);
862  integral_numerical_entropy_function += integrand_numerical_entropy_function * quad_weights[iquad] * metric_oper.det_Jac_vol[iquad];
863  }
864  }
865  // update integrated quantities and return
866  const double mpi_integrated_numerical_entropy = dealii::Utilities::MPI::sum(integral_numerical_entropy_function, this->mpi_communicator);
867 
868  return mpi_integrated_numerical_entropy;
869 }
870 
871 
872 template <int dim, int nspecies, int nstate>
874  const double current_time,
875  const std::shared_ptr <DGBase<dim, nspecies, double>> dg)
876 {
877  // Output velocity field for spectra obtaining kinetic energy spectra
879  const double time_step = this->get_time_step();
880  const double next_time = current_time + time_step;
882  // Check if current time is an output time
883  bool is_output_time = false; // default initialization
885  is_output_time = current_time == desired_time;
886  } else {
887  is_output_time = ((current_time<=desired_time) && (next_time>desired_time));
888  }
889  if(is_output_time) {
890  // Output velocity field for current index
892 
893  // Update index s.t. it never goes out of bounds
897  }
898  }
899  }
900 }
901 
902 template <int dim, int nspecies, int nstate>
904  const double FR_entropy_contribution_RRK_solver,
905  const unsigned int current_iteration,
906  const std::shared_ptr <DGBase<dim, nspecies, double>> dg)
907 {
908 
909  const double current_numerical_entropy = this->compute_current_integrated_numerical_entropy(dg);
910 
911  if (current_iteration==0) {
912  this->previous_numerical_entropy = current_numerical_entropy;
913  this->initial_numerical_entropy_abs = abs(current_numerical_entropy);
914  }
915 
916  const double current_numerical_entropy_change_FRcorrected = (current_numerical_entropy - this->previous_numerical_entropy + FR_entropy_contribution_RRK_solver)/this->initial_numerical_entropy_abs;
917  this->previous_numerical_entropy = current_numerical_entropy;
918  this->cumulative_numerical_entropy_change_FRcorrected+=current_numerical_entropy_change_FRcorrected;
919 
920 }
921 
922 template <int dim, int nspecies, int nstate>
924  const std::shared_ptr <ODE::ODESolverBase<dim, nspecies, double>> ode_solver,
925  const std::shared_ptr <DGBase<dim, nspecies, double>> dg,
926  const std::shared_ptr <dealii::TableHandler> unsteady_data_table,
927  const bool do_write_unsteady_data_table_file)
928 {
929  // unpack current iteration and current time from ode solver
930  const unsigned int current_iteration = ode_solver->current_iteration;
931  const double current_time = ode_solver->current_time;
932  // Compute and update integrated quantities
934  // Get computed quantities
935  const double integrated_kinetic_energy = this->get_integrated_kinetic_energy();
936  const double integrated_enstrophy = this->get_integrated_enstrophy();
937  const double vorticity_based_dissipation_rate = this->get_vorticity_based_dissipation_rate();
938  const double pressure_dilatation_based_dissipation_rate = this->get_pressure_dilatation_based_dissipation_rate();
939  const double deviatoric_strain_rate_tensor_based_dissipation_rate = this->get_deviatoric_strain_rate_tensor_based_dissipation_rate();
940  const double strain_rate_tensor_based_dissipation_rate = this->get_strain_rate_tensor_based_dissipation_rate();
941 
943  const bool is_rrk = (this->all_param.ode_solver_param.use_relaxation_runge_kutta);
944  const double relaxation_parameter = ode_solver->relaxation_parameter_RRK_solver;
945 
947  this->update_numerical_entropy(ode_solver->FR_entropy_contribution_RRK_solver,current_iteration, dg);
948  }
949 
950  const double integrated_incompressible_kinetic_energy = this->get_integrated_incompressible_kinetic_energy();
951  const double integrated_incompressible_enstrophy = this->get_integrated_incompressible_enstrophy();
952  const double integrated_incompressible_palinstrophy = this->get_integrated_incompressible_palinstrophy();
953  double integrated_angular_momentum = 0.0;
954  if(this->do_compute_angular_momentum) integrated_angular_momentum = this->get_integrated_angular_momentum();
955 
956  if(this->mpi_rank==0) {
957  // Add values to data table
958  this->add_value_to_data_table(current_time,"time",unsteady_data_table);
959  if(do_calculate_numerical_entropy) this->add_value_to_data_table(this->cumulative_numerical_entropy_change_FRcorrected,"numerical_entropy_scaled_cumulative",unsteady_data_table);
960  if(is_rrk) this->add_value_to_data_table(relaxation_parameter, "relaxation_parameter",unsteady_data_table);
961  this->add_value_to_data_table(integrated_kinetic_energy,"kinetic_energy",unsteady_data_table);
962  this->add_value_to_data_table(integrated_enstrophy,"enstrophy",unsteady_data_table);
963  if(is_viscous_flow) this->add_value_to_data_table(vorticity_based_dissipation_rate,"eps_vorticity",unsteady_data_table);
964  this->add_value_to_data_table(pressure_dilatation_based_dissipation_rate,"eps_pressure",unsteady_data_table);
965  if(is_viscous_flow) this->add_value_to_data_table(strain_rate_tensor_based_dissipation_rate,"eps_strain",unsteady_data_table);
966  if(is_viscous_flow) this->add_value_to_data_table(deviatoric_strain_rate_tensor_based_dissipation_rate,"eps_dev_strain",unsteady_data_table);
967  this->add_value_to_data_table(integrated_incompressible_kinetic_energy,"incompressible_kinetic_energy",unsteady_data_table);
968  this->add_value_to_data_table(integrated_incompressible_enstrophy,"incompressible_enstrophy",unsteady_data_table);
969  this->add_value_to_data_table(integrated_incompressible_palinstrophy,"incompressible_palinstrophy",unsteady_data_table);
970  if(this->do_compute_angular_momentum) this->add_value_to_data_table(integrated_angular_momentum,"angular_momentum",unsteady_data_table);
971  // Write to file
972  if(do_write_unsteady_data_table_file) {
973  std::ofstream unsteady_data_table_file(this->unsteady_data_table_filename_with_extension);
974  unsteady_data_table->write_text(unsteady_data_table_file);
975  }
976  }
977  // Print to console
978  this->pcout << " Iter: " << current_iteration
979  << " Time: " << current_time
980  << " Energy: " << integrated_kinetic_energy
981  << " Enstrophy: " << integrated_enstrophy;
982  if(is_viscous_flow) {
983  this->pcout << " eps_vorticity: " << vorticity_based_dissipation_rate
984  << " eps_p+eps_strain: " << (pressure_dilatation_based_dissipation_rate + strain_rate_tensor_based_dissipation_rate);
985  }
987  this->pcout << " Num. Entropy cumulative, FR corrected: " << std::setprecision(16) << this->cumulative_numerical_entropy_change_FRcorrected;
988  }
989  if(is_rrk){
990  this->pcout << " Relaxation Parameter: " << std::setprecision(16) << relaxation_parameter;
991  }
992  this->pcout << std::endl;
993 
994  // Abort if energy is nan
995  if(std::isnan(integrated_kinetic_energy)) {
996  this->pcout << " ERROR: Kinetic energy at time " << current_time << " is nan." << std::endl;
997  this->pcout << " Consider decreasing the time step / CFL number. Aborting..." << std::endl;
998  if(this->mpi_rank==0) std::abort();
999  }
1000 
1001  // check for case dependant non-physical behavior
1004  this->pcout << " ERROR: Non-physical behaviour encountered in PeriodicTurbulence." << std::endl;
1005  this->pcout << " --> Integrated kinetic energy has increased from the last time step in a closed system without any external sources." << std::endl;
1006  this->pcout << " ==> Consider decreasing the time step / CFL number. Aborting..." << std::endl;
1007  if(this->mpi_rank==0) std::abort();
1008  } else {
1010  }
1011  }
1012 
1013  // Output velocity field if current time is output file
1015 }
1016 
1017 #if PHILIP_DIM!=1 && PHILIP_SPECIES==1
1019 #endif
1020 
1021 } // FlowSolver namespace
1022 } // PHiLiP namespace
1023 
void update_maximum_local_wave_speed(DGBase< dim, nspecies, double > &dg) override
Updates the maximum local wave speed.
const double domain_size
Domain size (length in 1D, area in 2D, and volume in 3D)
FlowCaseType
Selects the flow case to be simulated.
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.
double initial_time
Initial time at which we initialize the ODE solver with.
bool check_nonphysical_flow_case_behavior
For TGV, flag to check if non-physical case dependant behaviour is encounted.
FlowCaseType flow_case_type
Selected FlowCaseType from the input file.
const Parameters::AllParameters all_param
All parameters.
const dealii::hp::FECollection< 1 > oneD_fe_collection_1state
1D Finite Element Collection for p-finite-element to represent the solution for a single state...
Definition: dg_base.hpp:1150
double courant_friedrichs_lewy_number
Courant-Friedrichs-Lewy (CFL) number for constant time step.
bool adaptive_time_step
Flag for computing the time step on the fly.
const bool output_vorticity_magnitude_field_in_addition_to_velocity
Flag for outputting vorticity magnitude field in addition to velocity field at fixed times...
void compute_and_update_integrated_quantities(DGBase< dim, nspecies, double > &dg)
FlowSolverParam flow_solver_param
Contains the parameters for simulation cases (flow solver test)
const bool output_viscosity_field_in_addition_to_velocity
Flag for outputting viscosity field in addition to velocity field at fixed times. ...
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
double constant_time_step
Constant time step.
double mach_inf
Mach number at infinity.
bool restart_computation_from_file
Restart computation from restart file.
const bool do_compute_angular_momentum
Flag to compute angular momentum.
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
virtual void display_additional_flow_case_specific_parameters() const override
Display additional more specific flow case parameters.
PartialDifferentialEquation
Possible Partial Differential Equations to solve.
const double domain_left
Domain left-boundary value for generating the grid.
const unsigned int output_velocity_number_of_subvisions
Number of subdivisions to apply when writting the velocity field at equidistant nodes.
Files for the baseline physics.
Definition: ADTypes.hpp:10
double get_adaptive_time_step(std::shared_ptr< DGBase< dim, nspecies, double >> dg) const override
Function to compute the adaptive time step.
double cumulative_numerical_entropy_change_FRcorrected
Cumulative change in numerical entropy.
void build_1D_shape_functions_at_flux_nodes(const dealii::FESystem< 1, 1 > &finite_element, const dealii::Quadrature< 1 > &quadrature, const dealii::Quadrature< 0 > &face_quadrature)
Constructs the volume, gradient, surface, and surface gradient operator.
Definition: operators.cpp:2313
std::array< double, NUMBER_OF_INTEGRATED_QUANTITIES > integrated_quantities
Array for storing the integrated quantities; done for computational efficiency.
std::shared_ptr< Physics::NavierStokes< dim, nspecies, dim+2, double > > navier_stokes_physics
Pointer to Navier-Stokes physics object for computing things on the fly.
Base class ODE solver.
bool is_viscous_flow
Identifies if viscous flow; initialized as true.
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
std::string flow_field_quantity_filename_prefix
Flow field quantity filename prefix.
double reynolds_number_inf
Farfield Reynolds number.
EulerParam euler_param
Contains parameters for the Euler equations non-dimensionalization.
unsigned int poly_degree
Polynomial order (P) of the basis functions for DG.
Main parameter class that contains the various other sub-parameter classes.
bool do_calculate_numerical_entropy
For TGV, flag to calculate and write numerical entropy.
bool do_calculate_numerical_entropy
Identifies if numerical entropy should be calculated; initialized as false.
dealii::Table< 1, double > output_velocity_field_times
Times at which to output the velocity field.
std::string output_velocity_field_times_string
String of velocity field output times.
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
double initial_numerical_entropy_abs
Numerical entropy at initial time.
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
ODESolverParam ode_solver_param
Contains parameters for ODE solver.
const std::string unsteady_data_table_filename_with_extension
Filename (with extension) for the unsteady data table.
NavierStokesParam navier_stokes_param
Contains parameters for the Navier-Stokes equations non-dimensionalization.
const Parameters::AllParameters *const all_parameters
Pointer to all parameters.
Definition: dg_base.hpp:91
double maximum_local_wave_speed
Maximum local wave speed (i.e. convective eigenvalue)
Base metric operators class that stores functions used in both the volume and on surface.
Definition: operators.h:1131
double previous_numerical_entropy
Numerical entropy at previous timestep.
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
double get_integrated_incompressible_enstrophy() const
Gets the nondimensional integrated incompressible enstrophy given a DG object from dg->solution...
double compute_angular_momentum(const dealii::Point< dim > position, const dealii::Tensor< 1, 3, double > vorticity) const
Function to compute the angular momentum.
const bool output_solution_at_exact_fixed_times
Flag for outputting the solution at exact fixed times by decreasing the time step on the fly...
PeriodicTurbulence(const Parameters::AllParameters *const parameters_input)
Constructor.
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
dealii::FullMatrix< double > oneD_grad_operator
Stores the one dimensional gradient operator.
Definition: operators.h:388
double get_numerical_entropy(const std::shared_ptr< DGBase< dim, nspecies, double >>) const
Retrieves cumulative_numerical_entropy_change_FRcorrected.
double get_integrated_incompressible_kinetic_energy() const
Gets the nondimensional integrated incompressible kinetic energy given a DG object from dg->solution...
void update_numerical_entropy(const double FR_entropy_contribution_RRK_solver, const unsigned int current_iteration, const std::shared_ptr< DGBase< dim, nspecies, double >> dg)
Update numerical entropy variables.
double integrated_kinetic_energy_at_previous_time_step
Integrated kinetic energy over the domain at previous time step; used for ensuring a physically consi...
dealii::LinearAlgebra::distributed::Vector< double > solution
Current modal coefficients of the solution.
Definition: dg_base.hpp:409
const std::string output_flow_field_files_directory_name
Directory for writting flow field files.
const int number_of_cells_per_direction
Number of cells per direction for the grid.
double get_integrated_angular_momentum() const
Gets the nondimensional integrated angular momentum given a DG object from dg->solution.
const unsigned int number_of_times_to_output_velocity_field
Number of times to output the velocity field.
void output_velocity_field(std::shared_ptr< DGBase< dim, nspecies, double >> dg, const unsigned int output_file_index, const double current_time) const
Output the velocity field to file.
const bool output_density_field_in_addition_to_velocity
Flag for outputting density field in addition to velocity field at fixed times.
void build_1D_shape_functions_at_grid_nodes(const dealii::FESystem< 1, 1 > &finite_element, const dealii::Quadrature< 1 > &quadrature)
Constructs the volume operator and gradient operator.
Definition: operators.cpp:2304
const unsigned int max_degree
Maximum degree used for p-refi1nement.
Definition: dg_base.hpp:104
static std::shared_ptr< PhysicsBase< dim, nspecies, nstate, real > > create_Physics(const Parameters::AllParameters *const parameters_input, std::shared_ptr< ModelBase< dim, nspecies, nstate, real > > model_input=nullptr)
Factory to return the correct physics given input file.
void output_velocity_field_if_current_time_is_output_time(const double current_time, const std::shared_ptr< DGBase< dim, nspecies, double >> dg)
Outputs the velocity field if the current time is an output time for the velocity field...
const MPI_Comm mpi_communicator
MPI communicator.
virtual void compute_unsteady_data_and_write_to_table(const std::shared_ptr< ODE::ODESolverBase< dim, nspecies, double >> ode_solver, const std::shared_ptr< DGBase< dim, nspecies, double >> dg, const std::shared_ptr< dealii::TableHandler > unsteady_data_table, const bool do_write_unsteady_data_table_file) override
Compute the desired unsteady data and write it to a table.
dealii::ConditionalOStream pcout
ConditionalOStream.
bool is_taylor_green_vortex
Identifies if taylor green vortex case; initialized as false.
void add_value_to_data_table(const double value, const std::string value_string, const std::shared_ptr< dealii::TableHandler > data_table) const
Add a value to a given data table with scientific format.
std::shared_ptr< dealii::TableHandler > exact_output_times_of_velocity_field_files_table
Data table storing the exact output times for the velocity field files.
double get_integrated_incompressible_palinstrophy() const
Gets the nondimensional integrated incompressible palinstrophy given a DG object from dg->solution...
const dealii::hp::FECollection< dim > fe_collection
Finite Element Collection for p-finite-element to represent the solution.
Definition: dg_base.hpp:1120
bool use_relaxation_runge_kutta
Use relaxation runge-kutta.
double get_time_step() const
Getter for time step.
DGBase is independent of the number of state variables.
Definition: dg_base.hpp:82
double get_constant_time_step(std::shared_ptr< DGBase< dim, nspecies, double >> dg) const override
Function to compute the constant time step.
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
bool is_decaying_homogeneous_isotropic_turbulence
Identified if DHIT case; initialized as false.
virtual unsigned int get_number_of_degrees_of_freedom_per_state_from_poly_degree(const unsigned int poly_degree_input) const
Get the number of degrees of freedom per state from a given poly degree.
dealii::Tensor< 1, dim, std::vector< real > > flux_nodes_vol
Stores the physical volume flux nodes.
Definition: operators.h:1225
double get_deviatoric_strain_rate_tensor_based_dissipation_rate() const
Navier-Stokes equations. Derived from Euler for the convective terms, which is derived from PhysicsBa...
Definition: navier_stokes.h:12
unsigned int index_of_current_desired_time_to_output_velocity_field
Index of current desired time to output velocity field.
void build_1D_gradient_operator(const dealii::FESystem< 1, 1 > &finite_element, const dealii::Quadrature< 1 > &quadrature)
Assembles the one dimensional operator.
Definition: operators.cpp:1237
Projection operator corresponding to basis functions onto M-norm (L2).
Definition: operators.h:723
const double domain_right
Domain right-boundary value for generating the grid.
double compute_current_integrated_numerical_entropy(const std::shared_ptr< DGBase< dim, nspecies, double >> dg) const
Calculate numerical entropy by matrix-vector product.
virtual void display_grid_parameters() const
Display grid parameters.
const bool output_velocity_field_at_fixed_times
Flag for outputting velocity field at fixed times.